Skip to content

Commit 6c5e95d

Browse files
Brian Eckermeta-codesync[bot]
authored andcommitted
Optimize RendezvousHashFunc::operator() to avoid heap construction
Reviewed By: stuclar Differential Revision: D95628434 fbshipit-source-id: a193cd061d49339ff7b1e090f2b7390ff7e0f67f
1 parent 65e9297 commit 6c5e95d

File tree

4 files changed

+91
-3
lines changed

4 files changed

+91
-3
lines changed

mcrouter/lib/RendezvousHashFunc.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,27 @@ std::vector<RendezvousIterator::ScoreAndIndex> get_scores(
4444
}
4545
} // namespace
4646

47+
// O(N) max-tracking loop instead of building a full heap via begin().
48+
// Similar structure to WeightedRendezvousHashFunc::operator(), but uses the
49+
// raw hash value as the score rather than the weighted inverse-log transform
50+
// (weight * 1/(-log(U))). Both select the same winner when weights are uniform,
51+
// but this avoids the log/division cost. Tie-breaking (higher index wins) is
52+
// kept to match the priority_queue-based iterator path in begin().
53+
size_t RendezvousHashFunc::operator()(folly::StringPiece key) const {
54+
const uint64_t keyHash = RendezvousIterator::keyHash(key);
55+
double maxScore = -1.0;
56+
size_t maxIndex = 0;
57+
for (size_t i = 0; i < endpointHashes_.size(); ++i) {
58+
double score =
59+
static_cast<double>(hash128to64(endpointHashes_[i], keyHash));
60+
if (score > maxScore || (score == maxScore && i > maxIndex)) {
61+
maxScore = score;
62+
maxIndex = i;
63+
}
64+
}
65+
return maxIndex;
66+
}
67+
4768
RendezvousIterator RendezvousHashFunc::begin(folly::StringPiece key) const {
4869
return RendezvousIterator(get_scores(endpointHashes_, key));
4970
}

mcrouter/lib/RendezvousHashFunc.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,7 @@ class RendezvousHashFunc {
3737
const std::vector<folly::StringPiece>& endpoints,
3838
const folly::dynamic& json);
3939

40-
size_t operator()(folly::StringPiece key) const {
41-
return *begin(key);
42-
}
40+
size_t operator()(folly::StringPiece key) const;
4341

4442
RendezvousIterator begin(folly::StringPiece key) const;
4543

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#include <folly/Benchmark.h>
9+
#include <folly/Range.h>
10+
#include <folly/init/Init.h>
11+
#include <folly/json/dynamic.h>
12+
13+
#include "mcrouter/lib/RendezvousHashFunc.h"
14+
15+
constexpr folly::StringPiece kKey =
16+
"someKey_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+_sdkfjsdfksjdfasdfaksxxx";
17+
18+
using facebook::memcache::RendezvousHashFunc;
19+
20+
namespace {
21+
22+
std::vector<folly::StringPiece> makeEndpoints(size_t n) {
23+
static std::vector<std::string> storage;
24+
storage.clear();
25+
storage.reserve(n);
26+
for (size_t i = 0; i < n; ++i) {
27+
storage.push_back("endpoint" + std::to_string(i) + ".example.com:1234");
28+
}
29+
std::vector<folly::StringPiece> endpoints;
30+
endpoints.reserve(n);
31+
for (const auto& s : storage) {
32+
endpoints.emplace_back(s);
33+
}
34+
return endpoints;
35+
}
36+
37+
} // namespace
38+
39+
void rendezvousHashBench(size_t iters, size_t poolSize) {
40+
RendezvousHashFunc func(
41+
std::vector<folly::StringPiece>(), folly::dynamic::object);
42+
BENCHMARK_SUSPEND {
43+
auto endpoints = makeEndpoints(poolSize);
44+
func = RendezvousHashFunc(endpoints, folly::dynamic::object);
45+
}
46+
for (size_t i = 0; i < iters; ++i) {
47+
folly::doNotOptimizeAway(func(kKey));
48+
}
49+
}
50+
51+
BENCHMARK_NAMED_PARAM(rendezvousHashBench, pool_10, 10)
52+
BENCHMARK_NAMED_PARAM(rendezvousHashBench, pool_100, 100)
53+
BENCHMARK_NAMED_PARAM(rendezvousHashBench, pool_500, 500)
54+
BENCHMARK_NAMED_PARAM(rendezvousHashBench, pool_1000, 1000)
55+
56+
int main(int argc, char** argv) {
57+
const folly::Init init(&argc, &argv, true);
58+
59+
folly::runBenchmarks();
60+
return 0;
61+
}

mcrouter/lib/test/RendezvousHashTest.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,14 @@ TEST(RendezvousHashFunc, rendezvous_10) {
7373
{947, 1026, 1028, 981, 1016, 970, 1013, 939, 1023, 1057}));
7474
}
7575

76+
TEST(RendezvousHashFunc, operator_matches_begin) {
77+
auto func = genRendezvousHashFunc(343);
78+
for (size_t i = 0; i < 10000; ++i) {
79+
auto key = "mykey:" + folly::to<std::string>(i);
80+
EXPECT_EQ(func(key), *func.begin(key));
81+
}
82+
}
83+
7684
TEST(RendezvousHashFunc, rendezvous_rehash) {
7785
const uint32_t n = 499;
7886
auto combined = test::genEndpoints(n);

0 commit comments

Comments
 (0)