diff options
43 files changed, 305 insertions, 291 deletions
diff --git a/README.md b/README.md index a2b64f20fc3..3056f619982 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -<!-- Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<!-- Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> -[![#Vespa](https://vespa.ai/img/VespaLogoBlack.png)](https://vespa.ai) +[![#Vespa](https://vespa.ai/assets/vespa-logo-color.png)](https://vespa.ai) The big data serving engine - Store, search, rank and organize big data at user serving time. Vespa is an engine for low-latency computation over large data sets. diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/JvmOptionsTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/JvmOptionsTest.java index 294df42bd77..60af25a3087 100644 --- a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/JvmOptionsTest.java +++ b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/JvmOptionsTest.java @@ -78,11 +78,11 @@ public class JvmOptionsTest extends ContainerModelBuilderTestBase { } private static void verifyIgnoreJvmGCOptions(boolean isHosted) throws IOException, SAXException { - verifyIgnoreJvmGCOptionsIfJvmArgs("jvmargs", ContainerCluster.G1GC); - verifyIgnoreJvmGCOptionsIfJvmArgs( "jvm-options", "-XX:+UseG1GC"); + verifyIgnoreJvmGCOptionsIfJvmArgs("jvmargs", ContainerCluster.G1GC, isHosted); + verifyIgnoreJvmGCOptionsIfJvmArgs( "jvm-options", "-XX:+UseG1GC", isHosted); } - private static void verifyIgnoreJvmGCOptionsIfJvmArgs(String jvmOptionsName, String expectedGC) throws IOException, SAXException { + private static void verifyIgnoreJvmGCOptionsIfJvmArgs(String jvmOptionsName, String expectedGC, boolean isHosted) throws IOException, SAXException { String servicesXml = "<container version='1.0'>" + " <nodes jvm-gc-options='-XX:+UseG1GC' " + jvmOptionsName + "='-XX:+UseParNewGC'>" + @@ -95,6 +95,7 @@ public class JvmOptionsTest extends ContainerModelBuilderTestBase { VespaModel model = new VespaModel(new NullConfigModelRegistry(), new DeployState.Builder() .applicationPackage(applicationPackage) .deployLogger(logger) + .properties(new TestProperties().setHostedVespa(isHosted)) .build()); QrStartConfig.Builder qrStartBuilder = new QrStartConfig.Builder(); model.getConfig(qrStartBuilder, "container/container.0"); diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java index 979cd9060d9..081c6afa2b8 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java @@ -50,7 +50,7 @@ public class ControllerMaintenance extends AbstractComponent { maintainers.add(new ApplicationOwnershipConfirmer(controller, intervals.applicationOwnershipConfirmer, controller.serviceRegistry().ownershipIssues())); maintainers.add(new SystemUpgrader(controller, intervals.systemUpgrader)); maintainers.add(new JobRunner(controller, intervals.jobRunner)); - maintainers.add(new OsVersionStatusUpdater(controller, intervals.defaultInterval)); + maintainers.add(new OsVersionStatusUpdater(controller, intervals.osVersionStatusUpdater)); maintainers.add(new ContactInformationMaintainer(controller, intervals.contactInformationMaintainer)); maintainers.add(new NameServiceDispatcher(controller, intervals.nameServiceDispatcher)); maintainers.add(new CostReportMaintainer(controller, intervals.costReportMaintainer, controller.serviceRegistry().costReportConsumer())); @@ -98,6 +98,7 @@ public class ControllerMaintenance extends AbstractComponent { private final Duration applicationOwnershipConfirmer; private final Duration systemUpgrader; private final Duration jobRunner; + private final Duration osVersionStatusUpdater; private final Duration osUpgrader; private final Duration contactInformationMaintainer; private final Duration nameServiceDispatcher; @@ -120,13 +121,14 @@ public class ControllerMaintenance extends AbstractComponent { this.readyJobsTrigger = duration(1, MINUTES); this.deploymentMetricsMaintainer = duration(10, MINUTES); this.applicationOwnershipConfirmer = duration(12, HOURS); - this.systemUpgrader = duration(90, SECONDS); + this.systemUpgrader = duration(2, MINUTES); this.jobRunner = duration(90, SECONDS); + this.osVersionStatusUpdater = duration(2, MINUTES); this.osUpgrader = duration(1, MINUTES); this.contactInformationMaintainer = duration(12, HOURS); - this.nameServiceDispatcher = duration(30, SECONDS); + this.nameServiceDispatcher = duration(1, MINUTES); this.costReportMaintainer = duration(2, HOURS); - this.resourceMeterMaintainer = duration(1, MINUTES); + this.resourceMeterMaintainer = duration(3, MINUTES); this.cloudEventReporter = duration(30, MINUTES); this.resourceTagMaintainer = duration(30, MINUTES); this.systemRoutingPolicyMaintainer = duration(10, MINUTES); diff --git a/eval/src/tests/eval/fast_value/fast_value_test.cpp b/eval/src/tests/eval/fast_value/fast_value_test.cpp index 6cf43511977..9d29d8de660 100644 --- a/eval/src/tests/eval/fast_value/fast_value_test.cpp +++ b/eval/src/tests/eval/fast_value/fast_value_test.cpp @@ -142,9 +142,9 @@ TEST(FastValueBuilderTest, mixed_add_subspace_robustness) { } } -GenSpec G() { return GenSpec().cells_float(); } +GenSpec G() { return GenSpec(); } -std::vector<GenSpec> layouts = { +const std::vector<GenSpec> layouts = { G(), G().idx("x", 3), G().idx("x", 3).idx("y", 5), @@ -159,7 +159,9 @@ std::vector<GenSpec> layouts = { TEST(FastValueBuilderFactoryTest, fast_values_can_be_copied) { auto factory = FastValueBuilderFactory::get(); for (const auto &layout: layouts) { - for (TensorSpec expect : { layout.gen(), layout.cpy().cells_double().gen() }) { + for (TensorSpec expect : { layout.cpy().cells_float().gen(), + layout.cpy().cells_double().gen() }) + { std::unique_ptr<Value> value = value_from_spec(expect, factory); std::unique_ptr<Value> copy = factory.copy(*value); TensorSpec actual = spec_from_value(*copy); diff --git a/eval/src/tests/eval/simple_value/simple_value_test.cpp b/eval/src/tests/eval/simple_value/simple_value_test.cpp index 09884b0ed0d..c1301bf6b1a 100644 --- a/eval/src/tests/eval/simple_value/simple_value_test.cpp +++ b/eval/src/tests/eval/simple_value/simple_value_test.cpp @@ -23,9 +23,9 @@ using Handle = SharedStringRepo::Handle; vespalib::string as_str(string_id label) { return Handle::string_from_id(label); } -GenSpec G() { return GenSpec().cells_float(); } +GenSpec G() { return GenSpec(); } -std::vector<GenSpec> layouts = { +const std::vector<GenSpec> layouts = { G(), G().idx("x", 3), G().idx("x", 3).idx("y", 5), @@ -37,7 +37,7 @@ std::vector<GenSpec> layouts = { G().map("x", {"a","b","c"}).idx("y", 5).map("z", {"i","j","k","l"}) }; -std::vector<GenSpec> join_layouts = { +const std::vector<GenSpec> join_layouts = { G(), G(), G().idx("x", 5), G().idx("x", 5), G().idx("x", 5), G().idx("y", 5), @@ -67,7 +67,9 @@ TensorSpec simple_value_join(const TensorSpec &a, const TensorSpec &b, join_fun_ TEST(SimpleValueTest, simple_values_can_be_converted_from_and_to_tensor_spec) { for (const auto &layout: layouts) { - for (TensorSpec expect : { layout.gen(), layout.cpy().cells_double().gen() }) { + for (TensorSpec expect : { layout.cpy().cells_float().gen(), + layout.cpy().cells_double().gen() }) + { std::unique_ptr<Value> value = value_from_spec(expect, SimpleValueBuilderFactory::get()); TensorSpec actual = spec_from_value(*value); EXPECT_EQ(actual, expect); @@ -77,7 +79,9 @@ TEST(SimpleValueTest, simple_values_can_be_converted_from_and_to_tensor_spec) { TEST(SimpleValueTest, simple_values_can_be_copied) { for (const auto &layout: layouts) { - for (TensorSpec expect : { layout.gen(), layout.cpy().cells_double().gen() }) { + for (TensorSpec expect : { layout.cpy().cells_float().gen(), + layout.cpy().cells_double().gen() }) + { std::unique_ptr<Value> value = value_from_spec(expect, SimpleValueBuilderFactory::get()); std::unique_ptr<Value> copy = SimpleValueBuilderFactory::get().copy(*value); TensorSpec actual = spec_from_value(*copy); @@ -124,10 +128,14 @@ GenSpec::seq_t N_16ths = [] (size_t i) noexcept { return (i + 1.0) / 16.0; }; TEST(SimpleValueTest, new_generic_join_works_for_simple_values) { ASSERT_TRUE((join_layouts.size() % 2) == 0); for (size_t i = 0; i < join_layouts.size(); i += 2) { - const auto l = join_layouts[i].seq(N_16ths); - const auto r = join_layouts[i + 1].seq(N_16ths); - for (TensorSpec lhs : { l.gen(), l.cpy().cells_double().gen() }) { - for (TensorSpec rhs : { r.gen(), r.cpy().cells_double().gen() }) { + const auto l = join_layouts[i].cpy().seq(N_16ths); + const auto r = join_layouts[i + 1].cpy().seq(N_16ths); + for (TensorSpec lhs : { l.cpy().cells_float().gen(), + l.cpy().cells_double().gen() }) + { + for (TensorSpec rhs : { r.cpy().cells_float().gen(), + r.cpy().cells_double().gen() }) + { for (auto fun: {operation::Add::f, operation::Sub::f, operation::Mul::f, operation::Div::f}) { SCOPED_TRACE(fmt("\n===\nLHS: %s\nRHS: %s\n===\n", lhs.to_string().c_str(), rhs.to_string().c_str())); auto expect = ReferenceOperations::join(lhs, rhs, fun); diff --git a/eval/src/tests/eval/tensor_lambda/tensor_lambda_test.cpp b/eval/src/tests/eval/tensor_lambda/tensor_lambda_test.cpp index 18198a75f7d..dd21b663fa9 100644 --- a/eval/src/tests/eval/tensor_lambda/tensor_lambda_test.cpp +++ b/eval/src/tests/eval/tensor_lambda/tensor_lambda_test.cpp @@ -23,12 +23,10 @@ using namespace vespalib::eval::tensor_function; const ValueBuilderFactory &simple_factory = SimpleValueBuilderFactory::get(); const ValueBuilderFactory &prod_factory = FastValueBuilderFactory::get(); -TensorSpec spec(double v) { return TensorSpec("double").add({}, v); } - EvalFixture::ParamRepo make_params() { return EvalFixture::ParamRepo() - .add("a", spec(1)) - .add("b", spec(2)) + .add("a", GenSpec().seq_bias(1).gen()) + .add("b", GenSpec().seq_bias(2).gen()) .add("x3", GenSpec().idx("x", 3).gen()) .add("x3f", GenSpec().idx("x", 3).cells_float().gen()) .add("x3m", GenSpec().map("x", 3).gen()) diff --git a/eval/src/tests/eval/value_codec/value_codec_test.cpp b/eval/src/tests/eval/value_codec/value_codec_test.cpp index acce0f5667f..110b58c27de 100644 --- a/eval/src/tests/eval/value_codec/value_codec_test.cpp +++ b/eval/src/tests/eval/value_codec/value_codec_test.cpp @@ -15,9 +15,9 @@ using namespace vespalib::eval::test; const ValueBuilderFactory &factory = SimpleValueBuilderFactory::get(); -GenSpec G() { return GenSpec().cells_float(); } +GenSpec G() { return GenSpec(); } -std::vector<GenSpec> layouts = { +const std::vector<GenSpec> layouts = { G(), G().idx("x", 3), G().idx("x", 3).idx("y", 5), @@ -32,7 +32,9 @@ std::vector<GenSpec> layouts = { TEST(ValueCodecTest, simple_values_can_be_converted_from_and_to_tensor_spec) { for (const auto &layout: layouts) { - for (TensorSpec expect : { layout.gen(), layout.cpy().cells_double().gen() }) { + for (TensorSpec expect : { layout.cpy().cells_float().gen(), + layout.cpy().cells_double().gen() }) + { std::unique_ptr<Value> value = value_from_spec(expect, factory); TensorSpec actual = spec_from_value(*value); EXPECT_EQ(actual, expect); diff --git a/eval/src/tests/instruction/generic_concat/generic_concat_test.cpp b/eval/src/tests/instruction/generic_concat/generic_concat_test.cpp index 59c4c5bf0c8..17e012b8e33 100644 --- a/eval/src/tests/instruction/generic_concat/generic_concat_test.cpp +++ b/eval/src/tests/instruction/generic_concat/generic_concat_test.cpp @@ -18,11 +18,11 @@ using namespace vespalib::eval::test; using vespalib::make_string_short::fmt; -GenSpec G() { return GenSpec().cells_float(); } +GenSpec G() { return GenSpec(); } GenSpec::seq_t N_16ths = [] (size_t i) noexcept { return (i + 1.0) / 16.0; }; -std::vector<GenSpec> concat_layouts = { +const std::vector<GenSpec> concat_layouts = { G(), G(), G(), G().idx("y", 5), G().idx("y", 5), G(), @@ -76,10 +76,14 @@ TensorSpec perform_generic_concat(const TensorSpec &a, const TensorSpec &b, void test_generic_concat_with(const ValueBuilderFactory &factory) { ASSERT_TRUE((concat_layouts.size() % 2) == 0); for (size_t i = 0; i < concat_layouts.size(); i += 2) { - const auto &l = concat_layouts[i]; - const auto &r = concat_layouts[i+1].seq(N_16ths); - for (TensorSpec lhs : { l.gen(), l.cpy().cells_double().gen() }) { - for (TensorSpec rhs : { r.gen(), r.cpy().cells_double().gen() }) { + const auto l = concat_layouts[i]; + const auto r = concat_layouts[i+1].cpy().seq(N_16ths); + for (TensorSpec lhs : { l.cpy().cells_float().gen(), + l.cpy().cells_double().gen() }) + { + for (TensorSpec rhs : { r.cpy().cells_float().gen(), + r.cpy().cells_double().gen() }) + { SCOPED_TRACE(fmt("\n===\nin LHS: %s\nin RHS: %s\n===\n", lhs.to_string().c_str(), rhs.to_string().c_str())); auto actual = perform_generic_concat(lhs, rhs, "y", factory); auto expect = ReferenceOperations::concat(lhs, rhs, "y"); diff --git a/eval/src/tests/instruction/generic_create/generic_create_test.cpp b/eval/src/tests/instruction/generic_create/generic_create_test.cpp index 2dce4509571..fcf4618d592 100644 --- a/eval/src/tests/instruction/generic_create/generic_create_test.cpp +++ b/eval/src/tests/instruction/generic_create/generic_create_test.cpp @@ -19,9 +19,9 @@ using namespace vespalib::eval::test; using vespalib::make_string_short::fmt; -GenSpec G() { return GenSpec().cells_float(); } +GenSpec G() { return GenSpec(); } -std::vector<GenSpec> create_layouts = { +const std::vector<GenSpec> create_layouts = { G().idx("x", 3), G().idx("x", 3).idx("y", 5), G().idx("x", 3).idx("y", 5).idx("z", 7), @@ -91,7 +91,9 @@ TensorSpec perform_generic_create(const TensorSpec &a, const ValueBuilderFactory void test_generic_create_with(const ValueBuilderFactory &factory) { for (const auto &layout : create_layouts) { - for (TensorSpec full : { layout.gen(), layout.cpy().cells_double().gen() }) { + for (TensorSpec full : { layout.cpy().cells_float().gen(), + layout.cpy().cells_double().gen() }) + { auto actual = perform_generic_create(full, factory); auto expect = reference_create(full).normalize(); EXPECT_EQ(actual, expect); diff --git a/eval/src/tests/instruction/generic_join/generic_join_test.cpp b/eval/src/tests/instruction/generic_join/generic_join_test.cpp index 55dc4c25389..f724cdf1024 100644 --- a/eval/src/tests/instruction/generic_join/generic_join_test.cpp +++ b/eval/src/tests/instruction/generic_join/generic_join_test.cpp @@ -19,9 +19,9 @@ using vespalib::make_string_short::fmt; GenSpec::seq_t N_16ths = [] (size_t i) noexcept { return (i + 1.0) / 16.0; }; -GenSpec G() { return GenSpec().cells_float().seq(N_16ths); } +GenSpec G() { return GenSpec().seq(N_16ths); } -std::vector<GenSpec> join_layouts = { +const std::vector<GenSpec> join_layouts = { G(), G(), G().idx("x", 5), G().idx("x", 5), G().idx("x", 5), G().idx("y", 5), @@ -107,8 +107,12 @@ TEST(GenericJoinTest, generic_join_works_for_simple_and_fast_values) { for (size_t i = 0; i < join_layouts.size(); i += 2) { const auto &l = join_layouts[i]; const auto &r = join_layouts[i+1]; - for (TensorSpec lhs : { l.gen(), l.cpy().cells_double().gen() }) { - for (TensorSpec rhs : { r.gen(), r.cpy().cells_double().gen() }) { + for (TensorSpec lhs : { l.cpy().cells_float().gen(), + l.cpy().cells_double().gen() }) + { + for (TensorSpec rhs : { r.cpy().cells_float().gen(), + r.cpy().cells_double().gen() }) + { for (auto fun: {operation::Add::f, operation::Sub::f, operation::Mul::f, operation::Div::f}) { SCOPED_TRACE(fmt("\n===\nLHS: %s\nRHS: %s\n===\n", lhs.to_string().c_str(), rhs.to_string().c_str())); auto expect = ReferenceOperations::join(lhs, rhs, fun); diff --git a/eval/src/tests/instruction/generic_map/generic_map_test.cpp b/eval/src/tests/instruction/generic_map/generic_map_test.cpp index aaa8990d794..8e39fa68072 100644 --- a/eval/src/tests/instruction/generic_map/generic_map_test.cpp +++ b/eval/src/tests/instruction/generic_map/generic_map_test.cpp @@ -19,9 +19,9 @@ using vespalib::make_string_short::fmt; GenSpec::seq_t N_16ths = [] (size_t i) noexcept { return (i + 1.0) / 16.0; }; -GenSpec G() { return GenSpec().cells_float().seq(N_16ths); } +GenSpec G() { return GenSpec().seq(N_16ths); } -std::vector<GenSpec> map_layouts = { +const std::vector<GenSpec> map_layouts = { G(), G().idx("x", 3), G().idx("x", 3).idx("y", 5), @@ -43,7 +43,9 @@ TensorSpec perform_generic_map(const TensorSpec &a, map_fun_t func, const ValueB void test_generic_map_with(const ValueBuilderFactory &factory) { for (const auto &layout : map_layouts) { - for (TensorSpec lhs : { layout.gen(), layout.cpy().cells_double().gen() }) { + for (TensorSpec lhs : { layout.cpy().cells_float().gen(), + layout.cpy().cells_double().gen() }) + { for (auto func : {operation::Floor::f, operation::Fabs::f, operation::Square::f, operation::Inv::f}) { SCOPED_TRACE(fmt("\n===\nLHS: %s\n===\n", lhs.to_string().c_str())); auto expect = ReferenceOperations::map(lhs, func); diff --git a/eval/src/tests/instruction/generic_merge/generic_merge_test.cpp b/eval/src/tests/instruction/generic_merge/generic_merge_test.cpp index f2ddd9b74d8..d5f7bc071f6 100644 --- a/eval/src/tests/instruction/generic_merge/generic_merge_test.cpp +++ b/eval/src/tests/instruction/generic_merge/generic_merge_test.cpp @@ -18,11 +18,11 @@ using namespace vespalib::eval::test; using vespalib::make_string_short::fmt; -GenSpec G() { return GenSpec().cells_float(); } +GenSpec G() { return GenSpec(); } GenSpec::seq_t N_16ths = [] (size_t i) noexcept { return (i + 1.0) / 16.0; }; -std::vector<GenSpec> merge_layouts = { +const std::vector<GenSpec> merge_layouts = { G(), G(), G().idx("x", 5), G().idx("x", 5), G().idx("x", 3).idx("y", 5), G().idx("x", 3).idx("y", 5), @@ -48,10 +48,14 @@ TensorSpec perform_generic_merge(const TensorSpec &a, const TensorSpec &b, join_ void test_generic_merge_with(const ValueBuilderFactory &factory) { ASSERT_TRUE((merge_layouts.size() % 2) == 0); for (size_t i = 0; i < merge_layouts.size(); i += 2) { - const auto &l = merge_layouts[i]; - const auto &r = merge_layouts[i+1].seq(N_16ths); - for (TensorSpec lhs : { l.gen(), l.cpy().cells_double().gen() }) { - for (TensorSpec rhs : { r.gen(), r.cpy().cells_double().gen() }) { + const auto l = merge_layouts[i]; + const auto r = merge_layouts[i+1].cpy().seq(N_16ths); + for (TensorSpec lhs : { l.cpy().cells_float().gen(), + l.cpy().cells_double().gen() }) + { + for (TensorSpec rhs : { r.cpy().cells_float().gen(), + r.cpy().cells_double().gen() }) + { SCOPED_TRACE(fmt("\n===\nLHS: %s\nRHS: %s\n===\n", lhs.to_string().c_str(), rhs.to_string().c_str())); for (auto fun: {operation::Add::f, operation::Mul::f, operation::Sub::f, operation::Max::f}) { auto expect = ReferenceOperations::merge(lhs, rhs, fun); diff --git a/eval/src/tests/instruction/generic_peek/generic_peek_test.cpp b/eval/src/tests/instruction/generic_peek/generic_peek_test.cpp index 092a91711ba..c80e8a1296b 100644 --- a/eval/src/tests/instruction/generic_peek/generic_peek_test.cpp +++ b/eval/src/tests/instruction/generic_peek/generic_peek_test.cpp @@ -22,9 +22,9 @@ using namespace vespalib::eval::test; using vespalib::make_string_short::fmt; -GenSpec G() { return GenSpec().cells_float(); } +GenSpec G() { return GenSpec(); } -std::vector<GenSpec> peek_layouts = { +const std::vector<GenSpec> peek_layouts = { G().idx("x", 4), G().idx("x", 4).idx("y", 5), G().idx("x", 4).idx("y", 5).idx("z", 3), @@ -194,7 +194,9 @@ void fill_dims_and_check(const TensorSpec &input, void test_generic_peek_with(const ValueBuilderFactory &factory) { for (const auto &layout : peek_layouts) { - for (TensorSpec input : { layout.gen(), layout.cpy().cells_double().gen() }) { + for (TensorSpec input : { layout.cpy().cells_float().gen(), + layout.cpy().cells_double().gen() }) + { ValueType input_type = ValueType::from_spec(input.type()); const auto &dims = input_type.dimensions(); PeekSpec spec; diff --git a/eval/src/tests/instruction/generic_reduce/generic_reduce_test.cpp b/eval/src/tests/instruction/generic_reduce/generic_reduce_test.cpp index 77317b0ee27..2c5baf234c4 100644 --- a/eval/src/tests/instruction/generic_reduce/generic_reduce_test.cpp +++ b/eval/src/tests/instruction/generic_reduce/generic_reduce_test.cpp @@ -20,9 +20,9 @@ using vespalib::make_string_short::fmt; GenSpec::seq_t N_16ths = [] (size_t i) noexcept { return (i + 1.0) / 16.0; }; -GenSpec G() { return GenSpec().cells_float().seq(N_16ths); } +GenSpec G() { return GenSpec().seq(N_16ths); } -std::vector<GenSpec> layouts = { +const std::vector<GenSpec> layouts = { G(), G().idx("x", 3), G().idx("x", 3).idx("y", 5), @@ -70,7 +70,9 @@ TEST(GenericReduceTest, sparse_reduce_plan_can_be_created) { void test_generic_reduce_with(const ValueBuilderFactory &factory) { for (const auto &layout: layouts) { - for (TensorSpec input : { layout.gen(), layout.cpy().cells_double().gen() }) { + for (TensorSpec input : { layout.cpy().cells_float().gen(), + layout.cpy().cells_double().gen() }) + { SCOPED_TRACE(fmt("tensor type: %s, num_cells: %zu", input.type().c_str(), input.cells().size())); for (Aggr aggr: {Aggr::SUM, Aggr::AVG, Aggr::MIN, Aggr::MAX}) { SCOPED_TRACE(fmt("aggregator: %s", AggrNames::name_of(aggr)->c_str())); diff --git a/eval/src/tests/instruction/generic_rename/generic_rename_test.cpp b/eval/src/tests/instruction/generic_rename/generic_rename_test.cpp index 430e417e288..f0c2241202e 100644 --- a/eval/src/tests/instruction/generic_rename/generic_rename_test.cpp +++ b/eval/src/tests/instruction/generic_rename/generic_rename_test.cpp @@ -17,9 +17,9 @@ using namespace vespalib::eval::test; using vespalib::make_string_short::fmt; -GenSpec G() { return GenSpec().cells_float(); } +GenSpec G() { return GenSpec(); } -std::vector<GenSpec> rename_layouts = { +const std::vector<GenSpec> rename_layouts = { G().idx("x", 3), G().idx("x", 3).idx("y", 5), G().idx("x", 3).idx("y", 5).idx("z", 7), @@ -110,7 +110,9 @@ TensorSpec perform_generic_rename(const TensorSpec &a, void test_generic_rename_with(const ValueBuilderFactory &factory) { for (const auto &layout : rename_layouts) { - for (TensorSpec lhs : { layout.gen(), layout.cpy().cells_double().gen() }) { + for (TensorSpec lhs : { layout.cpy().cells_float().gen(), + layout.cpy().cells_double().gen() }) + { ValueType lhs_type = ValueType::from_spec(lhs.type()); for (const auto & from_to : rename_from_to) { ValueType renamed_type = lhs_type.rename(from_to.from, from_to.to); diff --git a/eval/src/tests/instruction/join_with_number/join_with_number_function_test.cpp b/eval/src/tests/instruction/join_with_number/join_with_number_function_test.cpp index 2d943aa569e..e6a256a493b 100644 --- a/eval/src/tests/instruction/join_with_number/join_with_number_function_test.cpp +++ b/eval/src/tests/instruction/join_with_number/join_with_number_function_test.cpp @@ -33,12 +33,10 @@ std::ostream &operator<<(std::ostream &os, Primary primary) const ValueBuilderFactory &prod_factory = FastValueBuilderFactory::get(); -TensorSpec spec(double v) { return TensorSpec("double").add({}, v); } - EvalFixture::ParamRepo make_params() { auto repo = EvalFixture::ParamRepo() - .add("a", spec(1.5)) - .add("number", spec(2.5)) + .add("a", GenSpec().seq_bias(1.5).gen()) + .add("number", GenSpec().seq_bias(2.5).gen()) .add("dense", GenSpec().idx("y", 5).gen()) .add_variants("x3y5", GenSpec().idx("x", 3).idx("y", 5)) .add_variants("mixed", GenSpec().map("x", {"a"}).idx("y", 5).map("z", {"d","e"})) diff --git a/eval/src/tests/instruction/mixed_map_function/mixed_map_function_test.cpp b/eval/src/tests/instruction/mixed_map_function/mixed_map_function_test.cpp index 45e885fac33..3a7d1368f03 100644 --- a/eval/src/tests/instruction/mixed_map_function/mixed_map_function_test.cpp +++ b/eval/src/tests/instruction/mixed_map_function/mixed_map_function_test.cpp @@ -13,19 +13,13 @@ using namespace vespalib::eval::tensor_function; const ValueBuilderFactory &prod_factory = FastValueBuilderFactory::get(); -TensorSpec spec(double v) { return TensorSpec("double").add({}, v); } -TensorSpec sparse_spec = GenSpec().map("x", {"a"}).gen(); -TensorSpec mixed_spec = GenSpec().map("x", {"a"}).idx("y", 5).gen(); - EvalFixture::ParamRepo make_params() { return EvalFixture::ParamRepo() - .add("a", spec(1.5)) - .add("b", spec(2.5)) - .add("sparse", sparse_spec) - .add("mixed", mixed_spec) - .add_mutable("@sparse", sparse_spec) - .add_mutable("@mixed", mixed_spec) - .add_matrix("x", 5, "y", 3); + .add("a", GenSpec().seq_bias(1.5).gen()) + .add("b", GenSpec().seq_bias(2.5).gen()) + .add_variants("sparse", GenSpec().map("x", {"a"})) + .add_variants("mixed", GenSpec().map("x", {"a"}).idx("y", 5)) + .add_variants("x5y3", GenSpec().idx("x", 5).idx("y", 3)); } EvalFixture::ParamRepo param_repo = make_params(); @@ -57,12 +51,12 @@ void verify_not_optimized(const vespalib::string &expr) { TEST(MapTest, dense_map_is_optimized) { verify_optimized("map(x5y3,f(x)(x+10))", false); - verify_optimized("map(x5y3f,f(x)(x+10))", false); + verify_optimized("map(x5y3_f,f(x)(x+10))", false); } TEST(MapTest, simple_dense_map_can_be_inplace) { verify_optimized("map(@x5y3,f(x)(x+10))", true); - verify_optimized("map(@x5y3f,f(x)(x+10))", true); + verify_optimized("map(@x5y3_f,f(x)(x+10))", true); } TEST(MapTest, scalar_map_is_not_optimized) { diff --git a/eval/src/tests/instruction/mixed_simple_join_function/mixed_simple_join_function_test.cpp b/eval/src/tests/instruction/mixed_simple_join_function/mixed_simple_join_function_test.cpp index 02e13fcbef3..105ae22e06e 100644 --- a/eval/src/tests/instruction/mixed_simple_join_function/mixed_simple_join_function_test.cpp +++ b/eval/src/tests/instruction/mixed_simple_join_function/mixed_simple_join_function_test.cpp @@ -43,12 +43,10 @@ std::ostream &operator<<(std::ostream &os, Overlap overlap) const ValueBuilderFactory &prod_factory = FastValueBuilderFactory::get(); -TensorSpec spec(double v) { return TensorSpec("double").add({}, v); } - EvalFixture::ParamRepo make_params() { return EvalFixture::ParamRepo() - .add("a", spec(1.5)) - .add("b", spec(2.5)) + .add("a", GenSpec().seq_bias(1.5).gen()) + .add("b", GenSpec().seq_bias(2.5).gen()) .add("sparse", GenSpec().map("x", {"a", "b", "c"}).gen()) .add("mixed", GenSpec().map("x", {"a", "b", "c"}).idx("y", 5).idx("z", 3).gen()) .add("empty_mixed", GenSpec().map("x", {}).idx("y", 5).idx("z", 3).gen()) diff --git a/eval/src/tests/instruction/pow_as_map_optimizer/pow_as_map_optimizer_test.cpp b/eval/src/tests/instruction/pow_as_map_optimizer/pow_as_map_optimizer_test.cpp index cceb18bfea6..fe32a59bb78 100644 --- a/eval/src/tests/instruction/pow_as_map_optimizer/pow_as_map_optimizer_test.cpp +++ b/eval/src/tests/instruction/pow_as_map_optimizer/pow_as_map_optimizer_test.cpp @@ -14,12 +14,10 @@ using namespace vespalib::eval; const ValueBuilderFactory &prod_factory = FastValueBuilderFactory::get(); -TensorSpec spec(double v) { return TensorSpec("double").add({}, v); } - EvalFixture::ParamRepo make_params() { return EvalFixture::ParamRepo() - .add("a", spec(1.5)) - .add("b", spec(2.5)) + .add("a", GenSpec().seq_bias(1.5).gen()) + .add("b", GenSpec().seq_bias(2.5).gen()) .add("sparse", GenSpec().map("x", {"a","b"}).gen()) .add("mixed", GenSpec().map("x", {"a"}).idx("y", 5).gen()) .add_variants("x5y3", GenSpec().idx("x", 5).idx("y", 3)); diff --git a/eval/src/tests/instruction/sparse_dot_product_function/sparse_dot_product_function_test.cpp b/eval/src/tests/instruction/sparse_dot_product_function/sparse_dot_product_function_test.cpp index 65eab2778aa..ac333f5224a 100644 --- a/eval/src/tests/instruction/sparse_dot_product_function/sparse_dot_product_function_test.cpp +++ b/eval/src/tests/instruction/sparse_dot_product_function/sparse_dot_product_function_test.cpp @@ -17,10 +17,8 @@ const ValueBuilderFactory &test_factory = SimpleValueBuilderFactory::get(); EvalFixture::ParamRepo make_params() { return EvalFixture::ParamRepo() - .add("v1_x", GenSpec().map("x", 32, 1).seq_bias(3.0).gen()) - .add("v1_x_f", GenSpec().map("x", 32, 1).seq_bias(3.0).cells_float().gen()) - .add("v2_x", GenSpec().map("x", 16, 2).seq_bias(7.0).gen()) - .add("v2_x_f", GenSpec().map("x", 16, 2).seq_bias(7.0).cells_float().gen()) + .add_variants("v1_x", GenSpec().map("x", 32, 1).seq_bias(3.0)) + .add_variants("v2_x", GenSpec().map("x", 16, 2).seq_bias(7.0)) .add("v3_y", GenSpec().map("y", 10, 1).gen()) .add("v4_xd", GenSpec().idx("x", 10).gen()) .add("m1_xy", GenSpec().map("x", 32, 1).map("y", 16, 2).seq_bias(3.0).gen()) @@ -53,8 +51,6 @@ TEST(SparseDotProduct, expression_can_be_optimized) { assert_optimized("reduce(v1_x*v2_x,sum,x)"); assert_optimized("reduce(v2_x*v1_x,sum)"); - assert_optimized("reduce(v1_x*v2_x_f,sum)"); - assert_optimized("reduce(v1_x_f*v2_x,sum)"); assert_optimized("reduce(v1_x_f*v2_x_f,sum)"); } @@ -80,6 +76,12 @@ TEST(SparseDotProduct, similar_expressions_are_not_optimized) assert_not_optimized("reduce(m3_xym*m3_xym,sum)"); } +TEST(SparseDotProduct, mixed_cell_types_are_not_optimized) +{ + assert_not_optimized("reduce(v1_x*v2_x_f,sum)"); + assert_not_optimized("reduce(v1_x_f*v2_x,sum)"); +} + //----------------------------------------------------------------------------- GTEST_MAIN_RUN_ALL_TESTS() diff --git a/eval/src/tests/instruction/sum_max_dot_product_function/sum_max_dot_product_function_test.cpp b/eval/src/tests/instruction/sum_max_dot_product_function/sum_max_dot_product_function_test.cpp index 616649e914b..1013c98b424 100644 --- a/eval/src/tests/instruction/sum_max_dot_product_function/sum_max_dot_product_function_test.cpp +++ b/eval/src/tests/instruction/sum_max_dot_product_function/sum_max_dot_product_function_test.cpp @@ -52,16 +52,16 @@ GenSpec DocGen(size_t y_size, size_t z_size) { return GenSpec().cells_float().ma GenSpec Que() { return QueGen(3, 5); } GenSpec Doc() { return DocGen(6, 5); } -GenSpec QueX0() { return QueGen(0, 5); } -GenSpec DocX0() { return DocGen(0, 5); } +GenSpec QueEmptyX() { return QueGen(0, 5); } +GenSpec DocEmptyX() { return DocGen(0, 5); } -GenSpec QueZ1() { return QueGen(3, 1); } -GenSpec DocZ1() { return DocGen(6, 1); } +GenSpec QueTrivialZ() { return QueGen(3, 1); } +GenSpec DocTrivialZ() { return DocGen(6, 1); } auto query = Que().gen(); auto document = Doc().gen(); -auto empty_query = QueX0().gen(); -auto empty_document = DocX0().gen(); +auto empty_query = QueEmptyX().gen(); +auto empty_document = DocEmptyX().gen(); TEST(SumMaxDotProduct, expressions_can_be_optimized) { @@ -81,8 +81,8 @@ TEST(SumMaxDotProduct, double_cells_are_not_optimized) { } TEST(SumMaxDotProduct, trivial_dot_product_is_not_optimized) { - auto trivial_query = QueZ1().gen(); - auto trivial_document = DocZ1().gen(); + auto trivial_query = QueTrivialZ().gen(); + auto trivial_document = DocTrivialZ().gen(); assert_not_optimized(trivial_query, trivial_document); } diff --git a/eval/src/tests/streamed/value/streamed_value_test.cpp b/eval/src/tests/streamed/value/streamed_value_test.cpp index d1b0e0a8d56..7aaa8cdebbc 100644 --- a/eval/src/tests/streamed/value/streamed_value_test.cpp +++ b/eval/src/tests/streamed/value/streamed_value_test.cpp @@ -23,9 +23,9 @@ using Handle = SharedStringRepo::Handle; vespalib::string as_str(string_id label) { return Handle::string_from_id(label); } -GenSpec G() { return GenSpec().cells_float(); } +GenSpec G() { return GenSpec(); } -std::vector<GenSpec> layouts = { +const std::vector<GenSpec> layouts = { G(), G().idx("x", 3), G().idx("x", 3).idx("y", 5), @@ -37,7 +37,7 @@ std::vector<GenSpec> layouts = { G().map("x", {"a","b","c"}).idx("y", 5).map("z", {"i","j","k","l"}) }; -std::vector<GenSpec> join_layouts = { +const std::vector<GenSpec> join_layouts = { G(), G(), G().idx("x", 5), G().idx("x", 5), G().idx("x", 5), G().idx("y", 5), @@ -67,7 +67,9 @@ TensorSpec streamed_value_join(const TensorSpec &a, const TensorSpec &b, join_fu TEST(StreamedValueTest, streamed_values_can_be_converted_from_and_to_tensor_spec) { for (const auto &layout: layouts) { - for (TensorSpec expect : { layout.gen(), layout.cpy().cells_double().gen() }) { + for (TensorSpec expect : { layout.cpy().cells_float().gen(), + layout.cpy().cells_double().gen() }) + { std::unique_ptr<Value> value = value_from_spec(expect, StreamedValueBuilderFactory::get()); TensorSpec actual = spec_from_value(*value); EXPECT_EQ(actual, expect); @@ -77,7 +79,9 @@ TEST(StreamedValueTest, streamed_values_can_be_converted_from_and_to_tensor_spec TEST(StreamedValueTest, streamed_values_can_be_copied) { for (const auto &layout: layouts) { - for (TensorSpec expect : { layout.gen(), layout.cpy().cells_double().gen() }) { + for (TensorSpec expect : { layout.cpy().cells_float().gen(), + layout.cpy().cells_double().gen() }) + { std::unique_ptr<Value> value = value_from_spec(expect, StreamedValueBuilderFactory::get()); std::unique_ptr<Value> copy = StreamedValueBuilderFactory::get().copy(*value); TensorSpec actual = spec_from_value(*copy); @@ -124,10 +128,14 @@ GenSpec::seq_t N_16ths = [] (size_t i) noexcept { return (i + 1.0) / 16.0; }; TEST(StreamedValueTest, new_generic_join_works_for_streamed_values) { ASSERT_TRUE((join_layouts.size() % 2) == 0); for (size_t i = 0; i < join_layouts.size(); i += 2) { - const auto l = join_layouts[i].seq(N_16ths); - const auto r = join_layouts[i + 1].seq(N_16ths); - for (TensorSpec lhs : { l.gen(), l.cpy().cells_double().gen() }) { - for (TensorSpec rhs : { r.gen(), r.cpy().cells_double().gen() }) { + const auto l = join_layouts[i].cpy().seq(N_16ths); + const auto r = join_layouts[i + 1].cpy().seq(N_16ths); + for (TensorSpec lhs : { l.cpy().cells_float().gen(), + l.cpy().cells_double().gen() }) + { + for (TensorSpec rhs : { r.cpy().cells_float().gen(), + r.cpy().cells_double().gen() }) + { for (auto fun: {operation::Add::f, operation::Sub::f, operation::Mul::f, operation::Max::f}) { SCOPED_TRACE(fmt("\n===\nLHS: %s\nRHS: %s\n===\n", lhs.to_string().c_str(), rhs.to_string().c_str())); auto expect = ReferenceOperations::join(lhs, rhs, fun); diff --git a/eval/src/vespa/eval/eval/fast_addr_map.h b/eval/src/vespa/eval/eval/fast_addr_map.h index d8f68d2a37c..7df6bd7fafc 100644 --- a/eval/src/vespa/eval/eval/fast_addr_map.h +++ b/eval/src/vespa/eval/eval/fast_addr_map.h @@ -20,9 +20,13 @@ namespace vespalib::eval { class FastAddrMap { public: - // label hasing functions - static constexpr uint32_t hash_label(string_id label) { return label.hash(); } - static constexpr uint32_t hash_label(const string_id *label) { return label->hash(); } + // label extracting functions + static constexpr string_id self(string_id label) { return label; } + static constexpr string_id self(const string_id *label) { return *label; } + + // label hashing functions + static constexpr uint32_t hash_label(string_id label) { return label.value(); } + static constexpr uint32_t hash_label(const string_id *label) { return label->value(); } static constexpr uint32_t combine_label_hash(uint32_t full_hash, uint32_t next_hash) { return ((full_hash * 31) + next_hash); } @@ -71,27 +75,27 @@ public: template <typename T> constexpr uint32_t operator()(const AltKey<T> &key) const { return key.hash; } constexpr uint32_t operator()(const Entry &entry) const { return entry.hash; } + constexpr uint32_t operator()(string_id label) const { return label.value(); } }; // equality functor for sparse hash set struct Equal { const LabelView &label_view; Equal(const LabelView &label_view_in) : label_view(label_view_in) {} - static constexpr bool eq_labels(string_id a, string_id b) { return (a == b); } - static constexpr bool eq_labels(string_id a, const string_id *b) { return (a == *b); } template <typename T> bool operator()(const Entry &a, const AltKey<T> &b) const { - if ((a.hash != b.hash) || (b.key.size() != label_view.addr_size)) { + if (a.hash != b.hash) { return false; } auto a_key = label_view.get_addr(a.tag.idx); for (size_t i = 0; i < a_key.size(); ++i) { - if (!eq_labels(a_key[i], b.key[i])) { + if (a_key[i] != self(b.key[i])) { return false; } } return true; } + bool operator()(const Entry &a, string_id b) const { return (a.hash == b.value()); } }; using HashType = hashtable<Entry, Entry, Hash, Equal, Identity, hashtable_base::and_modulator>; @@ -101,8 +105,8 @@ private: HashType _map; public: - FastAddrMap(size_t num_mapped_dims, const std::vector<string_id> &labels, size_t expected_subspaces) - : _labels(num_mapped_dims, labels), + FastAddrMap(size_t num_mapped_dims, const std::vector<string_id> &labels_in, size_t expected_subspaces) + : _labels(num_mapped_dims, labels_in), _map(expected_subspaces * 2, Hash(), Equal(_labels)) {} ~FastAddrMap(); FastAddrMap(const FastAddrMap &) = delete; @@ -113,15 +117,24 @@ public: ConstArrayRef<string_id> get_addr(size_t idx) const { return _labels.get_addr(idx); } size_t size() const { return _map.size(); } constexpr size_t addr_size() const { return _labels.addr_size; } + const std::vector<string_id> &labels() const { return _labels.labels; } template <typename T> size_t lookup(ConstArrayRef<T> addr, uint32_t hash) const { + // assert(addr_size() == addr.size()); AltKey<T> key{addr, hash}; auto pos = _map.find(key); return (pos == _map.end()) ? npos() : pos->tag.idx; } + size_t lookup_singledim(string_id addr) const { + // assert(addr_size() == 1); + auto pos = _map.find(addr); + return (pos == _map.end()) ? npos() : pos->tag.idx; + } template <typename T> size_t lookup(ConstArrayRef<T> addr) const { - return lookup(addr, hash_labels(addr)); + return (addr.size() == 1) + ? lookup_singledim(self(addr[0])) + : lookup(addr, hash_labels(addr)); } void add_mapping(uint32_t hash) { uint32_t idx = _map.size(); diff --git a/eval/src/vespa/eval/eval/fast_value.hpp b/eval/src/vespa/eval/eval/fast_value.hpp index 5657c93f53f..88319df7590 100644 --- a/eval/src/vespa/eval/eval/fast_value.hpp +++ b/eval/src/vespa/eval/eval/fast_value.hpp @@ -6,6 +6,7 @@ #include <vespa/eval/instruction/generic_join.h> #include <vespa/vespalib/stllike/hashtable.hpp> #include <vespa/vespalib/util/shared_string_repo.h> +#include <typeindex> namespace vespalib::eval { @@ -135,9 +136,9 @@ struct FastIterateView : public Value::Index::View { //----------------------------------------------------------------------------- using JoinAddrSource = instruction::SparseJoinPlan::Source; + // This is the class instructions will look for when optimizing sparse // operations by calling inline functions directly. - struct FastValueIndex final : Value::Index { FastAddrMap map; FastValueIndex(size_t num_mapped_dims_in, const std::vector<string_id> &labels, size_t expected_subspaces_in) @@ -164,6 +165,18 @@ struct FastValueIndex final : Value::Index { std::unique_ptr<View> create_view(const std::vector<size_t> &dims) const override; }; +inline bool is_fast(const Value::Index &index) { + return (std::type_index(typeid(index)) == std::type_index(typeid(FastValueIndex))); +} + +inline bool are_fast(const Value::Index &a, const Value::Index &b) { + return (is_fast(a) && is_fast(b)); +} + +constexpr const FastValueIndex &as_fast(const Value::Index &index) { + return static_cast<const FastValueIndex &>(index); +} + //----------------------------------------------------------------------------- template <typename T> diff --git a/eval/src/vespa/eval/instruction/sparse_dot_product_function.cpp b/eval/src/vespa/eval/instruction/sparse_dot_product_function.cpp index 93ae2856372..7cc4417bdbb 100644 --- a/eval/src/vespa/eval/instruction/sparse_dot_product_function.cpp +++ b/eval/src/vespa/eval/instruction/sparse_dot_product_function.cpp @@ -2,8 +2,8 @@ #include "sparse_dot_product_function.h" #include "generic_join.h" -#include "detect_type.h" #include <vespa/eval/eval/fast_value.hpp> +#include <vespa/vespalib/util/typify.h> namespace vespalib::eval { @@ -13,62 +13,76 @@ using namespace instruction; namespace { -template <typename SCT, typename BCT> -double my_fast_sparse_dot_product(const FastValueIndex &small_idx, const FastValueIndex &big_idx, - const SCT *small_cells, const BCT *big_cells) +template <typename CT> +double my_sparse_dot_product_fallback(const Value::Index &lhs_idx, const Value::Index &rhs_idx, + const CT *lhs_cells, const CT *rhs_cells, size_t num_mapped_dims) __attribute__((noinline)); +template <typename CT> +double my_sparse_dot_product_fallback(const Value::Index &lhs_idx, const Value::Index &rhs_idx, + const CT *lhs_cells, const CT *rhs_cells, size_t num_mapped_dims) { double result = 0.0; - small_idx.map.each_map_entry([&](auto small_subspace, auto hash) { - auto small_addr = small_idx.map.get_addr(small_subspace); - auto big_subspace = big_idx.map.lookup(small_addr, hash); - if (big_subspace != FastAddrMap::npos()) { - result += (small_cells[small_subspace] * big_cells[big_subspace]); - } - }); + SparseJoinPlan plan(num_mapped_dims); + SparseJoinState sparse(plan, lhs_idx, rhs_idx); + auto outer = sparse.first_index.create_view({}); + auto inner = sparse.second_index.create_view(sparse.second_view_dims); + outer->lookup({}); + while (outer->next_result(sparse.first_address, sparse.first_subspace)) { + inner->lookup(sparse.address_overlap); + if (inner->next_result(sparse.second_only_address, sparse.second_subspace)) { + result += (lhs_cells[sparse.lhs_subspace] * rhs_cells[sparse.rhs_subspace]); + } + } return result; } -template <typename LCT, typename RCT> -void my_sparse_dot_product_op(InterpretedFunction::State &state, uint64_t num_mapped_dims) { - const auto &lhs_idx = state.peek(1).index(); - const auto &rhs_idx = state.peek(0).index(); - const LCT *lhs_cells = state.peek(1).cells().typify<LCT>().cbegin(); - const RCT *rhs_cells = state.peek(0).cells().typify<RCT>().cbegin(); - if (auto indexes = detect_type<FastValueIndex>(lhs_idx, rhs_idx)) { -#if __has_cpp_attribute(likely) - [[likely]]; -#endif - const auto &lhs_fast = indexes.get<0>(); - const auto &rhs_fast = indexes.get<1>(); - double result = (rhs_fast.map.size() < lhs_fast.map.size()) - ? my_fast_sparse_dot_product(rhs_fast, lhs_fast, rhs_cells, lhs_cells) - : my_fast_sparse_dot_product(lhs_fast, rhs_fast, lhs_cells, rhs_cells); - state.pop_pop_push(state.stash.create<ScalarValue<double>>(result)); - } else { -#if __has_cpp_attribute(unlikely) - [[unlikely]]; -#endif - double result = 0.0; - SparseJoinPlan plan(num_mapped_dims); - SparseJoinState sparse(plan, lhs_idx, rhs_idx); - auto outer = sparse.first_index.create_view({}); - auto inner = sparse.second_index.create_view(sparse.second_view_dims); - outer->lookup({}); - while (outer->next_result(sparse.first_address, sparse.first_subspace)) { - inner->lookup(sparse.address_overlap); - if (inner->next_result(sparse.second_only_address, sparse.second_subspace)) { - result += (lhs_cells[sparse.lhs_subspace] * rhs_cells[sparse.rhs_subspace]); +template <typename CT, bool single_dim> +double my_fast_sparse_dot_product(const FastAddrMap *small_map, const FastAddrMap *big_map, + const CT *small_cells, const CT *big_cells) +{ + double result = 0.0; + if (big_map->size() < small_map->size()) { + std::swap(small_map, big_map); + std::swap(small_cells, big_cells); + } + if constexpr (single_dim) { + const auto &labels = small_map->labels(); + for (size_t i = 0; i < labels.size(); ++i) { + auto big_subspace = big_map->lookup_singledim(labels[i]); + if (big_subspace != FastAddrMap::npos()) { + result += (small_cells[i] * big_cells[big_subspace]); } } - state.pop_pop_push(state.stash.create<ScalarValue<double>>(result)); + } else { + small_map->each_map_entry([&](auto small_subspace, auto hash) { + auto small_addr = small_map->get_addr(small_subspace); + auto big_subspace = big_map->lookup(small_addr, hash); + if (big_subspace != FastAddrMap::npos()) { + result += (small_cells[small_subspace] * big_cells[big_subspace]); + } + }); } + return result; +} + +template <typename CT, bool single_dim> +void my_sparse_dot_product_op(InterpretedFunction::State &state, uint64_t num_mapped_dims) { + const auto &lhs_idx = state.peek(1).index(); + const auto &rhs_idx = state.peek(0).index(); + const CT *lhs_cells = state.peek(1).cells().typify<CT>().cbegin(); + const CT *rhs_cells = state.peek(0).cells().typify<CT>().cbegin(); + double result = __builtin_expect(are_fast(lhs_idx, rhs_idx), true) + ? my_fast_sparse_dot_product<CT,single_dim>(&as_fast(lhs_idx).map, &as_fast(rhs_idx).map, lhs_cells, rhs_cells) + : my_sparse_dot_product_fallback<CT>(lhs_idx, rhs_idx, lhs_cells, rhs_cells, num_mapped_dims); + state.pop_pop_push(state.stash.create<ScalarValue<double>>(result)); } struct MyGetFun { - template <typename LCT, typename RCT> - static auto invoke() { return my_sparse_dot_product_op<LCT,RCT>; } + template <typename CT, typename SINGLE_DIM> + static auto invoke() { return my_sparse_dot_product_op<CT,SINGLE_DIM::value>; } }; +using MyTypify = TypifyValue<TypifyCellType,TypifyBool>; + } // namespace <unnamed> SparseDotProductFunction::SparseDotProductFunction(const TensorFunction &lhs_in, @@ -80,15 +94,18 @@ SparseDotProductFunction::SparseDotProductFunction(const TensorFunction &lhs_in, InterpretedFunction::Instruction SparseDotProductFunction::compile_self(const ValueBuilderFactory &, Stash &) const { - auto op = typify_invoke<2,TypifyCellType,MyGetFun>(lhs().result_type().cell_type(), rhs().result_type().cell_type()); - return InterpretedFunction::Instruction(op, lhs().result_type().count_mapped_dimensions()); + size_t num_dims = lhs().result_type().count_mapped_dimensions(); + auto op = typify_invoke<2,MyTypify,MyGetFun>(lhs().result_type().cell_type(), + (num_dims == 1)); + return InterpretedFunction::Instruction(op, num_dims); } bool SparseDotProductFunction::compatible_types(const ValueType &res, const ValueType &lhs, const ValueType &rhs) { return (res.is_scalar() && (res.cell_type() == CellType::DOUBLE) && - lhs.is_sparse() && (rhs.dimensions() == lhs.dimensions())); + lhs.is_sparse() && (rhs.dimensions() == lhs.dimensions()) && + lhs.cell_type() == rhs.cell_type()); } const TensorFunction & diff --git a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java index 1f2927663df..075166e4759 100644 --- a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java +++ b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java @@ -74,7 +74,7 @@ public class Flags { public static final UnboundStringFlag RESPONSE_SEQUENCER_TYPE = defineStringFlag( "response-sequencer-type", "ADAPTIVE", - List.of("baldersheim"), "2020-12-02", "2021-02-01", + List.of("baldersheim"), "2020-12-02", "2022-01-01", "Selects type of sequenced executor used for mbus responses, valid values are LATENCY, ADAPTIVE, THROUGHPUT", "Takes effect at redeployment", ZONE_ID, APPLICATION_ID); @@ -109,25 +109,11 @@ public class Flags { public static final UnboundBooleanFlag USE_THREE_PHASE_UPDATES = defineFeatureFlag( "use-three-phase-updates", false, - List.of("vekterli"), "2020-12-02", "2021-02-01", + List.of("vekterli"), "2020-12-02", "2021-03-01", "Whether to enable the use of three-phase updates when bucket replicas are out of sync.", "Takes effect at redeployment", ZONE_ID, APPLICATION_ID); - public static final UnboundBooleanFlag USE_FAST_VALUE_TENSOR_IMPLEMENTATION = defineFeatureFlag( - "use-fast-value-tensor-implementation", false, - List.of("geirst"), "2020-12-02", "2021-02-01", - "Whether to use FastValueBuilderFactory as the tensor implementation on all content nodes.", - "Takes effect at restart of content node process", - ZONE_ID, APPLICATION_ID); - - public static final UnboundBooleanFlag TCP_ABORT_ON_OVERFLOW = defineFeatureFlag( - "tcp-abort-on-overflow", false, - List.of("andreer"), "2020-12-02", "2021-02-01", - "Whether to set /proc/sys/net/ipv4/tcp_abort_on_overflow to 0 (false) or 1 (true)", - "Takes effect on next host-admin tick.", - HOSTNAME); - public static final UnboundStringFlag TLS_FOR_ZOOKEEPER_CLIENT_SERVER_COMMUNICATION = defineStringFlag( "tls-for-zookeeper-client-server-communication", "OFF", List.of("hmusum"), "2020-12-02", "2021-04-01", @@ -183,14 +169,6 @@ public class Flags { ZONE_ID ); - public static final UnboundIntFlag TENANT_NODE_QUOTA = defineIntFlag( - "tenant-node-quota", 5, - List.of("andreer"), "2020-12-02", "2021-02-01", - "The number of nodes a tenant is allowed to request per cluster", - "Only takes effect on next deployment, if set to a value other than the default for flag!", - APPLICATION_ID - ); - public static final UnboundBooleanFlag ONLY_PUBLIC_ACCESS = defineFeatureFlag( "enable-public-only", false, List.of("ogronnesby"), "2020-12-02", "2021-02-01", diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java index 96bce8b71d4..05c20ee69f1 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java @@ -17,10 +17,7 @@ import com.yahoo.config.provision.ProvisionLogger; import com.yahoo.config.provision.Provisioner; import com.yahoo.config.provision.Zone; import com.yahoo.transaction.Mutex; -import com.yahoo.vespa.flags.FetchVector; import com.yahoo.vespa.flags.FlagSource; -import com.yahoo.vespa.flags.Flags; -import com.yahoo.vespa.flags.IntFlag; import com.yahoo.vespa.hosted.provision.Node; import com.yahoo.vespa.hosted.provision.NodeList; import com.yahoo.vespa.hosted.provision.NodeRepository; @@ -59,7 +56,6 @@ public class NodeRepositoryProvisioner implements Provisioner { private final Activator activator; private final Optional<LoadBalancerProvisioner> loadBalancerProvisioner; private final NodeResourceLimits nodeResourceLimits; - private final IntFlag tenantNodeQuota; @Inject public NodeRepositoryProvisioner(NodeRepository nodeRepository, Zone zone, @@ -76,7 +72,6 @@ public class NodeRepositoryProvisioner implements Provisioner { provisionServiceProvider.getHostProvisioner(), loadBalancerProvisioner); this.activator = new Activator(nodeRepository, loadBalancerProvisioner); - this.tenantNodeQuota = Flags.TENANT_NODE_QUOTA.bindTo(flagSource); } @@ -92,10 +87,6 @@ public class NodeRepositoryProvisioner implements Provisioner { if (cluster.group().isPresent()) throw new IllegalArgumentException("Node requests cannot specify a group"); - if ( ! hasQuota(application, requested.maxResources().nodes())) - throw new IllegalArgumentException(requested + " requested for " + cluster + - ". Max value exceeds your quota. Resolve this at https://cloud.vespa.ai/pricing"); - nodeResourceLimits.ensureWithinAdvertisedLimits("Min", requested.minResources().nodeResources(), cluster); nodeResourceLimits.ensureWithinAdvertisedLimits("Max", requested.maxResources().nodeResources(), cluster); @@ -194,15 +185,6 @@ public class NodeRepositoryProvisioner implements Provisioner { ", downscaling to " + actualNodes + " nodes in " + zone.environment()); } - private boolean hasQuota(ApplicationId application, int requestedNodes) { - if ( ! this.zone.system().isPublic()) return true; // no quota management - - if (application.tenant().value().hashCode() == 3857) return requestedNodes <= 60; - if (application.tenant().value().hashCode() == -1271827001) return requestedNodes <= 75; - - return requestedNodes <= tenantNodeQuota.with(FetchVector.Dimension.APPLICATION_ID, application.tenant().value()).value(); - } - private List<HostSpec> asSortedHosts(List<Node> nodes, NodeResources requestedResources) { nodes.sort(Comparator.comparingInt(node -> node.allocation().get().membership().index())); List<HostSpec> hosts = new ArrayList<>(nodes.size()); diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java index cbac5a39e09..be7d2656d13 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java @@ -633,35 +633,6 @@ public class ProvisioningTest { } @Test - public void out_of_quota() { - ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(SystemName.Public, - Environment.prod, - RegionName.from("us-east"))).build(); - - tester.makeReadyHosts(13, defaultResources).activateTenantHosts(); - ApplicationId application = ProvisioningTester.applicationId(); - try { - prepare(application, 2, 2, 6, 3, defaultResources, tester); - fail("Expected exception"); - } - catch (IllegalArgumentException e) { - assertEquals("6 nodes with [vcpu: 1.0, memory: 4.0 Gb, disk 10.0 Gb, bandwidth: 4.0 Gbps] requested for content cluster 'content0' 6.42. Max value exceeds your quota. Resolve this at https://cloud.vespa.ai/pricing", - e.getMessage()); - } - } - - @Test - public void no_out_of_quota_outside_public() { - ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(SystemName.main, - Environment.prod, - RegionName.from("us-east"))).build(); - - tester.makeReadyHosts(13, defaultResources).activateTenantHosts(); - ApplicationId application = ProvisioningTester.applicationId(); - prepare(application, 2, 2, 6, 3, defaultResources, tester); - } - - @Test public void out_of_capacity_but_cannot_fail() { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))).build(); tester.makeReadyHosts(4, defaultResources).activateTenantHosts(); diff --git a/persistence/src/vespa/persistence/dummyimpl/dummy_bucket_executor.h b/persistence/src/vespa/persistence/dummyimpl/dummy_bucket_executor.h index b832cb6c02c..2479787eb31 100644 --- a/persistence/src/vespa/persistence/dummyimpl/dummy_bucket_executor.h +++ b/persistence/src/vespa/persistence/dummyimpl/dummy_bucket_executor.h @@ -18,7 +18,7 @@ public: DummyBucketExecutor(size_t numExecutors); ~DummyBucketExecutor() override; std::unique_ptr<BucketTask> execute(const Bucket & bucket, std::unique_ptr<BucketTask> task) override; - void sync() override; + void sync(); private: std::unique_ptr<vespalib::SyncableThreadExecutor> _executor; std::mutex _lock; diff --git a/persistence/src/vespa/persistence/dummyimpl/dummypersistence.cpp b/persistence/src/vespa/persistence/dummyimpl/dummypersistence.cpp index 0865500d3c0..305f1c81192 100644 --- a/persistence/src/vespa/persistence/dummyimpl/dummypersistence.cpp +++ b/persistence/src/vespa/persistence/dummyimpl/dummypersistence.cpp @@ -866,14 +866,10 @@ DummyPersistence::register_resource_usage_listener(IResourceUsageListener &liste namespace { -class SyncExecutorOnDestruction : public vespalib::IDestructorCallback { +class ExecutorRegistration : public vespalib::IDestructorCallback { public: - explicit SyncExecutorOnDestruction(std::shared_ptr<BucketExecutor> executor) : _executor(std::move(executor)) { } - ~SyncExecutorOnDestruction() override { - if (_executor) { - _executor->sync(); - } - } + explicit ExecutorRegistration(std::shared_ptr<BucketExecutor> executor) : _executor(std::move(executor)) { } + ~ExecutorRegistration() override = default; private: std::shared_ptr<BucketExecutor> _executor; }; @@ -885,7 +881,7 @@ DummyPersistence::register_executor(std::shared_ptr<BucketExecutor> executor) { assert(_bucket_executor.expired()); _bucket_executor = executor; - return std::make_unique<SyncExecutorOnDestruction>(executor); + return std::make_unique<ExecutorRegistration>(executor); } std::string diff --git a/persistence/src/vespa/persistence/spi/bucketexecutor.h b/persistence/src/vespa/persistence/spi/bucketexecutor.h index 8237b78cca0..d1ada30959e 100644 --- a/persistence/src/vespa/persistence/spi/bucketexecutor.h +++ b/persistence/src/vespa/persistence/spi/bucketexecutor.h @@ -29,7 +29,6 @@ public: struct BucketExecutor { virtual ~BucketExecutor() = default; virtual std::unique_ptr<BucketTask> execute(const Bucket & bucket, std::unique_ptr<BucketTask> task) = 0; - virtual void sync() = 0; }; } diff --git a/searchcore/src/tests/proton/documentdb/lid_space_compaction/lid_space_jobtest.h b/searchcore/src/tests/proton/documentdb/lid_space_compaction/lid_space_jobtest.h index fd1cf7fb5a8..dd2760022c2 100644 --- a/searchcore/src/tests/proton/documentdb/lid_space_compaction/lid_space_jobtest.h +++ b/searchcore/src/tests/proton/documentdb/lid_space_compaction/lid_space_jobtest.h @@ -5,8 +5,9 @@ #include <vespa/persistence/spi/bucketexecutor.h> #include <vespa/vespalib/gtest/gtest.h> +namespace storage::spi::dummy { class DummyBucketExecutor; } struct JobTestBase : public ::testing::TestWithParam<bool> { - std::unique_ptr<storage::spi::BucketExecutor> _bucketExecutor; + std::unique_ptr<storage::spi::dummy::DummyBucketExecutor> _bucketExecutor; std::unique_ptr<vespalib::SyncableThreadExecutor> _singleExecutor; std::unique_ptr<searchcorespi::index::IThreadService> _master; std::shared_ptr<MyHandler> _handler; @@ -54,7 +55,7 @@ struct JobTest : public JobTestBase { std::unique_ptr<IMaintenanceJobRunner> _jobRunner; JobTest(); - ~JobTest(); + ~JobTest() override; void init(uint32_t allowedLidBloat, double allowedLidBloatFactor, double resourceLimitFactor = RESOURCE_LIMIT_FACTOR, @@ -68,7 +69,7 @@ struct JobTest : public JobTestBase { class JobDisabledByRemoveOpsTest : public JobTest { public: JobDisabledByRemoveOpsTest(); - ~JobDisabledByRemoveOpsTest(); + ~JobDisabledByRemoveOpsTest() override; void job_is_disabled_while_remove_ops_are_ongoing(bool remove_batch); void job_becomes_disabled_if_remove_ops_starts(bool remove_batch); @@ -80,7 +81,7 @@ struct MyCountJobRunner; struct MaxOutstandingJobTest : public JobTest { std::unique_ptr<MyCountJobRunner> runner; MaxOutstandingJobTest(); - ~MaxOutstandingJobTest(); + ~MaxOutstandingJobTest() override; void init(uint32_t maxOutstandingMoveOps); void assertRunToBlocked(); void assertRunToNotBlocked(); diff --git a/searchcore/src/vespa/searchcore/proton/persistenceengine/persistenceengine.cpp b/searchcore/src/vespa/searchcore/proton/persistenceengine/persistenceengine.cpp index b188b97404d..d92a945a42a 100644 --- a/searchcore/src/vespa/searchcore/proton/persistenceengine/persistenceengine.cpp +++ b/searchcore/src/vespa/searchcore/proton/persistenceengine/persistenceengine.cpp @@ -741,14 +741,10 @@ PersistenceEngine::getWLock() const namespace { -class SyncExecutorOnDestruction : public vespalib::IDestructorCallback { +class ExecutorRegistration : public vespalib::IDestructorCallback { public: - explicit SyncExecutorOnDestruction(std::shared_ptr<BucketExecutor> executor) : _executor(std::move(executor)) { } - ~SyncExecutorOnDestruction() override { - if (_executor) { - _executor->sync(); - } - } + explicit ExecutorRegistration(std::shared_ptr<BucketExecutor> executor) : _executor(std::move(executor)) { } + ~ExecutorRegistration() override = default; private: std::shared_ptr<BucketExecutor> _executor; }; @@ -760,7 +756,7 @@ PersistenceEngine::register_executor(std::shared_ptr<BucketExecutor> executor) { assert(_bucket_executor.expired()); _bucket_executor = executor; - return std::make_unique<SyncExecutorOnDestruction>(executor); + return std::make_unique<ExecutorRegistration>(executor); } std::unique_ptr<BucketTask> @@ -772,11 +768,4 @@ PersistenceEngine::execute(const storage::spi::Bucket &bucket, std::unique_ptr<B return task; } -void PersistenceEngine::sync() { - auto bucketExecutor = get_bucket_executor(); - if (bucketExecutor) { - bucketExecutor->sync(); - } -} - } // storage diff --git a/searchcore/src/vespa/searchcore/proton/persistenceengine/persistenceengine.h b/searchcore/src/vespa/searchcore/proton/persistenceengine/persistenceengine.h index 77dfc74d765..f1f680f63f6 100644 --- a/searchcore/src/vespa/searchcore/proton/persistenceengine/persistenceengine.h +++ b/searchcore/src/vespa/searchcore/proton/persistenceengine/persistenceengine.h @@ -130,7 +130,6 @@ public: WriteGuard getWLock() const; ResourceUsageTracker &get_resource_usage_tracker() noexcept { return *_resource_usage_tracker; } std::unique_ptr<BucketTask> execute(const Bucket &bucket, std::unique_ptr<BucketTask> task) override; - void sync() override; }; } diff --git a/searchcore/src/vespa/searchcore/proton/server/lid_space_compaction_job_base.cpp b/searchcore/src/vespa/searchcore/proton/server/lid_space_compaction_job_base.cpp index d3dae686d2d..3987282ef34 100644 --- a/searchcore/src/vespa/searchcore/proton/server/lid_space_compaction_job_base.cpp +++ b/searchcore/src/vespa/searchcore/proton/server/lid_space_compaction_job_base.cpp @@ -130,6 +130,9 @@ LidSpaceCompactionJobBase::run() } if (_scanItr && !_scanItr->valid()) { + if (!inSync()) { + return false; + } if (shouldRestartScanDocuments(_handler->getLidStatus())) { _scanItr = _handler->getIterator(); } else { diff --git a/searchcore/src/vespa/searchcore/proton/server/lid_space_compaction_job_base.h b/searchcore/src/vespa/searchcore/proton/server/lid_space_compaction_job_base.h index 759a18361f7..e91502f766c 100644 --- a/searchcore/src/vespa/searchcore/proton/server/lid_space_compaction_job_base.h +++ b/searchcore/src/vespa/searchcore/proton/server/lid_space_compaction_job_base.h @@ -50,6 +50,7 @@ private: void compactLidSpace(const search::LidUsageStats &stats); bool remove_batch_is_ongoing() const; bool remove_is_ongoing() const; + virtual bool inSync() const { return true; } protected: search::DocumentMetaData getNextDocument(const search::LidUsageStats &stats, bool retryLastDocument); public: diff --git a/searchcore/src/vespa/searchcore/proton/server/lid_space_compaction_job_take2.cpp b/searchcore/src/vespa/searchcore/proton/server/lid_space_compaction_job_take2.cpp index cbf3de20b1f..acb81ef6976 100644 --- a/searchcore/src/vespa/searchcore/proton/server/lid_space_compaction_job_take2.cpp +++ b/searchcore/src/vespa/searchcore/proton/server/lid_space_compaction_job_take2.cpp @@ -11,6 +11,7 @@ #include <vespa/vespalib/util/destructor_callbacks.h> #include <vespa/vespalib/util/lambdatask.h> #include <cassert> +#include <thread> using search::DocumentMetaData; using search::LidUsageStats; @@ -34,19 +35,30 @@ CompactionJob::scanDocuments(const LidUsageStats &stats) moveDocument(meta, std::make_shared<DoneContext>(std::make_pair(std::move(opsTracker), std::move(onDone)))); })); if (failed) return false; + _startedCount.fetch_add(1, std::memory_order_relaxed); if (isBlocked(BlockedReason::OUTSTANDING_OPS)) { return true; } } } - if (!_scanItr->valid()) { - sync(); - } return false; } +namespace { + class IncOnDestruct { + public: + IncOnDestruct(std::atomic<size_t> & count) : _count(count) {} + ~IncOnDestruct() { + _count.fetch_add(1, std::memory_order_relaxed); + } + private: + std::atomic<size_t> & _count; + }; +} void CompactionJob::moveDocument(const search::DocumentMetaData & meta, std::shared_ptr<IDestructorCallback> context) { + IncOnDestruct countGuard(_executedCount); + if (_stopped.load(std::memory_order_relaxed)) return; // The real lid must be sampled in the master thread. //TODO remove target lid from createMoveOperation interface auto op = _handler->createMoveOperation(meta, 0); @@ -55,6 +67,7 @@ CompactionJob::moveDocument(const search::DocumentMetaData & meta, std::shared_p if (meta.gid != op->getDocument()->getId().getGlobalId()) return; _master.execute(makeLambdaTask([this, metaThen=meta, moveOp=std::move(op), onDone=std::move(context)]() { + if (_stopped.load(std::memory_order_relaxed)) return; search::DocumentMetaData metaNow = _handler->getMetaData(metaThen.lid); if (metaNow.lid != metaThen.lid) return; if (metaNow.bucketId != metaThen.bucketId) return; @@ -82,20 +95,25 @@ CompactionJob::CompactionJob(const DocumentDBLidSpaceCompactionConfig &config, blockableConfig, clusterStateChangedNotifier, nodeRetired), _master(master), _bucketExecutor(bucketExecutor), - _bucketSpace(bucketSpace) -{ -} + _bucketSpace(bucketSpace), + _stopped(false), + _startedCount(0), + _executedCount(0) +{ } CompactionJob::~CompactionJob() = default; -void -CompactionJob::sync() { - _bucketExecutor.sync(); +bool +CompactionJob::inSync() const { + return _executedCount == _startedCount; } void CompactionJob::onStop() { - sync(); + _stopped = true; + while ( ! inSync() ) { + std::this_thread::sleep_for(1ms); + } } } // namespace proton diff --git a/searchcore/src/vespa/searchcore/proton/server/lid_space_compaction_job_take2.h b/searchcore/src/vespa/searchcore/proton/server/lid_space_compaction_job_take2.h index 053950ebd2f..65b847d0468 100644 --- a/searchcore/src/vespa/searchcore/proton/server/lid_space_compaction_job_take2.h +++ b/searchcore/src/vespa/searchcore/proton/server/lid_space_compaction_job_take2.h @@ -4,8 +4,9 @@ #include "lid_space_compaction_job_base.h" #include <vespa/document/bucket/bucketspace.h> +#include <atomic> -namespace storage::spi { struct BucketExecutor;} +namespace storage::spi { struct BucketExecutor; } namespace searchcorespi::index { struct IThreadService; } namespace vespalib { class IDestructorCallback; } namespace proton { @@ -28,11 +29,14 @@ private: IThreadService & _master; BucketExecutor &_bucketExecutor; document::BucketSpace _bucketSpace; + std::atomic<bool> _stopped; + std::atomic<size_t> _startedCount; + std::atomic<size_t> _executedCount; bool scanDocuments(const search::LidUsageStats &stats) override; void moveDocument(const search::DocumentMetaData & meta, std::shared_ptr<IDestructorCallback> onDone); void onStop() override; - void sync(); + bool inSync() const override; public: CompactionJob(const DocumentDBLidSpaceCompactionConfig &config, std::shared_ptr<ILidSpaceCompactionHandler> handler, diff --git a/storage/src/vespa/storage/persistence/filestorage/filestormanager.cpp b/storage/src/vespa/storage/persistence/filestorage/filestormanager.cpp index c71a7fee424..7c175686359 100644 --- a/storage/src/vespa/storage/persistence/filestorage/filestormanager.cpp +++ b/storage/src/vespa/storage/persistence/filestorage/filestormanager.cpp @@ -49,10 +49,6 @@ public: return _executor.execute(bucket, std::move(task)); } - void sync() override { - _executor.sync(); - } - private: spi::BucketExecutor & _executor; }; @@ -990,8 +986,4 @@ FileStorManager::execute(const spi::Bucket &bucket, std::unique_ptr<spi::BucketT return task; } -void -FileStorManager::sync() { -} - } // storage diff --git a/storage/src/vespa/storage/persistence/filestorage/filestormanager.h b/storage/src/vespa/storage/persistence/filestorage/filestormanager.h index 6eaef45e9bd..a48b4e0e208 100644 --- a/storage/src/vespa/storage/persistence/filestorage/filestormanager.h +++ b/storage/src/vespa/storage/persistence/filestorage/filestormanager.h @@ -175,7 +175,6 @@ private: void update_reported_state_after_db_init(); std::unique_ptr<spi::BucketTask> execute(const spi::Bucket &bucket, std::unique_ptr<spi::BucketTask> task) override; - void sync() override; }; } // storage diff --git a/vespajlib/src/main/java/com/yahoo/concurrent/maintenance/Maintainer.java b/vespajlib/src/main/java/com/yahoo/concurrent/maintenance/Maintainer.java index daad1f8fb4b..02968352f0c 100644 --- a/vespajlib/src/main/java/com/yahoo/concurrent/maintenance/Maintainer.java +++ b/vespajlib/src/main/java/com/yahoo/concurrent/maintenance/Maintainer.java @@ -88,7 +88,7 @@ public abstract class Maintainer implements Runnable { try (var lock = jobControl.lockJob(name())) { if (maintain()) jobMetrics.recordSuccessOf(name()); } catch (Throwable e) { - log.log(Level.WARNING, this + " failed. Will retry in " + interval.toMinutes() + " minutes", e); + log.log(Level.WARNING, this + " failed. Will retry in " + interval, e); } finally { jobMetrics.forward(name()); } diff --git a/vespalib/src/tests/shared_string_repo/shared_string_repo_test.cpp b/vespalib/src/tests/shared_string_repo/shared_string_repo_test.cpp index 4c3c32c2326..97c767a17ed 100644 --- a/vespalib/src/tests/shared_string_repo/shared_string_repo_test.cpp +++ b/vespalib/src/tests/shared_string_repo/shared_string_repo_test.cpp @@ -361,6 +361,8 @@ TEST("require that basic handle usage works") { TEST_DO(verify_not_eq(empty, bar)); TEST_DO(verify_not_eq(foo, bar)); + EXPECT_EQUAL(empty.id().hash(), 0u); + EXPECT_EQUAL(empty.id().value(), 0u); EXPECT_TRUE(empty.id() == string_id()); EXPECT_TRUE(empty2.id() == string_id()); EXPECT_EQUAL(empty.as_string(), vespalib::string("")); @@ -415,9 +417,11 @@ TEST("require that handle can be self-assigned") { //----------------------------------------------------------------------------- -void verify_direct(const vespalib::string &str) { +void verify_direct(const vespalib::string &str, size_t value) { size_t before = SharedStringRepo::stats().active_entries; Handle handle(str); + EXPECT_EQUAL(handle.id().hash(), value + 1); + EXPECT_EQUAL(handle.id().value(), value + 1); EXPECT_EQUAL(SharedStringRepo::stats().active_entries, before); EXPECT_EQUAL(handle.as_string(), str); } @@ -425,14 +429,15 @@ void verify_direct(const vespalib::string &str) { void verify_not_direct(const vespalib::string &str) { size_t before = SharedStringRepo::stats().active_entries; Handle handle(str); + EXPECT_EQUAL(handle.id().hash(), handle.id().value()); EXPECT_EQUAL(SharedStringRepo::stats().active_entries, before + 1); EXPECT_EQUAL(handle.as_string(), str); } TEST("require that direct handles work as expected") { - TEST_DO(verify_direct("")); + TEST_DO(verify_direct("", -1)); for (size_t i = 0; i < 100000; ++i) { - verify_direct(fmt("%zu", i)); + verify_direct(fmt("%zu", i), i); } TEST_DO(verify_not_direct(" ")); TEST_DO(verify_not_direct(" 5")); diff --git a/vespalib/src/vespa/vespalib/util/string_id.h b/vespalib/src/vespa/vespalib/util/string_id.h index 3e608c9d339..371caf8bf95 100644 --- a/vespalib/src/vespa/vespalib/util/string_id.h +++ b/vespalib/src/vespa/vespalib/util/string_id.h @@ -31,6 +31,7 @@ public: constexpr string_id &operator=(const string_id &) noexcept = default; constexpr string_id &operator=(string_id &&) noexcept = default; constexpr uint32_t hash() const noexcept { return _id; } + constexpr uint32_t value() const noexcept { return _id; } // NB: not lexical sorting order, but can be used in maps constexpr bool operator<(const string_id &rhs) const noexcept { return (_id < rhs._id); } constexpr bool operator==(const string_id &rhs) const noexcept { return (_id == rhs._id); } |