-
Notifications
You must be signed in to change notification settings - Fork 3.7k
chore(dataobj): Improve performance of dataset.Value API #18339
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
Using dataset.Value is a sizeable portion of total query time. This commit adds benchmarks to more directly test improvements to dataset.Value.
This commit makes several small changes to improve the performance of the dataset.Value API: 1. Reduce the complexity of methods so that methods can be inlined. The previous complexity of Value.Type bubbled down to all the methods, making them hard to inline. Using error types for panics also reduces the complexity cost of the panic lines while retaining the same error messages; the cost of both string concatenation and fmt.Sprintf bubbled up the cost, further making it difficult to inline functions. 2. Use pointer receivers wherever possible for Value methods. As each Value is 28 bytes (previously 24, but still large), copying the Value on the stack to call a non-inlined method was expensive. This was seen most prominently in CompareValues, which still can't be inlined. Using pointers for CompareValues significantly improves its performance. 3. Replace use of `any` for the final field with a plain `*byte` type. This ensures that there are absolutely no allocations for that field. Some usages of the old field have been replaced with the explicit kind field. Additionally, smaller changes have been made: * All methods that mutate the state of Value have been removed, with the exception of Zero. Functionality of these methods have been pushed down to the caller. This was done to reduce the complexity of the code, but also to manually inline that logic into the plain value encoder. * Usage of cmp.Compare has been replaced with a hand-rolled compare method for integers, which is slightly less expensive due to not needing to handle the possibility of floating-point numbers. As a result of these changes, the synthetic logql/bench benchmarks show a global 2x speed improvement in queries, alongside a 4% reduction in allocations.
I made a few smaller changes since the last time I ran the benchmarks. I'm running them one final time, and I'll share the results here. |
The results are a little hard to verify using the CI, since it seems pretty inconsistent with its results today. It initially looked like this PR improved the speed by 2x, but it seems like it's probably closer to 10-15%. The correctness tests are failing too, but they are also failing on the baseline I used, so I think that can be ignored here. |
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.
lgtm
Co-authored-by: Ashwanth <iamashwanth@gmail.com>
Co-authored-by: Ashwanth <iamashwanth@gmail.com>
This PR makes several small changes to improve the performance of the dataset.Value API:
Reduce the complexity of methods so that methods can be inlined. The previous complexity of Value.Type bubbled down to all the methods, making them hard to inline.
Using error types for panics also reduces the complexity cost of the panic lines while retaining the same error messages; the cost of both string concatenation and fmt.Sprintf bubbled up the cost, further making it difficult to inline functions.
Use pointer receivers wherever possible for Value methods. As each Value is 28 bytes (previously 24, but still large), copying the Value on the stack to call a non-inlined method was expensive. This was seen most prominently in CompareValues, which still can't be inlined. Using pointers for CompareValues significantly improves its performance.
Replace use of
any
for the final field with a plain*byte
type. This ensures that there are absolutely no allocations for that field. Some usages of the old field have been replaced with the explicit kind field.Additionally, smaller changes have been made:
All methods that mutate the state of Value have been removed, with the exception of Zero. Functionality of these methods have been pushed down to the caller. This was done to reduce the complexity of the code, but also to manually inline that logic into the plain value encoder.
Usage of cmp.Compare has been replaced with a hand-rolled compare method for integers, which is slightly less expensive due to not needing to handle the possibility of floating-point numbers.
As a result of these changes, the synthetic logql/bench benchmarks show a 10-15% speed improvement in all queries.