diff options
author | Ivan Fefer <ivan.fefer@mongodb.com> | 2022-09-21 15:57:14 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2022-09-21 17:25:41 +0000 |
commit | 3aa324accc64c532f53983258b4c7624b0b0158c (patch) | |
tree | 51fc3643aa0d4c14d79b55196964a4f37c665b63 | |
parent | 6f450a3cea6287612329f44e90536d72fe7c16c5 (diff) | |
download | mongo-3aa324accc64c532f53983258b4c7624b0b0158c.tar.gz |
SERVER-69798: Add benchmark for SBE expressions
-rw-r--r-- | buildscripts/resmokeconfig/suites/benchmarks.yml | 3 | ||||
-rw-r--r-- | buildscripts/resmokeconfig/suites/benchmarks_expression.yml | 14 | ||||
-rw-r--r-- | buildscripts/resmokeconfig/suites/benchmarks_expression_sbe.yml | 14 | ||||
-rw-r--r-- | etc/evergreen_yml_components/definitions.yml | 24 | ||||
-rw-r--r-- | src/mongo/db/pipeline/SConscript | 19 | ||||
-rw-r--r-- | src/mongo/db/pipeline/expression_bm.cpp | 499 | ||||
-rw-r--r-- | src/mongo/db/pipeline/expression_bm_fixture.cpp | 446 | ||||
-rw-r--r-- | src/mongo/db/pipeline/expression_bm_fixture.h | 251 | ||||
-rw-r--r-- | src/mongo/db/query/SConscript | 16 | ||||
-rw-r--r-- | src/mongo/db/query/sbe_expression_bm.cpp | 147 |
10 files changed, 957 insertions, 476 deletions
diff --git a/buildscripts/resmokeconfig/suites/benchmarks.yml b/buildscripts/resmokeconfig/suites/benchmarks.yml index 5bbd6e460ef..e4cb3c6b7aa 100644 --- a/buildscripts/resmokeconfig/suites/benchmarks.yml +++ b/buildscripts/resmokeconfig/suites/benchmarks.yml @@ -18,6 +18,9 @@ selector: - build/install/bin/simple8b_bm* # Hash table benchmark is really slow, don't run on evergreen - build/install/bin/hash_table_bm* + # These benchmarks are being run as part of the benchmarks_expression*.yml + - build/install/bin/expression_bm* + - build/install/bin/sbe_expression_bm* executor: diff --git a/buildscripts/resmokeconfig/suites/benchmarks_expression.yml b/buildscripts/resmokeconfig/suites/benchmarks_expression.yml new file mode 100644 index 00000000000..809ab75b036 --- /dev/null +++ b/buildscripts/resmokeconfig/suites/benchmarks_expression.yml @@ -0,0 +1,14 @@ +# This benchmark measures the performance of expressions in classic engine. +test_kind: benchmark_test + +selector: + root: build/benchmarks.txt + include_files: + # The trailing asterisk is for handling the .exe extension on Windows. + - build/**/system_resource_canary_bm* + - build/install/bin/expression_bm* + +executor: + config: {} + hooks: + - class: CombineBenchmarkResults diff --git a/buildscripts/resmokeconfig/suites/benchmarks_expression_sbe.yml b/buildscripts/resmokeconfig/suites/benchmarks_expression_sbe.yml new file mode 100644 index 00000000000..3661db88c3d --- /dev/null +++ b/buildscripts/resmokeconfig/suites/benchmarks_expression_sbe.yml @@ -0,0 +1,14 @@ +# This benchmark measures the performance of expressions in SBE. +test_kind: benchmark_test + +selector: + root: build/benchmarks.txt + include_files: + # The trailing asterisk is for handling the .exe extension on Windows. + - build/**/system_resource_canary_bm* + - build/install/bin/sbe_expression_bm* + +executor: + config: {} + hooks: + - class: CombineBenchmarkResults diff --git a/etc/evergreen_yml_components/definitions.yml b/etc/evergreen_yml_components/definitions.yml index b89ac1a9fba..7d61b70bac7 100644 --- a/etc/evergreen_yml_components/definitions.yml +++ b/etc/evergreen_yml_components/definitions.yml @@ -3456,6 +3456,30 @@ tasks: # - func: "send benchmark results" # - func: "analyze benchmark results" +- <<: *benchmark_template + name: benchmarks_expression + tags: ["benchmarks"] + commands: + - func: "do benchmark setup" + - func: "run tests" + vars: + suite: benchmarks_expression + exec_timeout_secs: 18000 # 5 hour timeout. + resmoke_jobs_max: 1 + - func: "send benchmark results" + +- <<: *benchmark_template + name: benchmarks_expression_sbe + tags: ["benchmarks"] + commands: + - func: "do benchmark setup" + - func: "run tests" + vars: + suite: benchmarks_expression_sbe + exec_timeout_secs: 18000 # 5 hour timeout. + resmoke_jobs_max: 1 + - func: "send benchmark results" + - <<: *run_jepsen_template name: jepsen_register_findAndModify tags: ["jepsen"] diff --git a/src/mongo/db/pipeline/SConscript b/src/mongo/db/pipeline/SConscript index bca0ed04164..e3ef1f3b6e6 100644 --- a/src/mongo/db/pipeline/SConscript +++ b/src/mongo/db/pipeline/SConscript @@ -647,6 +647,21 @@ env.CppUnitTest( ], ) +bmEnv = env.Clone() +bmEnv.InjectThirdParty(libraries=["benchmark"]) + +bmEnv.Library( + target='expression_bm_fixture', + source=[ + 'expression_bm_fixture.cpp', + ], + LIBDEPS=[ + '$BUILD_DIR/mongo/base', + '$BUILD_DIR/mongo/db/exec/document_value/document_value', + '$BUILD_DIR/third_party/shim_benchmark', + ], +) + env.Benchmark( target='expression_bm', source=[ @@ -654,7 +669,7 @@ env.Benchmark( ], LIBDEPS=[ '$BUILD_DIR/mongo/db/query/query_test_service_context', - '$BUILD_DIR/mongo/db/query_expressions', - '$BUILD_DIR/mongo/db/service_context_test_fixture', + '$BUILD_DIR/mongo/db/query_expressions', '$BUILD_DIR/mongo/db/service_context_test_fixture', + 'expression_bm_fixture' ], ) diff --git a/src/mongo/db/pipeline/expression_bm.cpp b/src/mongo/db/pipeline/expression_bm.cpp index 0d2a2e798c3..001d0d9b6f8 100644 --- a/src/mongo/db/pipeline/expression_bm.cpp +++ b/src/mongo/db/pipeline/expression_bm.cpp @@ -33,487 +33,42 @@ #include "mongo/db/matcher/expression_parser.h" #include "mongo/db/pipeline/expression.h" +#include "mongo/db/pipeline/expression_bm_fixture.h" #include "mongo/db/pipeline/expression_context_for_test.h" #include "mongo/db/query/query_test_service_context.h" namespace mongo { namespace { -void benchmarkExpression(BSONObj expressionSpec, - benchmark::State& state, - const std::vector<Document>& documents) { - QueryTestServiceContext testServiceContext; - auto opContext = testServiceContext.makeOperationContext(); - NamespaceString nss("test.bm"); - auto exprContext = make_intrusive<ExpressionContextForTest>(opContext.get(), nss); - // Build an expression. - auto expression = Expression::parseExpression( - exprContext.get(), expressionSpec, exprContext->variablesParseState); - - expression = expression->optimize(); - - // Prepare parameters for the 'evaluate()' call. - auto variables = &(exprContext->variables); - - // Run the test. - for (auto keepRunning : state) { - for (const auto& document : documents) { - benchmark::DoNotOptimize(expression->evaluate(document, variables)); +class ClassicExpressionBenchmarkFixture : public ExpressionBenchmarkFixture { + void benchmarkExpression(BSONObj expressionSpec, + benchmark::State& state, + const std::vector<Document>& documents) override final { + QueryTestServiceContext testServiceContext; + auto opContext = testServiceContext.makeOperationContext(); + NamespaceString nss("test.bm"); + auto exprContext = make_intrusive<ExpressionContextForTest>(opContext.get(), nss); + + // Build an expression. + auto expression = Expression::parseExpression( + exprContext.get(), expressionSpec, exprContext->variablesParseState); + + expression = expression->optimize(); + + // Prepare parameters for the 'evaluate()' call. + auto variables = &(exprContext->variables); + + // Run the test. + for (auto keepRunning : state) { + for (const auto& document : documents) { + benchmark::DoNotOptimize(expression->evaluate(document, variables)); + } + benchmark::ClobberMemory(); } - benchmark::ClobberMemory(); - } -} - -void benchmarkExpression(BSONObj expressionSpec, benchmark::State& state) { - std::vector<Document> documents = {{}}; - benchmarkExpression(expressionSpec, state, documents); -} - -/** - * Tests performance of 'evaluate()' of $dateDiff expression. - * - * startDate - start date in milliseconds from the UNIX epoch. - * endDate - end date in milliseconds from the UNIX epoch. - * unit - a string expression of units to use for date difference calculation. - * timezone - a string representation of timezone to use for date difference calculation. - * startOfWeek - a string representation of the first day of the week to use for date difference - * calculation when unit is a week. - * state - benchmarking state. - */ -void testDateDiffExpression(long long startDate, - long long endDate, - std::string unit, - boost::optional<std::string> timezone, - boost::optional<std::string> startOfWeek, - benchmark::State& state) { - // Build a $dateDiff expression. - BSONObjBuilder objBuilder; - objBuilder << "startDate" << Date_t::fromMillisSinceEpoch(startDate) << "endDate" - << "$endDate" - << "unit" << unit; - if (timezone) { - objBuilder << "timezone" << *timezone; - } - if (startOfWeek) { - objBuilder << "startOfWeek" << *startOfWeek; } - benchmarkExpression( - BSON("$dateDiff" << objBuilder.obj()), - state, - std::vector<Document>(1, {{"endDate"_sd, Date_t::fromMillisSinceEpoch(endDate)}})); -} - -void BM_DateDiffEvaluateMinute300Years(benchmark::State& state) { - testDateDiffExpression(-1640989478000LL /* 1918-01-01*/, - 7826117722000LL /* 2218-01-01*/, - "minute", - boost::none /*timezone*/, - boost::none /*startOfWeek*/, - state); -} - -void BM_DateDiffEvaluateMinute2Years(benchmark::State& state) { - testDateDiffExpression(1542448721000LL /* 2018-11-17*/, - 1605607121000LL /* 2020-11-17*/, - "minute", - boost::none /*timezone*/, - boost::none /*startOfWeek*/, - state); -} - -void BM_DateDiffEvaluateMinute2YearsWithTimezone(benchmark::State& state) { - testDateDiffExpression(1542448721000LL /* 2018-11-17*/, - 1605607121000LL /* 2020-11-17*/, - "minute", - std::string{"America/New_York"}, - boost::none /*startOfWeek*/, - state); -} - -void BM_DateDiffEvaluateWeek(benchmark::State& state) { - testDateDiffExpression(7826117722000LL /* 2218-01-01*/, - 4761280721000LL /*2120-11-17*/, - "week", - boost::none /*timezone*/, - std::string("Sunday") /*startOfWeek*/, - state); -} - -BENCHMARK(BM_DateDiffEvaluateMinute300Years); -BENCHMARK(BM_DateDiffEvaluateMinute2Years); -BENCHMARK(BM_DateDiffEvaluateMinute2YearsWithTimezone); -BENCHMARK(BM_DateDiffEvaluateWeek); - -/** - * Tests performance of evaluate() method of $dateAdd - */ -void testDateAddExpression(long long startDate, - std::string unit, - long long amount, - boost::optional<std::string> timezone, - benchmark::State& state) { - BSONObjBuilder objBuilder; - objBuilder << "startDate" - << "$startDate" - << "unit" << unit << "amount" << amount; - if (timezone) { - objBuilder << "timezone" << *timezone; - } - benchmarkExpression( - BSON("$dateAdd" << objBuilder.obj()), - state, - std::vector<Document>(1, {{"startDate"_sd, Date_t::fromMillisSinceEpoch(startDate)}})); -} - -void BM_DateAddEvaluate10Days(benchmark::State& state) { - testDateAddExpression(1604131115000LL, - "day", - 10LL, - boost::none, /* timezone */ - state); -} - -void BM_DateAddEvaluate600Minutes(benchmark::State& state) { - testDateAddExpression(1604131115000LL, - "minute", - 600LL, - boost::none, /* timezone */ - state); -} - -void BM_DateAddEvaluate100KSeconds(benchmark::State& state) { - testDateAddExpression(1604131115000LL, - "second", - 100000LL, - boost::none, /* timezone */ - state); -} - -void BM_DateAddEvaluate100Years(benchmark::State& state) { - testDateAddExpression(1604131115000LL, - "year", - 100LL, - boost::none, /* timezone */ - state); -} - -void BM_DateAddEvaluate12HoursWithTimezone(benchmark::State& state) { - testDateAddExpression(1604131115000LL, "hour", 12LL, std::string{"America/New_York"}, state); -} - -BENCHMARK(BM_DateAddEvaluate10Days); -BENCHMARK(BM_DateAddEvaluate600Minutes); -BENCHMARK(BM_DateAddEvaluate100KSeconds); -BENCHMARK(BM_DateAddEvaluate100Years); -BENCHMARK(BM_DateAddEvaluate12HoursWithTimezone); - -/** - * Tests performance of 'evaluate()' of $dateTrunc expression. - * - * date - start date in milliseconds from the UNIX epoch. - * unit - a string expression of units to use for date difference calculation. - * timezone - a string representation of timezone to use for date difference calculation. - * startOfWeek - a string representation of the first day of the week to use for date difference - * calculation when unit is a week. - * state - benchmarking state. - */ -void testDateTruncExpression(long long date, - std::string unit, - unsigned long binSize, - boost::optional<std::string> timezone, - boost::optional<std::string> startOfWeek, - benchmark::State& state) { - // Build a $dateTrunc expression. - BSONObjBuilder objBuilder; - objBuilder << "date" - << "$date" - << "unit" << unit << "binSize" << static_cast<long long>(binSize); - if (timezone) { - objBuilder << "timezone" << *timezone; - } - if (startOfWeek) { - objBuilder << "startOfWeek" << *startOfWeek; - } - benchmarkExpression( - BSON("$dateTrunc" << objBuilder.obj()), - state, - std::vector<Document>(1, {{"date"_sd, Date_t::fromMillisSinceEpoch(date)}})); -} - -void BM_DateTruncEvaluateMinute15NewYork(benchmark::State& state) { - testDateTruncExpression(1615460825000LL /* year 2021*/, - "minute", - 15, - std::string{"America/New_York"}, - boost::none /* startOfWeek */, - state); -} - -void BM_DateTruncEvaluateMinute15UTC(benchmark::State& state) { - testDateTruncExpression(1615460825000LL /* year 2021*/, - "minute", - 15, - boost::none, - boost::none /* startOfWeek */, - state); -} - -void BM_DateTruncEvaluateHour1UTCMinus0700(benchmark::State& state) { - testDateTruncExpression(1615460825000LL /* year 2021*/, - "hour", - 1, - std::string{"-07:00"}, - boost::none /* startOfWeek */, - state); -} - -void BM_DateTruncEvaluateWeek2NewYorkValue2100(benchmark::State& state) { - testDateTruncExpression(4108446425000LL /* year 2100*/, - "week", - 2, - std::string{"America/New_York"}, - std::string{"monday"} /* startOfWeek */, - state); -} - -void BM_DateTruncEvaluateWeek2UTCValue2100(benchmark::State& state) { - testDateTruncExpression(4108446425000LL /* year 2100*/, - "week", - 2, - std::string{"UTC"}, - std::string{"monday"} /* startOfWeek */, - state); -} - -void BM_DateTruncEvaluateMonth6NewYorkValue2100(benchmark::State& state) { - testDateTruncExpression(4108446425000LL /* year 2100*/, - "month", - 6, - std::string{"America/New_York"}, - boost::none /* startOfWeek */, - state); -} - -void BM_DateTruncEvaluateMonth6NewYorkValue2030(benchmark::State& state) { - testDateTruncExpression(1893466800000LL /* year 2030*/, - "month", - 6, - std::string{"America/New_York"}, - boost::none /* startOfWeek */, - state); -} - -void BM_DateTruncEvaluateMonth6UTCValue2030(benchmark::State& state) { - testDateTruncExpression(1893466800000LL /* year 2030*/, - "month", - 8, - boost::none, - boost::none /* startOfWeek */, - state); -} - -void BM_DateTruncEvaluateYear1NewYorkValue2020(benchmark::State& state) { - testDateTruncExpression(1583924825000LL /* year 2020*/, - "year", - 1, - std::string{"America/New_York"}, - boost::none /* startOfWeek */, - state); -} - -void BM_DateTruncEvaluateYear1UTCValue2020(benchmark::State& state) { - testDateTruncExpression(1583924825000LL /* year 2020*/, - "year", - 1, - boost::none, - boost::none /* startOfWeek */, - state); -} - -void BM_DateTruncEvaluateYear1NewYorkValue2100(benchmark::State& state) { - testDateTruncExpression(4108446425000LL /* year 2100*/, - "year", - 1, - std::string{"America/New_York"}, - boost::none /* startOfWeek */, - state); -} - -BENCHMARK(BM_DateTruncEvaluateMinute15NewYork); -BENCHMARK(BM_DateTruncEvaluateMinute15UTC); -BENCHMARK(BM_DateTruncEvaluateHour1UTCMinus0700); -BENCHMARK(BM_DateTruncEvaluateWeek2NewYorkValue2100); -BENCHMARK(BM_DateTruncEvaluateWeek2UTCValue2100); -BENCHMARK(BM_DateTruncEvaluateMonth6NewYorkValue2100); -BENCHMARK(BM_DateTruncEvaluateMonth6NewYorkValue2030); -BENCHMARK(BM_DateTruncEvaluateMonth6UTCValue2030); -BENCHMARK(BM_DateTruncEvaluateYear1NewYorkValue2020); -BENCHMARK(BM_DateTruncEvaluateYear1UTCValue2020); -BENCHMARK(BM_DateTruncEvaluateYear1NewYorkValue2100); - -/** - * Tests performance of 'evaluate()' of $getField expression. - */ -void BM_GetFieldEvaluateExpression(benchmark::State& state) { - BSONObjBuilder objBuilder; - objBuilder << "field" - << "x.y$z" - << "input" - << BSON("$const" << BSON("x.y$z" - << "abc")); - - benchmarkExpression(BSON("$getField" << objBuilder.obj()), state); -} - -void BM_GetFieldEvaluateShortSyntaxExpression(benchmark::State& state) { - benchmarkExpression(BSON("$getField" << BSON("$const" - << "$foo")), - state); -} - -void BM_GetFieldNestedExpression(benchmark::State& state) { - BSONObjBuilder innerObjBuilder; - innerObjBuilder << "field" - << "a.b" - << "input" << BSON("$const" << BSON("a.b" << BSON("c" << 123))); - BSONObjBuilder outerObjBuilder; - outerObjBuilder << "field" - << "c" - << "input" << BSON("$getField" << innerObjBuilder.obj()); - benchmarkExpression(BSON("$getField" << outerObjBuilder.obj()), state); -} - -BENCHMARK(BM_GetFieldEvaluateExpression); -BENCHMARK(BM_GetFieldEvaluateShortSyntaxExpression); -BENCHMARK(BM_GetFieldNestedExpression); - -/** - * Tests performance of 'evaluate()' of $setField and $unsetField expressions. - */ -void testSetFieldExpression(std::string fieldname, - std::string oldFieldValue, - std::string newFieldValue, - benchmark::State& state) { - BSONObjBuilder objBuilder; - objBuilder << "field" << fieldname << "input" - << BSON("$const" << BSON(fieldname << oldFieldValue << "f1" << 1 << "f2" << 2)) - << "value" << newFieldValue; - - benchmarkExpression(BSON("$setField" << objBuilder.obj()), state); -} - -void BM_SetFieldEvaluateExpression(benchmark::State& state) { - testSetFieldExpression("a.b", "x", "y", state); -} - -// The following two functions test different syntax for equivalent expressions: -// $unsetField is an alias for $setField with $$REMOVE. -void BM_SetFieldWithRemoveExpression(benchmark::State& state) { - testSetFieldExpression("a$b", "x", "$$REMOVE", state); -} - -void BM_UnsetFieldEvaluateExpression(benchmark::State& state) { - BSONObjBuilder objBuilder; - objBuilder << "field" - << "a$b.c" - << "input" - << BSON("$const" << BSON("a$b.c" - << "x" - << "f1" << 1 << "f2" << 2)); - - benchmarkExpression(BSON("$unsetField" << objBuilder.obj()), state); -} - -BENCHMARK(BM_SetFieldEvaluateExpression); -BENCHMARK(BM_SetFieldWithRemoveExpression); -BENCHMARK(BM_UnsetFieldEvaluateExpression); - -BSONArray randomBSONArray(int count, int max, int offset = 0) { - BSONArrayBuilder builder; - auto rng = PseudoRandom(std::random_device()()); - for (int i = 0; i < count; i++) { - builder.append(std::to_string(offset + rng.nextInt32(max))); - } - return builder.arr(); -} - -BSONArray rangeBSONArray(int count) { - BSONArrayBuilder builder; - for (int i = 0; i < count; i++) { - builder.append(std::to_string(i)); - } - return builder.arr(); -} - -/** - * Tests performance of $set* expressions. - */ -void BM_SetIsSubset_allPresent(benchmark::State& state) { - const int kMax = 100000; - BSONArray lhs = randomBSONArray(100000, kMax); - BSONArray rhs = rangeBSONArray(kMax); - - benchmarkExpression(BSON("$setIsSubset" << BSON_ARRAY("$arr" << rhs)), - state, - std::vector<Document>(100, {{"arr"_sd, lhs}})); -} - -void BM_SetIsSubset_nonePresent(benchmark::State& state) { - const int kMax = 100000; - BSONArray lhs = randomBSONArray(100000, kMax, kMax); - BSONArray rhs = rangeBSONArray(kMax); - - benchmarkExpression(BSON("$setIsSubset" << BSON_ARRAY("$arr" << rhs)), - state, - std::vector<Document>(100, {{"arr"_sd, lhs}})); -} - -void BM_SetIntersection(benchmark::State& state) { - const int kMax = 100000; - BSONArray lhs = randomBSONArray(100000, kMax); - BSONArray rhs = randomBSONArray(100000, kMax); - - benchmarkExpression(BSON("$setIntersection" << BSON_ARRAY("$arr" << rhs)), - state, - std::vector<Document>(100, {{"arr"_sd, lhs}})); -} - -void BM_SetDifference(benchmark::State& state) { - const int kMax = 100000; - BSONArray lhs = randomBSONArray(100000, kMax); - BSONArray rhs = randomBSONArray(100000, kMax); - - benchmarkExpression(BSON("$setDifference" << BSON_ARRAY("$arr" << rhs)), - state, - std::vector<Document>(100, {{"arr"_sd, lhs}})); -} - -void BM_SetEquals(benchmark::State& state) { - const int kMax = 100000; - BSONArray lhs = randomBSONArray(100000, kMax); - BSONArray rhs = randomBSONArray(100000, kMax); - - benchmarkExpression(BSON("$setEquals" << BSON_ARRAY("$arr" << rhs)), - state, - std::vector<Document>(100, {{"arr"_sd, lhs}})); -} - -void BM_SetUnion(benchmark::State& state) { - const int kMax = 100000; - BSONArray lhs = randomBSONArray(100000, kMax); - BSONArray rhs = randomBSONArray(100000, kMax); - - benchmarkExpression(BSON("$setUnion" << BSON_ARRAY("$arr" << rhs)), - state, - std::vector<Document>(100, {{"arr"_sd, lhs}})); -} +}; -BENCHMARK(BM_SetIsSubset_allPresent); -BENCHMARK(BM_SetIsSubset_nonePresent); -BENCHMARK(BM_SetIntersection); -BENCHMARK(BM_SetDifference); -BENCHMARK(BM_SetEquals); -BENCHMARK(BM_SetUnion); +BENCHMARK_EXPRESSIONS(ClassicExpressionBenchmarkFixture) } // namespace } // namespace mongo diff --git a/src/mongo/db/pipeline/expression_bm_fixture.cpp b/src/mongo/db/pipeline/expression_bm_fixture.cpp new file mode 100644 index 00000000000..bf118030a7f --- /dev/null +++ b/src/mongo/db/pipeline/expression_bm_fixture.cpp @@ -0,0 +1,446 @@ +/** + * Copyright (C) 2022-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * <http://www.mongodb.com/licensing/server-side-public-license>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the Server Side Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#include "mongo/db/pipeline/expression_bm_fixture.h" + +namespace mongo { + +namespace { + +BSONArray rangeBSONArray(int count) { + BSONArrayBuilder builder; + for (int i = 0; i < count; i++) { + builder.append(std::to_string(i)); + } + return builder.arr(); +} +} // namespace + +void ExpressionBenchmarkFixture::benchmarkExpression(BSONObj expressionSpec, + benchmark::State& state) { + std::vector<Document> documents = {{}}; + benchmarkExpression(expressionSpec, state, documents); +} + +void ExpressionBenchmarkFixture::noOpBenchmark(benchmark::State& state) { + benchmarkExpression(BSON("$const" << 1), state); +} + +void ExpressionBenchmarkFixture::benchmarkDateDiffEvaluateMinute300Years(benchmark::State& state) { + testDateDiffExpression(-1640989478000LL /* 1918-01-01*/, + 7826117722000LL /* 2218-01-01*/, + "minute", + boost::none /*timezone*/, + boost::none /*startOfWeek*/, + state); +} + +void ExpressionBenchmarkFixture::benchmarkDateDiffEvaluateMinute2Years(benchmark::State& state) { + testDateDiffExpression(1542448721000LL /* 2018-11-17*/, + 1605607121000LL /* 2020-11-17*/, + "minute", + boost::none /*timezone*/, + boost::none /*startOfWeek*/, + state); +} + +void ExpressionBenchmarkFixture::benchmarkDateDiffEvaluateMinute2YearsWithTimezone( + benchmark::State& state) { + testDateDiffExpression(1542448721000LL /* 2018-11-17*/, + 1605607121000LL /* 2020-11-17*/, + "minute", + std::string{"America/New_York"}, + boost::none /*startOfWeek*/, + state); +} + +void ExpressionBenchmarkFixture::benchmarkDateDiffEvaluateWeek(benchmark::State& state) { + testDateDiffExpression(7826117722000LL /* 2218-01-01*/, + 4761280721000LL /*2120-11-17*/, + "week", + boost::none /*timezone*/, + std::string("Sunday") /*startOfWeek*/, + state); +} + + +void ExpressionBenchmarkFixture::benchmarkDateAddEvaluate10Days(benchmark::State& state) { + testDateAddExpression(1604131115000LL, + "day", + 10LL, + boost::none, /* timezone */ + state); +} + +void ExpressionBenchmarkFixture::benchmarkDateAddEvaluate600Minutes(benchmark::State& state) { + testDateAddExpression(1604131115000LL, + "minute", + 600LL, + boost::none, /* timezone */ + state); +} + +void ExpressionBenchmarkFixture::benchmarkDateAddEvaluate100KSeconds(benchmark::State& state) { + testDateAddExpression(1604131115000LL, + "second", + 100000LL, + boost::none, /* timezone */ + state); +} + +void ExpressionBenchmarkFixture::benchmarkDateAddEvaluate100Years(benchmark::State& state) { + testDateAddExpression(1604131115000LL, + "year", + 100LL, + boost::none, /* timezone */ + state); +} + +void ExpressionBenchmarkFixture::benchmarkDateAddEvaluate12HoursWithTimezone( + benchmark::State& state) { + testDateAddExpression(1604131115000LL, "hour", 12LL, std::string{"America/New_York"}, state); +} + +void ExpressionBenchmarkFixture::benchmarkDateTruncEvaluateMinute15NewYork( + benchmark::State& state) { + testDateTruncExpression(1615460825000LL /* year 2021*/, + "minute", + 15, + std::string{"America/New_York"}, + boost::none /* startOfWeek */, + state); +} + +void ExpressionBenchmarkFixture::benchmarkDateTruncEvaluateMinute15UTC(benchmark::State& state) { + testDateTruncExpression(1615460825000LL /* year 2021*/, + "minute", + 15, + boost::none, + boost::none /* startOfWeek */, + state); +} + +void ExpressionBenchmarkFixture::benchmarkDateTruncEvaluateHour1UTCMinus0700( + benchmark::State& state) { + testDateTruncExpression(1615460825000LL /* year 2021*/, + "hour", + 1, + std::string{"-07:00"}, + boost::none /* startOfWeek */, + state); +} + +void ExpressionBenchmarkFixture::benchmarkDateTruncEvaluateWeek2NewYorkValue2100( + benchmark::State& state) { + testDateTruncExpression(4108446425000LL /* year 2100*/, + "week", + 2, + std::string{"America/New_York"}, + std::string{"monday"} /* startOfWeek */, + state); +} + +void ExpressionBenchmarkFixture::benchmarkDateTruncEvaluateWeek2UTCValue2100( + benchmark::State& state) { + testDateTruncExpression(4108446425000LL /* year 2100*/, + "week", + 2, + std::string{"UTC"}, + std::string{"monday"} /* startOfWeek */, + state); +} + +void ExpressionBenchmarkFixture::benchmarkDateTruncEvaluateMonth6NewYorkValue2100( + benchmark::State& state) { + testDateTruncExpression(4108446425000LL /* year 2100*/, + "month", + 6, + std::string{"America/New_York"}, + boost::none /* startOfWeek */, + state); +} + +void ExpressionBenchmarkFixture::benchmarkDateTruncEvaluateMonth6NewYorkValue2030( + benchmark::State& state) { + testDateTruncExpression(1893466800000LL /* year 2030*/, + "month", + 6, + std::string{"America/New_York"}, + boost::none /* startOfWeek */, + state); +} + +void ExpressionBenchmarkFixture::benchmarkDateTruncEvaluateMonth6UTCValue2030( + benchmark::State& state) { + testDateTruncExpression(1893466800000LL /* year 2030*/, + "month", + 8, + boost::none, + boost::none /* startOfWeek */, + state); +} + +void ExpressionBenchmarkFixture::benchmarkDateTruncEvaluateYear1NewYorkValue2020( + benchmark::State& state) { + testDateTruncExpression(1583924825000LL /* year 2020*/, + "year", + 1, + std::string{"America/New_York"}, + boost::none /* startOfWeek */, + state); +} + +void ExpressionBenchmarkFixture::benchmarkDateTruncEvaluateYear1UTCValue2020( + benchmark::State& state) { + testDateTruncExpression(1583924825000LL /* year 2020*/, + "year", + 1, + boost::none, + boost::none /* startOfWeek */, + state); +} + +void ExpressionBenchmarkFixture::benchmarkDateTruncEvaluateYear1NewYorkValue2100( + benchmark::State& state) { + testDateTruncExpression(4108446425000LL /* year 2100*/, + "year", + 1, + std::string{"America/New_York"}, + boost::none /* startOfWeek */, + state); +} + +/** + * Tests performance of $getField expression. + */ +void ExpressionBenchmarkFixture::benchmarkGetFieldEvaluateExpression(benchmark::State& state) { + BSONObjBuilder objBuilder; + objBuilder << "field" + << "x.y$z" + << "input" + << BSON("$const" << BSON("x.y$z" + << "abc")); + + benchmarkExpression(BSON("$getField" << objBuilder.obj()), state); +} + +void ExpressionBenchmarkFixture::benchmarkGetFieldEvaluateShortSyntaxExpression( + benchmark::State& state) { + benchmarkExpression(BSON("$getField" << BSON("$const" + << "$foo")), + state); +} + +void ExpressionBenchmarkFixture::benchmarkGetFieldNestedExpression(benchmark::State& state) { + BSONObjBuilder innerObjBuilder; + innerObjBuilder << "field" + << "a.b" + << "input" << BSON("$const" << BSON("a.b" << BSON("c" << 123))); + BSONObjBuilder outerObjBuilder; + outerObjBuilder << "field" + << "c" + << "input" << BSON("$getField" << innerObjBuilder.obj()); + benchmarkExpression(BSON("$getField" << outerObjBuilder.obj()), state); +} + +void ExpressionBenchmarkFixture::benchmarkSetFieldEvaluateExpression(benchmark::State& state) { + testSetFieldExpression("a.b", "x", "y", state); +} + +// The following two functions test different syntax for equivalent expressions: +// $unsetField is an alias for $setField with $$REMOVE. +void ExpressionBenchmarkFixture::benchmarkSetFieldWithRemoveExpression(benchmark::State& state) { + testSetFieldExpression("a$b", "x", "$$REMOVE", state); +} + +void ExpressionBenchmarkFixture::benchmarkUnsetFieldEvaluateExpression(benchmark::State& state) { + BSONObjBuilder objBuilder; + objBuilder << "field" + << "a$b.c" + << "input" + << BSON("$const" << BSON("a$b.c" + << "x" + << "f1" << 1 << "f2" << 2)); + + benchmarkExpression(BSON("$unsetField" << objBuilder.obj()), state); +} + +/** + * Tests performance of $set* expressions. + */ +void ExpressionBenchmarkFixture::benchmarkSetIsSubset_allPresent(benchmark::State& state) { + const int kMax = 100000; + BSONArray lhs = randomBSONArray(100000, kMax); + BSONArray rhs = rangeBSONArray(kMax); + + benchmarkExpression(BSON("$setIsSubset" << BSON_ARRAY("$arr" << rhs)), + state, + std::vector<Document>(100, {{"arr"_sd, lhs}})); +} + +void ExpressionBenchmarkFixture::benchmarkSetIsSubset_nonePresent(benchmark::State& state) { + const int kMax = 100000; + BSONArray lhs = randomBSONArray(100000, kMax, kMax); + BSONArray rhs = rangeBSONArray(kMax); + + benchmarkExpression(BSON("$setIsSubset" << BSON_ARRAY("$arr" << rhs)), + state, + std::vector<Document>(100, {{"arr"_sd, lhs}})); +} + +void ExpressionBenchmarkFixture::benchmarkSetIntersection(benchmark::State& state) { + const int kMax = 100000; + BSONArray lhs = randomBSONArray(100000, kMax); + BSONArray rhs = randomBSONArray(100000, kMax); + + benchmarkExpression(BSON("$setIntersection" << BSON_ARRAY("$lhs" + << "$rhs")), + state, + std::vector<Document>(100, {{"lhs", lhs}, {"rhs", rhs}})); +} + +void ExpressionBenchmarkFixture::benchmarkSetDifference(benchmark::State& state) { + const int kMax = 100000; + BSONArray lhs = randomBSONArray(100000, kMax); + BSONArray rhs = randomBSONArray(100000, kMax); + + benchmarkExpression(BSON("$setDifference" << BSON_ARRAY("$lhs" + << "$rhs")), + state, + std::vector<Document>(100, {{"lhs", lhs}, {"rhs", rhs}})); +} + +void ExpressionBenchmarkFixture::benchmarkSetEquals(benchmark::State& state) { + const int kMax = 100000; + BSONArray lhs = randomBSONArray(100000, kMax); + BSONArray rhs = randomBSONArray(100000, kMax); + + benchmarkExpression(BSON("$setEquals" << BSON_ARRAY("$lhs" + << "$rhs")), + state, + std::vector<Document>(100, {{"lhs", lhs}, {"rhs", rhs}})); +} + +void ExpressionBenchmarkFixture::benchmarkSetUnion(benchmark::State& state) { + const int kMax = 100000; + BSONArray lhs = randomBSONArray(100000, kMax); + BSONArray rhs = randomBSONArray(100000, kMax); + + benchmarkExpression(BSON("$setUnion" << BSON_ARRAY("$lhs" + << "$rhs")), + state, + std::vector<Document>(100, {{"lhs"_sd, lhs}, {"rhs"_sd, rhs}})); +} + +void ExpressionBenchmarkFixture::testDateDiffExpression(long long startDate, + long long endDate, + std::string unit, + boost::optional<std::string> timezone, + boost::optional<std::string> startOfWeek, + benchmark::State& state) { + // Build a $dateDiff expression. + BSONObjBuilder objBuilder; + objBuilder << "startDate" << Date_t::fromMillisSinceEpoch(startDate) << "endDate" + << "$endDate" + << "unit" << unit; + if (timezone) { + objBuilder << "timezone" << *timezone; + } + if (startOfWeek) { + objBuilder << "startOfWeek" << *startOfWeek; + } + benchmarkExpression( + BSON("$dateDiff" << objBuilder.obj()), + state, + std::vector<Document>(1, {{"endDate"_sd, Date_t::fromMillisSinceEpoch(endDate)}})); +} + +void ExpressionBenchmarkFixture::testDateTruncExpression(long long date, + std::string unit, + unsigned long binSize, + boost::optional<std::string> timezone, + boost::optional<std::string> startOfWeek, + benchmark::State& state) { + // Build a $dateTrunc expression. + BSONObjBuilder objBuilder; + objBuilder << "date" + << "$date" + << "unit" << unit << "binSize" << static_cast<long long>(binSize); + if (timezone) { + objBuilder << "timezone" << *timezone; + } + if (startOfWeek) { + objBuilder << "startOfWeek" << *startOfWeek; + } + benchmarkExpression( + BSON("$dateTrunc" << objBuilder.obj()), + state, + std::vector<Document>(1, {{"date"_sd, Date_t::fromMillisSinceEpoch(date)}})); +} + +void ExpressionBenchmarkFixture::testDateAddExpression(long long startDate, + std::string unit, + long long amount, + boost::optional<std::string> timezone, + benchmark::State& state) { + BSONObjBuilder objBuilder; + objBuilder << "startDate" + << "$startDate" + << "unit" << unit << "amount" << amount; + if (timezone) { + objBuilder << "timezone" << *timezone; + } + benchmarkExpression( + BSON("$dateAdd" << objBuilder.obj()), + state, + std::vector<Document>(1, {{"startDate"_sd, Date_t::fromMillisSinceEpoch(startDate)}})); +} + +void ExpressionBenchmarkFixture::testSetFieldExpression(std::string fieldname, + std::string oldFieldValue, + std::string newFieldValue, + benchmark::State& state) { + BSONObjBuilder objBuilder; + objBuilder << "field" << fieldname << "input" + << BSON("$const" << BSON(fieldname << oldFieldValue << "f1" << 1 << "f2" << 2)) + << "value" << newFieldValue; + + benchmarkExpression(BSON("$setField" << objBuilder.obj()), state); +} + +BSONArray ExpressionBenchmarkFixture::randomBSONArray(int count, int max, int offset) { + BSONArrayBuilder builder; + for (int i = 0; i < count; i++) { + builder.append(std::to_string(offset + random.nextInt32(max))); + } + return builder.arr(); +} + + +} // namespace mongo diff --git a/src/mongo/db/pipeline/expression_bm_fixture.h b/src/mongo/db/pipeline/expression_bm_fixture.h new file mode 100644 index 00000000000..a0a3b8ab116 --- /dev/null +++ b/src/mongo/db/pipeline/expression_bm_fixture.h @@ -0,0 +1,251 @@ +/** + * Copyright (C) 2022-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * <http://www.mongodb.com/licensing/server-side-public-license>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the Server Side Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#pragma once + +#include "mongo/platform/basic.h" + +#include <benchmark/benchmark.h> + +#include "mongo/bson/bsonobj.h" +#include "mongo/db/exec/document_value/document.h" +#include "mongo/platform/random.h" + +namespace mongo { + +class ExpressionBenchmarkFixture : public benchmark::Fixture { +private: + static constexpr int32_t kSeed = 1; + +public: + ExpressionBenchmarkFixture() : random(kSeed){}; + + void benchmarkExpression(BSONObj expressionSpec, benchmark::State& benchmarkState); + + virtual void benchmarkExpression(BSONObj expressionSpec, + benchmark::State& benchmarkState, + const std::vector<Document>& documents) = 0; + + void noOpBenchmark(benchmark::State& state); + + void benchmarkDateDiffEvaluateMinute300Years(benchmark::State& state); + void benchmarkDateDiffEvaluateMinute2Years(benchmark::State& state); + void benchmarkDateDiffEvaluateMinute2YearsWithTimezone(benchmark::State& state); + void benchmarkDateDiffEvaluateWeek(benchmark::State& state); + + void benchmarkDateAddEvaluate10Days(benchmark::State& state); + void benchmarkDateAddEvaluate600Minutes(benchmark::State& state); + void benchmarkDateAddEvaluate100KSeconds(benchmark::State& state); + void benchmarkDateAddEvaluate100Years(benchmark::State& state); + void benchmarkDateAddEvaluate12HoursWithTimezone(benchmark::State& state); + + void benchmarkDateTruncEvaluateMinute15NewYork(benchmark::State& state); + void benchmarkDateTruncEvaluateMinute15UTC(benchmark::State& state); + void benchmarkDateTruncEvaluateHour1UTCMinus0700(benchmark::State& state); + void benchmarkDateTruncEvaluateWeek2NewYorkValue2100(benchmark::State& state); + void benchmarkDateTruncEvaluateWeek2UTCValue2100(benchmark::State& state); + void benchmarkDateTruncEvaluateMonth6NewYorkValue2100(benchmark::State& state); + void benchmarkDateTruncEvaluateMonth6NewYorkValue2030(benchmark::State& state); + void benchmarkDateTruncEvaluateMonth6UTCValue2030(benchmark::State& state); + void benchmarkDateTruncEvaluateYear1NewYorkValue2020(benchmark::State& state); + void benchmarkDateTruncEvaluateYear1UTCValue2020(benchmark::State& state); + void benchmarkDateTruncEvaluateYear1NewYorkValue2100(benchmark::State& state); + + void benchmarkGetFieldEvaluateExpression(benchmark::State& state); + void benchmarkGetFieldEvaluateShortSyntaxExpression(benchmark::State& state); + void benchmarkGetFieldNestedExpression(benchmark::State& state); + void benchmarkSetFieldEvaluateExpression(benchmark::State& state); + void benchmarkSetFieldWithRemoveExpression(benchmark::State& state); + void benchmarkUnsetFieldEvaluateExpression(benchmark::State& state); + + void benchmarkSetIsSubset_allPresent(benchmark::State& state); + void benchmarkSetIsSubset_nonePresent(benchmark::State& state); + void benchmarkSetIntersection(benchmark::State& state); + void benchmarkSetDifference(benchmark::State& state); + void benchmarkSetEquals(benchmark::State& state); + void benchmarkSetUnion(benchmark::State& state); + +private: + void testDateDiffExpression(long long startDate, + long long endDate, + std::string unit, + boost::optional<std::string> timezone, + boost::optional<std::string> startOfWeek, + benchmark::State& state); + void testDateTruncExpression(long long date, + std::string unit, + unsigned long binSize, + boost::optional<std::string> timezone, + boost::optional<std::string> startOfWeek, + benchmark::State& state); + void testDateAddExpression(long long startDate, + std::string unit, + long long amount, + boost::optional<std::string> timezone, + benchmark::State& state); + void testSetFieldExpression(std::string fieldname, + std::string oldFieldValue, + std::string newFieldValue, + benchmark::State& state); + + BSONArray randomBSONArray(int count, int max, int offset = 0); + + PseudoRandom random; +}; + +#define BENCHMARK_EXPRESSIONS(Fixture) \ + \ + BENCHMARK_F(Fixture, NoOp) \ + (benchmark::State & state) { \ + noOpBenchmark(state); \ + } \ + \ + BENCHMARK_F(Fixture, DateDiffEvaluateMinute300Years) \ + (benchmark::State & state) { \ + benchmarkDateDiffEvaluateMinute300Years(state); \ + } \ + BENCHMARK_F(Fixture, DateDiffEvaluateMinute2Years) \ + (benchmark::State & state) { \ + benchmarkDateDiffEvaluateMinute2Years(state); \ + } \ + BENCHMARK_F(Fixture, DateDiffEvaluateMinute2YearsWithTimezone) \ + (benchmark::State & state) { \ + benchmarkDateDiffEvaluateMinute2YearsWithTimezone(state); \ + } \ + BENCHMARK_F(Fixture, DateDiffEvaluateWeek)(benchmark::State & state) { \ + benchmarkDateDiffEvaluateWeek(state); \ + } \ + \ + BENCHMARK_F(Fixture, DateAddEvaluate10Days)(benchmark::State & state) { \ + benchmarkDateAddEvaluate10Days(state); \ + } \ + BENCHMARK_F(Fixture, DateAddEvaluate600Minutes)(benchmark::State & state) { \ + benchmarkDateAddEvaluate600Minutes(state); \ + } \ + BENCHMARK_F(Fixture, DateAddEvaluate100KSeconds) \ + (benchmark::State & state) { \ + benchmarkDateAddEvaluate100KSeconds(state); \ + } \ + BENCHMARK_F(Fixture, DateAddEvaluate100Years)(benchmark::State & state) { \ + benchmarkDateAddEvaluate100Years(state); \ + } \ + BENCHMARK_F(Fixture, DateAddEvaluate12HoursWithTimezone) \ + (benchmark::State & state) { \ + benchmarkDateAddEvaluate12HoursWithTimezone(state); \ + } \ + \ + BENCHMARK_F(Fixture, DateTruncEvaluateMinute15NewYork) \ + (benchmark::State & state) { \ + benchmarkDateTruncEvaluateMinute15NewYork(state); \ + } \ + BENCHMARK_F(Fixture, DateTruncEvaluateMinute15UTC) \ + (benchmark::State & state) { \ + benchmarkDateTruncEvaluateMinute15UTC(state); \ + } \ + BENCHMARK_F(Fixture, DateTruncEvaluateHour1UTCMinus0700) \ + (benchmark::State & state) { \ + benchmarkDateTruncEvaluateHour1UTCMinus0700(state); \ + } \ + BENCHMARK_F(Fixture, DateTruncEvaluateWeek2NewYorkValue2100) \ + (benchmark::State & state) { \ + benchmarkDateTruncEvaluateWeek2NewYorkValue2100(state); \ + } \ + BENCHMARK_F(Fixture, DateTruncEvaluateWeek2UTCValue2100) \ + (benchmark::State & state) { \ + benchmarkDateTruncEvaluateWeek2UTCValue2100(state); \ + } \ + BENCHMARK_F(Fixture, DateTruncEvaluateMonth6NewYorkValue2100) \ + (benchmark::State & state) { \ + benchmarkDateTruncEvaluateMonth6NewYorkValue2100(state); \ + } \ + BENCHMARK_F(Fixture, DateTruncEvaluateMonth6NewYorkValue2030) \ + (benchmark::State & state) { \ + benchmarkDateTruncEvaluateMonth6NewYorkValue2030(state); \ + } \ + BENCHMARK_F(Fixture, DateTruncEvaluateMonth6UTCValue2030) \ + (benchmark::State & state) { \ + benchmarkDateTruncEvaluateMonth6UTCValue2030(state); \ + } \ + BENCHMARK_F(Fixture, DateTruncEvaluateYear1NewYorkValue2020) \ + (benchmark::State & state) { \ + benchmarkDateTruncEvaluateYear1NewYorkValue2020(state); \ + } \ + BENCHMARK_F(Fixture, DateTruncEvaluateYear1UTCValue2020) \ + (benchmark::State & state) { \ + benchmarkDateTruncEvaluateYear1UTCValue2020(state); \ + } \ + BENCHMARK_F(Fixture, DateTruncEvaluateYear1NewYorkValue2100) \ + (benchmark::State & state) { \ + benchmarkDateTruncEvaluateYear1NewYorkValue2100(state); \ + } \ + \ + BENCHMARK_F(Fixture, GetFieldEvaluateExpression) \ + (benchmark::State & state) { \ + benchmarkGetFieldEvaluateExpression(state); \ + } \ + BENCHMARK_F(Fixture, GetFieldEvaluateShortSyntaxExpression) \ + (benchmark::State & state) { \ + benchmarkGetFieldEvaluateShortSyntaxExpression(state); \ + } \ + BENCHMARK_F(Fixture, GetFieldNestedExpression)(benchmark::State & state) { \ + benchmarkGetFieldNestedExpression(state); \ + } \ + BENCHMARK_F(Fixture, SetFieldEvaluateExpression) \ + (benchmark::State & state) { \ + benchmarkSetFieldEvaluateExpression(state); \ + } \ + BENCHMARK_F(Fixture, SetFieldWithRemoveExpression) \ + (benchmark::State & state) { \ + benchmarkSetFieldWithRemoveExpression(state); \ + } \ + BENCHMARK_F(Fixture, UnsetFieldEvaluateExpression) \ + (benchmark::State & state) { \ + benchmarkUnsetFieldEvaluateExpression(state); \ + } \ + \ + BENCHMARK_F(Fixture, SetIsSubset_allPresent)(benchmark::State & state) { \ + benchmarkSetIsSubset_allPresent(state); \ + } \ + BENCHMARK_F(Fixture, SetIsSubset_nonePresent)(benchmark::State & state) { \ + benchmarkSetIsSubset_nonePresent(state); \ + } \ + BENCHMARK_F(Fixture, SetIntersection)(benchmark::State & state) { \ + benchmarkSetIntersection(state); \ + } \ + BENCHMARK_F(Fixture, SetDifference)(benchmark::State & state) { \ + benchmarkSetDifference(state); \ + } \ + BENCHMARK_F(Fixture, SetEquals)(benchmark::State & state) { \ + benchmarkSetEquals(state); \ + } \ + BENCHMARK_F(Fixture, SetUnion)(benchmark::State & state) { \ + benchmarkSetUnion(state); \ + } + +} // namespace mongo diff --git a/src/mongo/db/query/SConscript b/src/mongo/db/query/SConscript index 890bf8e1615..548e73b0ba6 100644 --- a/src/mongo/db/query/SConscript +++ b/src/mongo/db/query/SConscript @@ -399,8 +399,8 @@ env.CppUnitTest( "sbe_and_sorted_test.cpp", "sbe_stage_builder_accumulator_test.cpp", "sbe_stage_builder_lookup_test.cpp", - "sbe_stage_builder_test_fixture.cpp", "sbe_stage_builder_test.cpp", + "sbe_stage_builder_test_fixture.cpp", "sbe_shard_filter_test.cpp", "shard_filterer_factory_mock.cpp", "view_response_formatter_test.cpp", @@ -417,7 +417,6 @@ env.CppUnitTest( "$BUILD_DIR/mongo/db/repl/replmocks", "$BUILD_DIR/mongo/db/repl/storage_interface_impl", "$BUILD_DIR/mongo/db/service_context_d_test_fixture", - "$BUILD_DIR/mongo/db/service_context_test_fixture", "$BUILD_DIR/mongo/dbtests/mocklib", "$BUILD_DIR/mongo/rpc/rpc", "$BUILD_DIR/mongo/util/clock_source_mock", @@ -433,3 +432,16 @@ env.CppUnitTest( "query_test_service_context", ], ) + +env.Benchmark( + target="sbe_expression_bm", + source=[ + "sbe_expression_bm.cpp", + ], + LIBDEPS=[ + "$BUILD_DIR/mongo/db/auth/authmocks", + "$BUILD_DIR/mongo/db/pipeline/expression_bm_fixture", + "$BUILD_DIR/mongo/db/query_exec", + "query_test_service_context", + ], +) diff --git a/src/mongo/db/query/sbe_expression_bm.cpp b/src/mongo/db/query/sbe_expression_bm.cpp new file mode 100644 index 00000000000..867cbb5ca3a --- /dev/null +++ b/src/mongo/db/query/sbe_expression_bm.cpp @@ -0,0 +1,147 @@ +/** + * Copyright (C) 2022-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * <http://www.mongodb.com/licensing/server-side-public-license>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the Server Side Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#include <benchmark/benchmark.h> + +#include "mongo/db/exec/sbe/util/debug_print.h" +#include "mongo/db/pipeline/expression_bm_fixture.h" +#include "mongo/db/pipeline/expression_context_for_test.h" +#include "mongo/db/query/query_test_service_context.h" +#include "mongo/db/query/sbe_stage_builder.h" +#include "mongo/db/query/sbe_stage_builder_expression.h" + +#include "mongo/logv2/log.h" + +#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kTest + +namespace mongo { +namespace { + +class SbeExpressionBenchmarkFixture : public ExpressionBenchmarkFixture { +public: + SbeExpressionBenchmarkFixture() : _planStageData(std::make_unique<sbe::RuntimeEnvironment>()) { + _inputSlotId = _planStageData.env->registerSlot( + "input"_sd, sbe::value::TypeTags::Nothing, 0, false, &_slotIdGenerator); + _timeZoneDB = std::make_unique<TimeZoneDatabase>(); + _planStageData.env->registerSlot( + "timeZoneDB"_sd, + sbe::value::TypeTags::timeZoneDB, + sbe::value::bitcastFrom<TimeZoneDatabase*>(_timeZoneDB.get()), + false, + &_slotIdGenerator); + } + + void benchmarkExpression(BSONObj expressionSpec, + benchmark::State& benchmarkState, + const std::vector<Document>& documents) override final { + std::vector<BSONObj> bsonDocuments = convertToBson(documents); + QueryTestServiceContext serviceContext; + + auto opContext = serviceContext.makeOperationContext(); + auto exprContext = make_intrusive<ExpressionContextForTest>(opContext.get(), _nss); + auto expression = Expression::parseExpression( + exprContext.get(), expressionSpec, exprContext->variablesParseState); + + if (!exprContext->sbeCompatible) { + benchmarkState.SkipWithError("expression is not supported by SBE"); + return; + } + + expression = expression->optimize(); + + stage_builder::StageBuilderState state{ + opContext.get(), + &_planStageData, + _variables, + &_slotIdGenerator, + &_frameIdGenerator, + &_spoolIdGenerator, + false /* needsMerge */, + false /* allowDiskUse */ + }; + + auto [evalExpr, evalStage] = + stage_builder::generateExpression(state, + expression.get(), + stage_builder::EvalStage{}, + boost::make_optional(_inputSlotId), + kEmptyPlanNodeId); + tassert(6979800, "Unexpected: EvalStage.stage is not null", evalStage.stageIsNull()); + + auto expr = evalExpr.extractExpr(); + auto compiledExpr = expr->compile(_planStageData.ctx); + sbe::vm::ByteCode _vm; + + LOGV2_DEBUG( + 6979802, + 1, + "running sbe expression benchmark on expression {expression}, sbe representation {sbe}", + "expression"_attr = expression->serialize(true).toString(), + "sbe"_attr = sbe::DebugPrinter{true}.print(expr->debugPrint())); + + for (auto keepRunning : benchmarkState) { + for (const auto& document : bsonDocuments) { + _planStageData.env->resetSlot( + _inputSlotId, + sbe::value::TypeTags::bsonObject, + sbe::value::bitcastFrom<const char*>(document.objdata()), + false); + benchmark::DoNotOptimize(_vm.run(compiledExpr.get())); + } + benchmark::ClobberMemory(); + } + } + +private: + std::vector<BSONObj> convertToBson(const std::vector<Document>& documents) { + std::vector<BSONObj> result; + result.reserve(documents.size()); + std::transform(documents.begin(), + documents.end(), + std::back_inserter(result), + [](const auto& doc) { return doc.toBson(); }); + return result; + } + + NamespaceString _nss{"test.bm"}; + + stage_builder::PlanStageData _planStageData; + Variables _variables; + sbe::value::SlotIdGenerator _slotIdGenerator; + sbe::value::FrameIdGenerator _frameIdGenerator; + sbe::value::SpoolIdGenerator _spoolIdGenerator; + + sbe::value::SlotId _inputSlotId; + std::unique_ptr<TimeZoneDatabase> _timeZoneDB; +}; + +BENCHMARK_EXPRESSIONS(SbeExpressionBenchmarkFixture) + +} // namespace +} // namespace mongo |