Skip to content

Commit

Permalink
benchmark: add route matcher benchmarks for exact and prefix match (e…
Browse files Browse the repository at this point in the history
…nvoyproxy#37086)

## Description

This PR adds two new benchmarks to evaluate the performance of route
matching using the new [matcher
tree](https://www.envoyproxy.io/docs/envoy/latest/xds/type/matcher/v3/matcher.proto#xds-type-matcher-v3-matcher-matchertree)
functionality:
1. `bmRouteTableSizeWithMatcherTree` - Tests [exact
matching](https://arc.net/l/quote/arnhgeme) using matcher tree
2. `bmRouteTableSizeWithPrefixMatcherTree` - Tests [prefix
matching](https://arc.net/l/quote/kajwgisw) using matcher tree with
nested routes

These benchmarks follow the same pattern as existing route matching
benchmarks, testing performance with varying table sizes from **1** to
**2^13** routes. The prefix matcher benchmark creates a hierarchical
structure with shelves containing multiple routes, similar to real-world
use cases.

![Route
Graph](https://github.com/user-attachments/assets/c59d6f71-c78e-4a64-840b-8898082054c1)

```
Running /build/bazel_root/base/execroot/envoy/bazel-out/k8-opt/bin/test/common/router/config_impl_speed_test
Run on (32 X 3515.38 MHz CPU s)
CPU Caches:
  L1 Data 48 KiB (x16)
  L1 Instruction 32 KiB (x16)
  L2 Unified 1280 KiB (x16)
  L3 Unified 55296 KiB (x1)
Load Average: 4.80, 3.13, 9.79
--------------------------------------------------------------------------------------
Benchmark                                            Time             CPU   Iterations
--------------------------------------------------------------------------------------
bmRouteTableSizeWithPathPrefixMatch/1              536 ns          536 ns      1298506
bmRouteTableSizeWithPathPrefixMatch/2              587 ns          587 ns      1193335
bmRouteTableSizeWithPathPrefixMatch/4              691 ns          691 ns      1013703
bmRouteTableSizeWithPathPrefixMatch/8              846 ns          846 ns       827034
bmRouteTableSizeWithPathPrefixMatch/16            1231 ns         1231 ns       568054
bmRouteTableSizeWithPathPrefixMatch/32            1886 ns         1886 ns       373329
bmRouteTableSizeWithPathPrefixMatch/64            3223 ns         3223 ns       217808
bmRouteTableSizeWithPathPrefixMatch/128           5977 ns         5977 ns       117353
bmRouteTableSizeWithPathPrefixMatch/256          11384 ns        11384 ns        61955
bmRouteTableSizeWithPathPrefixMatch/512          21993 ns        21993 ns        31855
bmRouteTableSizeWithPathPrefixMatch/1024         48251 ns        48251 ns        14673
bmRouteTableSizeWithPathPrefixMatch/2048        104355 ns       104357 ns         5524
bmRouteTableSizeWithPathPrefixMatch/4096        205814 ns       205811 ns         3408
bmRouteTableSizeWithPathPrefixMatch/8192        410113 ns       410115 ns         1712
bmRouteTableSizeWithPathPrefixMatch/16384       851624 ns       851636 ns          828
bmRouteTableSizeWithExactPathMatch/1               534 ns          534 ns      1306682
bmRouteTableSizeWithExactPathMatch/2               584 ns          584 ns      1197580
bmRouteTableSizeWithExactPathMatch/4               689 ns          689 ns      1013885
bmRouteTableSizeWithExactPathMatch/8               843 ns          843 ns       828836
bmRouteTableSizeWithExactPathMatch/16             1227 ns         1227 ns       569857
bmRouteTableSizeWithExactPathMatch/32             1884 ns         1884 ns       371227
bmRouteTableSizeWithExactPathMatch/64             3261 ns         3261 ns       212785
bmRouteTableSizeWithExactPathMatch/128            6029 ns         6029 ns       115856
bmRouteTableSizeWithExactPathMatch/256           11367 ns        11367 ns        61665
bmRouteTableSizeWithExactPathMatch/512           22134 ns        22133 ns        31785
bmRouteTableSizeWithExactPathMatch/1024          46710 ns        46710 ns        14993
bmRouteTableSizeWithExactPathMatch/2048         110910 ns       110912 ns         6379
bmRouteTableSizeWithExactPathMatch/4096         212092 ns       212089 ns         3300
bmRouteTableSizeWithExactPathMatch/8192         417258 ns       417237 ns         1680
bmRouteTableSizeWithExactPathMatch/16384        869521 ns       869518 ns          814
bmRouteTableSizeWithRegexMatch/1                   630 ns          630 ns      1111278
bmRouteTableSizeWithRegexMatch/2                   763 ns          763 ns       922242
bmRouteTableSizeWithRegexMatch/4                  1029 ns         1029 ns       678372
bmRouteTableSizeWithRegexMatch/8                  1504 ns         1504 ns       466548
bmRouteTableSizeWithRegexMatch/16                 2926 ns         2926 ns       241356
bmRouteTableSizeWithRegexMatch/32                 5111 ns         5111 ns       136253
bmRouteTableSizeWithRegexMatch/64                 9584 ns         9584 ns        72823
bmRouteTableSizeWithRegexMatch/128               19555 ns        19555 ns        35903
bmRouteTableSizeWithRegexMatch/256               38923 ns        38922 ns        17859
bmRouteTableSizeWithRegexMatch/512              105522 ns       105519 ns         6660
bmRouteTableSizeWithRegexMatch/1024             207458 ns       207455 ns         3321
bmRouteTableSizeWithRegexMatch/2048             416225 ns       416216 ns         1678
bmRouteTableSizeWithRegexMatch/4096             827044 ns       827016 ns          761
bmRouteTableSizeWithRegexMatch/8192            1704151 ns      1704030 ns          377
bmRouteTableSizeWithRegexMatch/16384           4776874 ns      4776656 ns          135
bmRouteTableSizeWithExactMatcherTree/1             737 ns          737 ns       954898
bmRouteTableSizeWithExactMatcherTree/2             732 ns          732 ns       955453
bmRouteTableSizeWithExactMatcherTree/4             737 ns          737 ns       944687
bmRouteTableSizeWithExactMatcherTree/8             733 ns          733 ns       960146
bmRouteTableSizeWithExactMatcherTree/16            753 ns          753 ns       930218
bmRouteTableSizeWithExactMatcherTree/32            747 ns          747 ns       939365
bmRouteTableSizeWithExactMatcherTree/64            754 ns          754 ns       927099
bmRouteTableSizeWithExactMatcherTree/128           742 ns          742 ns       941579
bmRouteTableSizeWithExactMatcherTree/256           738 ns          738 ns       946895
bmRouteTableSizeWithExactMatcherTree/512           751 ns          751 ns       939048
bmRouteTableSizeWithExactMatcherTree/1024          752 ns          752 ns       922761
bmRouteTableSizeWithExactMatcherTree/2048          745 ns          745 ns       938027
bmRouteTableSizeWithExactMatcherTree/4096          751 ns          751 ns       934409
bmRouteTableSizeWithExactMatcherTree/8192          756 ns          756 ns       929814
bmRouteTableSizeWithExactMatcherTree/16384         751 ns          751 ns       935359
bmRouteTableSizeWithPrefixMatcherTree/1            729 ns          729 ns       962058
bmRouteTableSizeWithPrefixMatcherTree/2            727 ns          727 ns       960017
bmRouteTableSizeWithPrefixMatcherTree/4            731 ns          731 ns       963227
bmRouteTableSizeWithPrefixMatcherTree/8            730 ns          730 ns       961697
bmRouteTableSizeWithPrefixMatcherTree/16           728 ns          728 ns       961949
bmRouteTableSizeWithPrefixMatcherTree/32           730 ns          730 ns       963114
bmRouteTableSizeWithPrefixMatcherTree/64           729 ns          729 ns       962356
bmRouteTableSizeWithPrefixMatcherTree/128          741 ns          741 ns       943679
bmRouteTableSizeWithPrefixMatcherTree/256          745 ns          745 ns       942793
bmRouteTableSizeWithPrefixMatcherTree/512          748 ns          748 ns       941176
bmRouteTableSizeWithPrefixMatcherTree/1024         747 ns          747 ns       932049
bmRouteTableSizeWithPrefixMatcherTree/2048         748 ns          748 ns       932930
bmRouteTableSizeWithPrefixMatcherTree/4096         743 ns          743 ns       937013
bmRouteTableSizeWithPrefixMatcherTree/8192         746 ns          746 ns       938400
bmRouteTableSizeWithPrefixMatcherTree/16384        756 ns          756 ns       938556
```

---

**Commit Message:** benchmark: add route matcher benchmarks for exact
and prefix match
**Additional Description:** Add additional tests in the
`config_impl_speed_test` for testing the performance of route table
evaluation using the matcher semantics.
**Risk Level:** Very Low
**Testing:** Benchmarks run successfully with varying table sizes
**Docs Changes:** N/A
**Release Notes:** N/A

---------

Signed-off-by: Rohit Agrawal <[email protected]>
  • Loading branch information
agrawroh authored Nov 12, 2024
1 parent 4fd9bb6 commit 98e399b
Showing 1 changed file with 177 additions and 0 deletions.
177 changes: 177 additions & 0 deletions test/common/router/config_impl_speed_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,125 @@ static RouteConfiguration genRouteConfig(benchmark::State& state,
return route_config;
}

/**
* Generates a route config using matcher tree semantics with n entries.
*/
static RouteConfiguration genMatcherTreeRouteConfig(benchmark::State& state) {
RouteConfiguration route_config;
VirtualHost* v_host = route_config.add_virtual_hosts();
v_host->set_name("default");
v_host->add_domains("*");

auto* matcher = v_host->mutable_matcher();
auto* matcher_tree = matcher->mutable_matcher_tree();

// Configure the input to match on the :path header
auto* input = matcher_tree->mutable_input();
input->set_name("request-headers");
auto* typed_config = input->mutable_typed_config();
typed_config->set_type_url(
"type.googleapis.com/envoy.type.matcher.v3.HttpRequestHeaderMatchInput");

// Create the exact match map
auto* exact_match_map = matcher_tree->mutable_exact_match_map();
auto* map = exact_match_map->mutable_map();

// Create n routes in the matcher tree
for (int i = 0; i < state.range(0); ++i) {
std::string path = absl::StrCat("/shelves/shelf_", i, "/route_", i);

// Create the route configuration
Route route;
auto* match = route.mutable_match();
match->set_prefix("/");

DirectResponseAction* direct_response = route.mutable_direct_response();
direct_response->set_status(200);

auto* header = route.add_request_headers_to_add();
header->mutable_header()->set_key("x-route-header");
header->mutable_header()->set_value(absl::StrCat("matcher_tree_", i));

// Create the matcher action
auto& matcher_action = (*map)[path];
matcher_action.mutable_action()->set_name("route");
matcher_action.mutable_action()->mutable_typed_config()->PackFrom(route);
}

return route_config;
}

/**
* Generates a route config using prefix matcher tree semantics with n shelf groups,
* each containing m routes.
*/
static RouteConfiguration genPrefixMatcherTreeRouteConfig(benchmark::State& state) {
RouteConfiguration route_config;
VirtualHost* v_host = route_config.add_virtual_hosts();
v_host->set_name("default");
v_host->add_domains("*");

auto* matcher = v_host->mutable_matcher();
auto* matcher_tree = matcher->mutable_matcher_tree();

// Configure the input to match on the :path header
auto* input = matcher_tree->mutable_input();
input->set_name("request-headers");
auto* typed_config = input->mutable_typed_config();
typed_config->set_type_url(
"type.googleapis.com/envoy.type.matcher.v3.HttpRequestHeaderMatchInput");

// Create the prefix match map
auto* prefix_match_map = matcher_tree->mutable_prefix_match_map();
auto* map = prefix_match_map->mutable_map();

// We'll create ``sqrt(n)`` shelves with ``sqrt(n)`` routes each to
// get n total routes
int shelf_count = static_cast<int>(std::sqrt(state.range(0)));
int routes_per_shelf = shelf_count;

// Create shelves with their routes
for (int shelf = 0; shelf < shelf_count; ++shelf) {
std::string shelf_prefix = absl::StrCat("/shelves/shelf_", shelf);

// Create a RouteList for this shelf prefix
envoy::config::route::v3::RouteList route_list;

// Add routes for this shelf
for (int route = 0; route < routes_per_shelf; ++route) {
auto* new_route = route_list.add_routes();

// Set up the route match
auto* match = new_route->mutable_match();
match->set_prefix(absl::StrCat(shelf_prefix, "/route_", route));

// Set up the route action
DirectResponseAction* direct_response = new_route->mutable_direct_response();
direct_response->set_status(200);

// Add a header
auto* header = new_route->add_request_headers_to_add();
header->mutable_header()->set_key("x-route-header");
header->mutable_header()->set_value(absl::StrCat("match_tree_", shelf, "_", route));
}

// Add a catch-all route for this shelf
auto* catch_all = route_list.add_routes();
catch_all->mutable_match()->set_prefix(shelf_prefix);
catch_all->mutable_direct_response()->set_status(200);
auto* catch_all_header = catch_all->add_request_headers_to_add();
catch_all_header->mutable_header()->set_key("x-route-header");
catch_all_header->mutable_header()->set_value(absl::StrCat("match_tree_", shelf, "_default"));

// Create the matcher action for this shelf
auto& matcher_action = (*map)[shelf_prefix];
matcher_action.mutable_action()->set_name("route");
matcher_action.mutable_action()->mutable_typed_config()->PackFrom(route_list);
}

return route_config;
}

/**
* Measure the speed of doing a route match against a route table of varying sizes.
* Why? Currently, route matching is linear in first-to-win ordering.
Expand Down Expand Up @@ -137,10 +256,68 @@ static void bmRouteTableSizeWithRegexMatch(benchmark::State& state) {
bmRouteTableSize(state, RouteMatch::PathSpecifierCase::kSafeRegex);
}

/**
* Benchmark matcher tree route matching performance with exact path matchers in the form of:
* - /shelves/shelf_1/route_1
* - /shelves/shelf_2/route_2
* - etc.
*/
static void bmRouteTableSizeWithExactMatcherTree(benchmark::State& state) {
// Setup router for benchmarking
Api::ApiPtr api = Api::createApiForTest();
NiceMock<Server::Configuration::MockServerFactoryContext> factory_context;
NiceMock<Envoy::StreamInfo::MockStreamInfo> stream_info;
ON_CALL(factory_context, api()).WillByDefault(ReturnRef(*api));

// Create router config with matcher tree
std::shared_ptr<ConfigImpl> config =
*ConfigImpl::create(genMatcherTreeRouteConfig(state), factory_context,
ProtobufMessage::getNullValidationVisitor(), true);

for (auto _ : state) {
// Match against the last route in the config
int last_route_num = state.range(0) - 1;
config->route(genRequestHeaders(last_route_num), stream_info, 0);
}
}

/**
* Benchmark matcher tree route matching performance with prefix path matchers in the form of:
* - /shelves/shelf_1/...
* - /shelves/shelf_1/...
* - ...
* - /shelves/shelf_2/...
* - /shelves/shelf_2/...
* - ...
* - etc.
*/
static void bmRouteTableSizeWithPrefixMatcherTree(benchmark::State& state) {
// Setup router for benchmarking
Api::ApiPtr api = Api::createApiForTest();
NiceMock<Server::Configuration::MockServerFactoryContext> factory_context;
NiceMock<Envoy::StreamInfo::MockStreamInfo> stream_info;
ON_CALL(factory_context, api()).WillByDefault(ReturnRef(*api));

// Create router config with matcher tree
std::shared_ptr<ConfigImpl> config =
*ConfigImpl::create(genPrefixMatcherTreeRouteConfig(state), factory_context,
ProtobufMessage::getNullValidationVisitor(), true);

for (auto _ : state) {
// Match against the last route in the last shelf
int shelf_count = static_cast<int>(std::sqrt(state.range(0)));
int last_route_num = shelf_count - 1;
config->route(genRequestHeaders(last_route_num), stream_info, 0);
}
}

BENCHMARK(bmRouteTableSizeWithPathPrefixMatch)->RangeMultiplier(2)->Ranges({{1, 2 << 13}});
BENCHMARK(bmRouteTableSizeWithExactPathMatch)->RangeMultiplier(2)->Ranges({{1, 2 << 13}});
BENCHMARK(bmRouteTableSizeWithRegexMatch)->RangeMultiplier(2)->Ranges({{1, 2 << 13}});

BENCHMARK(bmRouteTableSizeWithExactMatcherTree)->RangeMultiplier(2)->Ranges({{1, 2 << 13}});
BENCHMARK(bmRouteTableSizeWithPrefixMatcherTree)->RangeMultiplier(2)->Ranges({{1, 2 << 13}});

} // namespace
} // namespace Router
} // namespace Envoy

0 comments on commit 98e399b

Please sign in to comment.