-
Notifications
You must be signed in to change notification settings - Fork 5.5k
cds: add a benchmark #14167
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
cds: add a benchmark #14167
Changes from 5 commits
f3b89b0
589c94f
d55b812
5f1834f
ca24a49
58cf314
cc7f54e
be6a36e
db19c30
cd034a9
95bc725
8c3b9c1
4f79709
9289d18
1d3692f
256b961
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,191 @@ | ||
| // Note: this should be run with --compilation_mode=opt, and would benefit from a | ||
| // quiescent system with disabled cstate power management. | ||
|
|
||
| #include "envoy/config/cluster/v3/cluster.pb.h" | ||
| #include "envoy/config/core/v3/health_check.pb.h" | ||
| #include "envoy/config/endpoint/v3/endpoint.pb.h" | ||
| #include "envoy/config/endpoint/v3/endpoint_components.pb.h" | ||
| #include "envoy/service/discovery/v3/discovery.pb.h" | ||
| #include "envoy/stats/scope.h" | ||
|
|
||
| #include "common/config/grpc_mux_impl.h" | ||
| #include "common/config/grpc_subscription_impl.h" | ||
| #include "common/config/utility.h" | ||
| #include "common/singleton/manager_impl.h" | ||
| #include "common/upstream/eds.h" | ||
|
|
||
| #include "server/transport_socket_config_impl.h" | ||
|
|
||
| #include "test/benchmark/main.h" | ||
| #include "test/common/upstream/utility.h" | ||
| #include "test/mocks/local_info/mocks.h" | ||
| #include "test/mocks/protobuf/mocks.h" | ||
| #include "test/mocks/runtime/mocks.h" | ||
| #include "test/mocks/server/admin.h" | ||
| #include "test/mocks/server/instance.h" | ||
| #include "test/mocks/ssl/mocks.h" | ||
| #include "test/mocks/upstream/cluster_manager.h" | ||
| #include "test/test_common/test_runtime.h" | ||
| #include "test/test_common/utility.h" | ||
|
|
||
| #include "benchmark/benchmark.h" | ||
|
|
||
| using ::benchmark::State; | ||
| using Envoy::benchmark::skipExpensiveBenchmarks; | ||
|
|
||
| namespace Envoy { | ||
| namespace Upstream { | ||
|
|
||
| class CdsSpeedTest { | ||
| public: | ||
| CdsSpeedTest(State& state, bool v2_config) | ||
| : state_(state), v2_config_(v2_config), | ||
| type_url_(v2_config_ | ||
| ? "type.googleapis.com/envoy.api.v2.ClusterLoadAssignment" | ||
| : "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment"), | ||
| subscription_stats_(Config::Utility::generateStats(stats_)), | ||
| api_(Api::createApiForTest(stats_)), async_client_(new Grpc::MockAsyncClient()), | ||
| grpc_mux_(new Config::GrpcMuxImpl( | ||
| local_info_, std::unique_ptr<Grpc::MockAsyncClient>(async_client_), dispatcher_, | ||
| *Protobuf::DescriptorPool::generated_pool()->FindMethodByName( | ||
| "envoy.service.endpoint.v3.EndpointDiscoveryService.StreamEndpoints"), | ||
|
pgenera marked this conversation as resolved.
Outdated
|
||
| envoy::config::core::v3::ApiVersion::AUTO, random_, stats_, {}, true)) { | ||
|
|
||
| resetCluster(R"EOF( | ||
| name: name | ||
| connect_timeout: 0.25s | ||
| type: EDS | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In terms of
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right now this exercises the number of clusters for both the v2 & v3 APIs; I propose taking out the v2 part and adding LB algorithm & endpoints per cluster, all for In the interim I've separated out the v2 & v3 tests to make the output more readable and give the Complexity Computing Magic a better chance at working. I haven't generated interesting output, perf or otherwise, as I just got this working again. I'm happy to do so once we know I'm testing the right things.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, let's drop v2 as it's on the way out. Agree on the changes proposed.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FWIW, I think we could also explore LB algorithm and endpoints in the EDS test if it was able to fully simulate what is going on in
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've dropped v2; maybe adding a larger set of test parameters to this or cds should go in a followup?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's do the additions in followup PRs. |
||
| eds_cluster_config: | ||
|
pgenera marked this conversation as resolved.
Outdated
|
||
| service_name: fare | ||
| eds_config: | ||
| api_config_source: | ||
| cluster_names: | ||
| - eds | ||
| refresh_delay: 1s | ||
| )EOF", | ||
| Envoy::Upstream::Cluster::InitializePhase::Secondary); | ||
|
|
||
| EXPECT_CALL(*cm_.subscription_factory_.subscription_, start(_, _)); | ||
| cluster_->initialize([this] { initialized_ = true; }); | ||
| EXPECT_CALL(*async_client_, startRaw(_, _, _, _)).WillOnce(testing::Return(&async_stream_)); | ||
| subscription_->start({"fare"}); | ||
| } | ||
|
|
||
| void resetCluster(const std::string& yaml_config, Cluster::InitializePhase initialize_phase) { | ||
| local_info_.node_.mutable_locality()->set_zone("us-east-1a"); | ||
| eds_cluster_ = parseClusterFromV3Yaml(yaml_config); | ||
| Envoy::Stats::ScopePtr scope = stats_.createScope(fmt::format( | ||
| "cluster.{}.", | ||
| eds_cluster_.alt_stat_name().empty() ? eds_cluster_.name() : eds_cluster_.alt_stat_name())); | ||
| Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context( | ||
| admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, stats_, | ||
| singleton_manager_, tls_, validation_visitor_, *api_); | ||
| cluster_ = std::make_shared<EdsClusterImpl>(eds_cluster_, runtime_, factory_context, | ||
| std::move(scope), false); | ||
| EXPECT_EQ(initialize_phase, cluster_->initializePhase()); | ||
| eds_callbacks_ = cm_.subscription_factory_.callbacks_; | ||
| subscription_ = std::make_unique<Config::GrpcSubscriptionImpl>( | ||
| grpc_mux_, *eds_callbacks_, resource_decoder_, subscription_stats_, type_url_, dispatcher_, | ||
| std::chrono::milliseconds(), false); | ||
| } | ||
|
|
||
| void clusterHelper(bool ignore_unknown_dynamic_fields, size_t num_clusters) { | ||
| state_.PauseTiming(); | ||
|
|
||
| auto response = std::make_unique<envoy::service::discovery::v3::DiscoveryResponse>(); | ||
| response->set_type_url(type_url_); | ||
| response->set_version_info(fmt::format("version-{}", version_++)); | ||
|
|
||
| // make a pile of dynamic clusters and add them to the response | ||
| for (size_t i = 0; i < num_clusters; ++i) { | ||
| envoy::config::endpoint::v3::ClusterLoadAssignment cluster_load_assignment; | ||
| cluster_load_assignment.set_cluster_name("fare_" + std::to_string(i)); | ||
|
|
||
| auto* endpoints = cluster_load_assignment.add_endpoints(); | ||
| endpoints->set_priority(1); | ||
| auto* locality = endpoints->mutable_locality(); | ||
| locality->set_region("region"); | ||
| locality->set_zone("zone"); | ||
| locality->set_sub_zone("sub_zone"); | ||
|
|
||
| auto* resource = response->mutable_resources()->Add(); | ||
| resource->PackFrom(cluster_load_assignment); | ||
| if (v2_config_) { | ||
| RELEASE_ASSERT(resource->type_url() == | ||
| "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", | ||
| ""); | ||
| resource->set_type_url("type.googleapis.com/envoy.api.v2.ClusterLoadAssignment"); | ||
| } | ||
| } | ||
| validation_visitor_.setSkipValidation(ignore_unknown_dynamic_fields); | ||
|
|
||
| state_.SetComplexityN(num_clusters); | ||
| state_.ResumeTiming(); | ||
| grpc_mux_->grpcStreamForTest().onReceiveMessage(std::move(response)); | ||
| } | ||
|
|
||
| TestDeprecatedV2Api _deprecated_v2_api_; | ||
|
pgenera marked this conversation as resolved.
Outdated
|
||
| State& state_; | ||
| const bool v2_config_; | ||
| const std::string type_url_; | ||
| uint64_t version_{}; | ||
| bool initialized_{}; | ||
| Stats::IsolatedStoreImpl stats_; | ||
| Config::SubscriptionStats subscription_stats_; | ||
| Ssl::MockContextManager ssl_context_manager_; | ||
| envoy::config::cluster::v3::Cluster eds_cluster_; | ||
| NiceMock<MockClusterManager> cm_; | ||
| NiceMock<Event::MockDispatcher> dispatcher_; | ||
| EdsClusterImplSharedPtr cluster_; | ||
| Config::SubscriptionCallbacks* eds_callbacks_{}; | ||
| Config::OpaqueResourceDecoderImpl<envoy::config::endpoint::v3::ClusterLoadAssignment> | ||
| resource_decoder_{validation_visitor_, "cluster_name"}; | ||
| NiceMock<Random::MockRandomGenerator> random_; | ||
| NiceMock<Runtime::MockLoader> runtime_; | ||
| NiceMock<LocalInfo::MockLocalInfo> local_info_; | ||
| NiceMock<Server::MockAdmin> admin_; | ||
| Singleton::ManagerImpl singleton_manager_{Thread::threadFactoryForTest()}; | ||
| NiceMock<ThreadLocal::MockInstance> tls_; | ||
| ProtobufMessage::MockValidationVisitor validation_visitor_; | ||
| Api::ApiPtr api_; | ||
| Grpc::MockAsyncClient* async_client_; | ||
| NiceMock<Grpc::MockAsyncStream> async_stream_; | ||
| Config::GrpcMuxImplSharedPtr grpc_mux_; | ||
| Config::GrpcSubscriptionImplPtr subscription_; | ||
| }; | ||
|
|
||
| } // namespace Upstream | ||
| } // namespace Envoy | ||
|
|
||
| static void addClusters(State& state) { | ||
| Envoy::Thread::MutexBasicLockable lock; | ||
| Envoy::Logger::Context logging_state(spdlog::level::warn, | ||
| Envoy::Logger::Logger::DEFAULT_LOG_FORMAT, lock, false); | ||
| for (auto _ : state) { // NOLINT(clang-analyzer-deadcode.DeadStores) | ||
| Envoy::Upstream::CdsSpeedTest speed_test(state, state.range(0)); | ||
| // if we've been instructed to skip tests, only run once no matter the argument: | ||
| uint32_t clusters = skipExpensiveBenchmarks() ? 1 : state.range(2); | ||
|
pgenera marked this conversation as resolved.
Outdated
|
||
| speed_test.clusterHelper(state.range(1), clusters); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wouldn't this also include the time it took to build the response proto in the benchmark?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't believe so; the calls to
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be clearer I think what we were benchmarking if we put the PauseTiming calls at the start of the loop here (line 162) and Resume at the end, and then put Resume/Pause calls in the helper functions surrounding the code we really want to test.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is what you mean. |
||
| } | ||
| } | ||
|
|
||
| BENCHMARK(addClusters) | ||
| ->Ranges({{false, true}, {false, true}, {64, 100000}}) | ||
| ->Unit(benchmark::kMillisecond) | ||
| ->Complexity(); | ||
|
|
||
| static void duplicateUpdate(State& state) { | ||
| Envoy::Thread::MutexBasicLockable lock; | ||
| Envoy::Logger::Context logging_state(spdlog::level::warn, | ||
| Envoy::Logger::Logger::DEFAULT_LOG_FORMAT, lock, false); | ||
|
|
||
| for (auto _ : state) { // NOLINT(clang-analyzer-deadcode.DeadStores) | ||
| Envoy::Upstream::CdsSpeedTest speed_test(state, false); | ||
| uint32_t clusters = skipExpensiveBenchmarks() ? 1 : state.range(0); | ||
|
pgenera marked this conversation as resolved.
Outdated
|
||
|
|
||
| speed_test.clusterHelper(true, clusters); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you comment a bit on a few choices here:
I'm not pushing back against those decisions I'm just trying to understand them.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This was mostly copy & pasted from eds_speed_test (I don't understand github's copy detection and why it didn't notice this); I've backported the changes to eds_speed_test as well.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. RE 1 and 3: cool, all set, thanks. Re 2: the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Testing bears out your hypothesis, I see what I missed there. |
||
| speed_test.clusterHelper(true, clusters); | ||
| } | ||
| } | ||
|
|
||
| BENCHMARK(duplicateUpdate)->Range(64, 100000)->Unit(benchmark::kMillisecond)->Complexity(); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -523,6 +523,7 @@ deallocate | |
| deallocated | ||
| deallocating | ||
| deallocation | ||
| deadcode | ||
| dec | ||
| dechunk | ||
| dechunked | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.