[CASSANDRA-21535] Avoid extra copy for cached Mutation serialization#4965
Open
koo-taejin wants to merge 2 commits into
Open
[CASSANDRA-21535] Avoid extra copy for cached Mutation serialization#4965koo-taejin wants to merge 2 commits into
koo-taejin wants to merge 2 commits into
Conversation
Description In Mutation.Serializer.serialization(), we first calculate the serialized size. After that, current code serializes the mutation to a thread local DataOutputBuffer. Then unsafeToByteArray() creates a new byte array and copies all serialized bytes. The serialized size is already known. So we can create a fixed size heap buffer first and serialize directly to that buffer. This change removes one full byte array copy when we create a cached mutation serialization. I also added a check that the written size is same as the calculated serialized size. Benchmark I added a JMH benchmark for this case. For every benchmark operation: - clear cached serialization - serialize the Mutation - create cached serialization again JDK 17, arm64, 1 thread: | Value size | Before | After | Result | |--:|-:|-:|--:| | 128 B | 3.70 M ops/s | 3.87 M ops/s | +4% | | 16 KiB | 608 K ops/s | 716 K ops/s | +18% | | 256 KiB | 41.4 K ops/s | 53.1 K ops/s | +28% | | 768 KiB | 6.83 K ops/s | 18.88 K ops/s | +177% | The result is bigger for large mutations because current code writes the data to a direct buffer first, and then copies all data again to a heap byte array. With this change, serialization writes directly to the final heap byte array. This benchmark measures cache creation cost. It does not mean normal write throughput will be 2.77x faster, because normal requests can reuse the cached serialization.
Contributor
|
Could you please also share the JMH benchmark too? |
Description In Mutation.Serializer.serialization(), we first calculate the serialized size. After that, current code serializes the mutation to a thread local DataOutputBuffer. Then unsafeToByteArray() creates a new byte array and copies all serialized bytes. The serialized size is already known. So we can create a fixed size heap buffer first and serialize directly to that buffer. This change removes one full byte array copy when we create a cached mutation serialization. I also added a check that the written size is same as the calculated serialized size. Benchmark I added a JMH benchmark for this case. For every benchmark operation: - clear cached serialization - serialize the Mutation - create cached serialization again JDK 17, arm64, 1 thread: | Value size | Before | After | Result | |--:|-:|-:|--:| | 128 B | 3.70 M ops/s | 3.87 M ops/s | +4% | | 16 KiB | 608 K ops/s | 716 K ops/s | +18% | | 256 KiB | 41.4 K ops/s | 53.1 K ops/s | +28% | | 768 KiB | 6.83 K ops/s | 18.88 K ops/s | +177% | The result is bigger for large mutations because current code writes the data to a direct buffer first, and then copies all data again to a heap byte array. With this change, serialization writes directly to the final heap byte array. This benchmark measures cache creation cost. It does not mean normal write throughput will be 2.77x faster, because normal requests can reuse the cached serialization.
Contributor
Author
|
I also include the files used for the test. Sorry, I should explain the test environment and the test result more clearly. I shared the result without including enough test details. Please feel free to give me any feedback. Thank you. :) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
In Mutation.Serializer.serialization(), we first calculate the serialized size.
After that, current code serializes the mutation to a thread local DataOutputBuffer. Then unsafeToByteArray() creates a new byte array and copies all serialized bytes.
The serialized size is already known.
So we can create a fixed size heap buffer first and serialize directly to that buffer.
This change removes one full byte array copy when we create a cached mutation serialization.
I also added a check that the written size is same as the calculated serialized size.
Benchmark
I added a JMH benchmark for this case.
For every benchmark operation:
JDK 17, arm64, 1 thread:
The result is bigger for large mutations because current code writes the data to a direct buffer first, and then copies all data again to a heap byte array.
With this change, serialization writes directly to the final heap byte array.
This benchmark measures cache creation cost. It does not mean normal write throughput will be 2.77x faster, because normal requests can reuse the cached serialization.
patch by koo.taejin; reviewed by for CASSANDRA-21535
The Cassandra Jira