diff --git a/source/common/network/lc_trie.h b/source/common/network/lc_trie.h index 3670eedd7c33..3dd7413b63cd 100644 --- a/source/common/network/lc_trie.h +++ b/source/common/network/lc_trie.h @@ -445,19 +445,13 @@ template class LcTrie { ip_prefixes_ = data; std::sort(ip_prefixes_.begin(), ip_prefixes_.end()); - // In theory, the trie_ vector can have at most twice the number of ip_prefixes entries - 1. - // However, due to the fill factor a buffer is added to the size of the - // trie_. The buffer value(2000000) is reused from the reference implementation in - // http://www.csc.kth.se/~snilsson/software/router/C/trie.c. - // TODO(ccaraman): Define a better buffer value when resizing the trie_. - maximum_trie_node_size = 2 * ip_prefixes_.size() + 2000000; - trie_.resize(maximum_trie_node_size); - // Build the trie_. + trie_.reserve(static_cast(ip_prefixes_.size() / fill_factor_)); uint32_t next_free_index = 1; buildRecursive(0u, 0u, ip_prefixes_.size(), 0u, next_free_index); // The value of next_free_index is the final size of the trie_. + ASSERT(next_free_index <= trie_.size()); trie_.resize(next_free_index); trie_.shrink_to_fit(); } @@ -575,21 +569,23 @@ template class LcTrie { */ void buildRecursive(uint32_t prefix, uint32_t first, uint32_t n, uint32_t position, uint32_t& next_free_index) { - // Setting a leaf, the branch and skip are 0. - if (n == 1) { + if (position >= trie_.size()) { // There is no way to predictably determine the number of trie nodes required to build a // LC-Trie. If while building the trie the position that is being set exceeds the maximum // number of supported trie_ entries, throw an Envoy Exception. - if (position >= maximum_trie_node_size) { + if (position >= MaxLcTrieNodes) { // Adding 1 to the position to count how many nodes are trying to be set. throw EnvoyException( fmt::format("The number of internal nodes required for the LC-Trie " "exceeded the maximum number of " "supported nodes. Minimum number of internal nodes required: " "'{0}'. Maximum number of supported nodes: '{1}'.", - (position + 1), maximum_trie_node_size)); + (position + 1), MaxLcTrieNodes)); } - + trie_.resize(position + 1); + } + // Setting a leaf, the branch and skip are 0. + if (n == 1) { trie_[position].address_ = first; return; } @@ -682,10 +678,6 @@ template class LcTrie { uint32_t address_ : 20; // If this 20-bit size changes, please change MaxLcTrieNodes too. }; - // During build(), an estimate of the number of nodes required will be made and set this - // value. This is used to ensure no out_of_range exception is thrown. - uint32_t maximum_trie_node_size; - // The CIDR range and data needs to be maintained separately from the LC-Trie. A LC-Trie skips // chunks of data while searching for a match. This means that the node found in the LC-Trie // is not guaranteed to have the IP address in range. The last step prior to returning diff --git a/test/common/network/lc_trie_speed_test.cc b/test/common/network/lc_trie_speed_test.cc index e859a56bc3ac..95136c37a709 100644 --- a/test/common/network/lc_trie_speed_test.cc +++ b/test/common/network/lc_trie_speed_test.cc @@ -7,14 +7,55 @@ namespace { std::vector addresses; +std::vector>> tag_data; + +std::vector>> + tag_data_nested_prefixes; + +std::vector>> + tag_data_minimal; + std::unique_ptr> lc_trie; std::unique_ptr> lc_trie_nested_prefixes; +std::unique_ptr> lc_trie_minimal; + } // namespace namespace Envoy { +static void BM_LcTrieConstruct(benchmark::State& state) { + std::unique_ptr> trie; + for (auto _ : state) { + trie = std::make_unique>(tag_data); + } + benchmark::DoNotOptimize(trie); +} + +BENCHMARK(BM_LcTrieConstruct); + +static void BM_LcTrieConstructNested(benchmark::State& state) { + std::unique_ptr> trie; + for (auto _ : state) { + trie = std::make_unique>(tag_data_nested_prefixes); + } + benchmark::DoNotOptimize(trie); +} + +BENCHMARK(BM_LcTrieConstructNested); + +static void BM_LcTrieConstructMinimal(benchmark::State& state) { + + std::unique_ptr> trie; + for (auto _ : state) { + trie = std::make_unique>(tag_data_minimal); + } + benchmark::DoNotOptimize(trie); +} + +BENCHMARK(BM_LcTrieConstructMinimal); + static void BM_LcTrieLookup(benchmark::State& state) { static size_t i = 0; size_t output_tags = 0; @@ -41,6 +82,19 @@ static void BM_LcTrieLookupWithNestedPrefixes(benchmark::State& state) { BENCHMARK(BM_LcTrieLookupWithNestedPrefixes); +static void BM_LcTrieLookupMinimal(benchmark::State& state) { + static size_t i = 0; + size_t output_tags = 0; + for (auto _ : state) { + i++; + i %= addresses.size(); + output_tags += lc_trie_minimal->getData(addresses[i]).size(); + } + benchmark::DoNotOptimize(output_tags); +} + +BENCHMARK(BM_LcTrieLookupMinimal); + } // namespace Envoy // Boilerplate main(), which discovers benchmarks in the same file and runs them. @@ -54,7 +108,10 @@ int main(int argc, char** argv) { addresses.push_back(Envoy::Network::Utility::parseInternetAddress(address)); } - std::vector>> tag_data; + // Construct three sets of prefixes: one consisting of 1,024 addresses in an + // RFC 5737 netblock, another consisting of those same addresses plus + // 0.0.0.0/0 (to exercise the LC Trie's support for nested prefixes), + // and finally a set containing only 0.0.0.0/0. for (int i = 0; i < 32; i++) { for (int j = 0; j < 32; j++) { tag_data.emplace_back(std::pair>( @@ -62,11 +119,18 @@ int main(int argc, char** argv) { {Envoy::Network::Address::CidrRange::create(fmt::format("192.0.{}.{}/32", i, j))}})); } } + tag_data_nested_prefixes = tag_data; + tag_data_nested_prefixes.emplace_back( + std::pair>( + {"tag_0", {Envoy::Network::Address::CidrRange::create("0.0.0.0/0")}})); + tag_data_minimal.emplace_back( + std::pair>( + {"tag_1", {Envoy::Network::Address::CidrRange::create("0.0.0.0/0")}})); lc_trie = std::make_unique>(tag_data); - tag_data.emplace_back(std::pair>( - {"tag_0", {Envoy::Network::Address::CidrRange::create("0.0.0.0/0")}})); - lc_trie_nested_prefixes = std::make_unique>(tag_data); + lc_trie_nested_prefixes = + std::make_unique>(tag_data_nested_prefixes); + lc_trie_minimal = std::make_unique>(tag_data_minimal); benchmark::Initialize(&argc, argv); if (benchmark::ReportUnrecognizedArguments(argc, argv)) {