-
Notifications
You must be signed in to change notification settings - Fork 889
JAVA-1392: Reduce lock contention in RPTokenFactory #795
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
Conversation
|
Note to reviewers: |
|
Nevermind, after a quick benchmark, it appears that Guava's Benchmark code here. Results on my local machine: So I will push a simplified implementation shortly. |
|
I'd suggest adding a a test case in RPTokenFactoryTest.java along the lines of this: @Test(groups = "unit")
public void should_hash_consistently() {
ByteBuffer byteBuffer = Bytes.fromHexString("0xCAFEBABE");
Token tokenA = factory.hash(byteBuffer);
Token tokenB = factory.hash(byteBuffer);
assertThat(tokenA).isEqualTo(factory.fromString("59959303159920881837560881824507314222"));
assertThat(tokenA).isEqualTo(tokenB);
}I'd added this as a commit to my local dev branch before making any changes to the factory itself to ensure that any changes I did make didn't accidentally result in a different hash value being returned. |
Added thanks! |
olim7t
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me!
| ByteBuffer byteBuffer = Bytes.fromHexString("0xCAFEBABE"); | ||
| Token tokenA = factory.hash(byteBuffer); | ||
| Token tokenB = factory.hash(byteBuffer); | ||
| assertThat(tokenA).isEqualTo(factory.fromString("59959303159920881837560881824507314222")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Trivial: you could have chained the isEqualTo calls.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since it was brought up on the mailing list I updated the Benchmark to include a test using a ThreadLocal to reuse MessageDigest. So I am 👍 on the proposed implementation.
It did appear to give a slight throughput improvement over the clone implementation, but I don't think it offers enough of an improvement to justify possible issues around classloader leaks. I think we can't be certain what threads call hash since both Metadata.getReplicas() and Metadata.newToken() call it and are public API methods, so it probably isn't a good idea to create a ThreadLocal for this.
8 threads (number of cores on my local laptop):
Benchmark Mode Cnt Score Error Units
MessageDigestBenchmark.benchmarkGuavaHashing thrpt 10 10473072.107 ± 713321.359 ops/s
MessageDigestBenchmark.benchmarkMessageDigestCustomPrototype thrpt 10 12769089.493 ± 246295.850 ops/s
MessageDigestBenchmark.benchmarkMessageDigestGetInstance thrpt 10 5334760.325 ± 616163.000 ops/s
MessageDigestBenchmark.benchmarkMessageDigestThreadLocal thrpt 10 13531501.777 ± 410707.323 ops/s
4 threads (cores / 2):
Benchmark Mode Cnt Score Error Units
MessageDigestBenchmark.benchmarkGuavaHashing thrpt 10 8977372.306 ± 643934.523 ops/s
MessageDigestBenchmark.benchmarkMessageDigestCustomPrototype thrpt 10 10462846.827 ± 806385.871 ops/s
MessageDigestBenchmark.benchmarkMessageDigestGetInstance thrpt 10 7134434.241 ± 116276.480 ops/s
MessageDigestBenchmark.benchmarkMessageDigestThreadLocal thrpt 10 11669175.419 ± 867734.739 ops/s
16 threads (cores * 2):
Benchmark Mode Cnt Score Error Units
MessageDigestBenchmark.benchmarkGuavaHashing thrpt 10 11907004.499 ± 263184.075 ops/s
MessageDigestBenchmark.benchmarkMessageDigestCustomPrototype thrpt 10 12413481.943 ± 1031950.103 ops/s
MessageDigestBenchmark.benchmarkMessageDigestGetInstance thrpt 10 5652411.878 ± 467668.584 ops/s
MessageDigestBenchmark.benchmarkMessageDigestThreadLocal thrpt 10 14715318.554 ± 317316.420 ops/s
1 thread:
Benchmark Mode Cnt Score Error Units
MessageDigestBenchmark.benchmarkGuavaHashing thrpt 10 3123045.280 ± 47916.632 ops/s
MessageDigestBenchmark.benchmarkMessageDigestCustomPrototype thrpt 10 3437793.199 ± 181377.439 ops/s
MessageDigestBenchmark.benchmarkMessageDigestGetInstance thrpt 10 2856639.462 ± 56669.009 ops/s
MessageDigestBenchmark.benchmarkMessageDigestThreadLocal thrpt 10 3832478.922 ± 55020.823 ops/s
Motivation: RPTokenFactory.md5() is called for almost every query plan by TokenAwarePolicy. This method in turn calls MessageDigest.getInstance(String), which is known to create lock contentions (see https://bugs.openjdk.java.net/browse/JDK-7092821). Modification: Clone a prototype instance of MessageDigest instead of creating a new one from scratch, thus avoiding lock contention. See google/guava#1197. Result: RPTokenFactory.md5() has an improved performance as no more lock contentions arrive.
Motivation:
RPTokenFactory.md5() is called for almost every query plan by TokenAwarePolicy.
This method in turn calls MessageDigest.getInstance(String),
which is known to create lock contentions (see https://bugs.openjdk.java.net/browse/JDK-7092821).
Modification:
Clone a prototype instance of MessageDigest instead of creating a new one from scratch.
Result:
RPTokenFactory.md5() has an improved performance as no more lock contentions arrive.