summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHåvard Pettersen <havardpe@oath.com>2020-06-17 15:15:30 +0000
committerHåvard Pettersen <havardpe@oath.com>2020-06-18 10:21:48 +0000
commit9b475a9b958ccf56a8c793021d21cdde09d39e15 (patch)
treed6e04e4ad2d8ed8d284447673e911fd9a7df24a8
parentd3b332d3d9772b4cdff30ad68c87d5a525c119eb (diff)
observe the results of multiply-add across implementations
- add specific test showing diverging results when using FMA - re-write eval test to not fail due to FMA - re-write hitcollector test to not fail due to FMA
-rw-r--r--eval/CMakeLists.txt1
-rw-r--r--eval/src/tests/eval/multiply_add/CMakeLists.txt9
-rw-r--r--eval/src/tests/eval/multiply_add/multiply_add_test.cpp44
-rw-r--r--eval/src/vespa/eval/eval/test/eval_spec.cpp6
-rw-r--r--searchlib/src/tests/hitcollector/hitcollector_test.cpp12
5 files changed, 63 insertions, 9 deletions
diff --git a/eval/CMakeLists.txt b/eval/CMakeLists.txt
index 3a9aabc83ba..3e81521550a 100644
--- a/eval/CMakeLists.txt
+++ b/eval/CMakeLists.txt
@@ -19,6 +19,7 @@ vespa_define_module(
src/tests/eval/gbdt
src/tests/eval/inline_operation
src/tests/eval/interpreted_function
+ src/tests/eval/multiply_add
src/tests/eval/node_tools
src/tests/eval/node_types
src/tests/eval/param_usage
diff --git a/eval/src/tests/eval/multiply_add/CMakeLists.txt b/eval/src/tests/eval/multiply_add/CMakeLists.txt
new file mode 100644
index 00000000000..c50aa4f50a2
--- /dev/null
+++ b/eval/src/tests/eval/multiply_add/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(eval_multiply_add_test_app TEST
+ SOURCES
+ multiply_add_test.cpp
+ DEPENDS
+ vespaeval
+ gtest
+)
+vespa_add_test(NAME eval_multiply_add_test_app COMMAND eval_multiply_add_test_app)
diff --git a/eval/src/tests/eval/multiply_add/multiply_add_test.cpp b/eval/src/tests/eval/multiply_add/multiply_add_test.cpp
new file mode 100644
index 00000000000..35cab0a6030
--- /dev/null
+++ b/eval/src/tests/eval/multiply_add/multiply_add_test.cpp
@@ -0,0 +1,44 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/eval/eval/function.h>
+#include <vespa/eval/eval/llvm/compiled_function.h>
+#include <vespa/eval/eval/interpreted_function.h>
+#include <vespa/eval/tensor/default_tensor_engine.h>
+#include <vespa/vespalib/gtest/gtest.h>
+
+using namespace vespalib::eval;
+
+using Engine = vespalib::tensor::DefaultTensorEngine;
+
+double gcc_fun(double a, double b) {
+ return (a * 3) + b;
+}
+
+TEST(MultiplyAddTest, multiply_add_gives_same_result) {
+ auto fun = Function::parse("a*3+b");
+ CompiledFunction cfun(*fun, PassParams::ARRAY);
+ NodeTypes node_types = NodeTypes(*fun, {ValueType::double_type(), ValueType::double_type()});
+ InterpretedFunction ifun(Engine::ref(), *fun, node_types);
+ auto llvm_fun = cfun.get_function();
+ //-------------------------------------------------------------------------
+ double a = -1.0/3.0;
+ double b = 1.0;
+ std::vector<double> ab({a, b});
+ SimpleParams params(ab);
+ InterpretedFunction::Context ictx(ifun);
+ //-------------------------------------------------------------------------
+ const Value &result_value = ifun.eval(ictx, params);
+ double ifun_res = result_value.as_double();
+ double llvm_res = llvm_fun(&ab[0]);
+ double gcc_res = gcc_fun(a, b);
+ fprintf(stderr, "ifun_res: %a\n", ifun_res);
+ fprintf(stderr, "llvm_res: %a\n", llvm_res);
+ fprintf(stderr, "gcc_res: %a\n", gcc_res);
+ EXPECT_EQ(ifun_res, llvm_res);
+ EXPECT_DOUBLE_EQ(llvm_res + 1.0, gcc_res + 1.0);
+ if (llvm_res != gcc_res) {
+ fprintf(stderr, "WARNING: diverging results caused by fused multiply add\n");
+ }
+}
+
+GTEST_MAIN_RUN_ALL_TESTS()
diff --git a/eval/src/vespa/eval/eval/test/eval_spec.cpp b/eval/src/vespa/eval/eval/test/eval_spec.cpp
index 709234a1a2c..dbc20dcf606 100644
--- a/eval/src/vespa/eval/eval/test/eval_spec.cpp
+++ b/eval/src/vespa/eval/eval/test/eval_spec.cpp
@@ -162,11 +162,11 @@ EvalSpec::add_function_call_cases() {
void
EvalSpec::add_tensor_operation_cases() {
add_rule({"a", -1.0, 1.0}, "map(a,f(x)(sin(x)))", [](double x){ return std::sin(x); });
- add_rule({"a", -1.0, 1.0}, "map(a,f(x)(x+x*3))", [](double x){ return (x + (x * 3)); });
+ add_rule({"a", -1.0, 1.0}, "map(a,f(x)(x*x*3))", [](double x){ return ((x * x) * 3); });
add_rule({"a", -1.0, 1.0}, {"b", -1.0, 1.0}, "join(a,b,f(x,y)(x+y))", [](double x, double y){ return (x + y); });
- add_rule({"a", -1.0, 1.0}, {"b", -1.0, 1.0}, "join(a,b,f(x,y)(x+y*3))", [](double x, double y){ return (x + (y * 3)); });
+ add_rule({"a", -1.0, 1.0}, {"b", -1.0, 1.0}, "join(a,b,f(x,y)(x*y*3))", [](double x, double y){ return ((x * y) * 3); });
add_rule({"a", -1.0, 1.0}, {"b", -1.0, 1.0}, "merge(a,b,f(x,y)(x+y))", [](double x, double y){ return (x + y); });
- add_rule({"a", -1.0, 1.0}, {"b", -1.0, 1.0}, "merge(a,b,f(x,y)(x+y*3))", [](double x, double y){ return (x + (y * 3)); });
+ add_rule({"a", -1.0, 1.0}, {"b", -1.0, 1.0}, "merge(a,b,f(x,y)(x*y*3))", [](double x, double y){ return ((x * y) * 3); });
add_rule({"a", -1.0, 1.0}, "reduce(a,avg)", [](double a){ return a; });
add_rule({"a", -1.0, 1.0}, "reduce(a,count)", [](double){ return 1.0; });
add_rule({"a", -1.0, 1.0}, "reduce(a,prod)", [](double a){ return a; });
diff --git a/searchlib/src/tests/hitcollector/hitcollector_test.cpp b/searchlib/src/tests/hitcollector/hitcollector_test.cpp
index 31a24d2a8f1..2274314c7da 100644
--- a/searchlib/src/tests/hitcollector/hitcollector_test.cpp
+++ b/searchlib/src/tests/hitcollector/hitcollector_test.cpp
@@ -55,7 +55,7 @@ void checkResult(const ResultSet & rs, const std::vector<RankedHit> & exp)
for (uint32_t i = 0; i < exp.size(); ++i) {
EXPECT_EQUAL(rh[i]._docId, exp[i]._docId);
- EXPECT_EQUAL(rh[i]._rankValue, exp[i]._rankValue);
+ EXPECT_EQUAL(rh[i]._rankValue + 1.0, exp[i]._rankValue + 1.0);
}
} else {
ASSERT_TRUE(rs.getArray() == nullptr);
@@ -328,7 +328,7 @@ TEST("testScaling") {
finalScores[3] = 300;
finalScores[4] = 400;
- testScaling(initScores, std::move(finalScores), exp);
+ TEST_DO(testScaling(initScores, std::move(finalScores), exp));
}
{ // scale down and adjust up
exp[0]._rankValue = 200; // scaled
@@ -342,7 +342,7 @@ TEST("testScaling") {
finalScores[3] = 500;
finalScores[4] = 600;
- testScaling(initScores, std::move(finalScores), exp);
+ TEST_DO(testScaling(initScores, std::move(finalScores), exp));
}
{ // scale up and adjust down
@@ -357,7 +357,7 @@ TEST("testScaling") {
finalScores[3] = 3250;
finalScores[4] = 4500;
- testScaling(initScores, std::move(finalScores), exp);
+ TEST_DO(testScaling(initScores, std::move(finalScores), exp));
}
{ // minimal scale (second phase range = 0 (4 - 4) -> 1)
exp[0]._rankValue = 1; // scaled
@@ -371,7 +371,7 @@ TEST("testScaling") {
finalScores[3] = 4;
finalScores[4] = 4;
- testScaling(initScores, std::move(finalScores), exp);
+ TEST_DO(testScaling(initScores, std::move(finalScores), exp));
}
{ // minimal scale (first phase range = 0 (4000 - 4000) -> 1)
std::vector<feature_t> is(initScores);
@@ -387,7 +387,7 @@ TEST("testScaling") {
finalScores[3] = 400;
finalScores[4] = 500;
- testScaling(is, std::move(finalScores), exp);
+ TEST_DO(testScaling(is, std::move(finalScores), exp));
}
}