Skip to content

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

Merged
merged 4 commits into from
Jul 8, 2025

Conversation

rfratto
Copy link
Member

@rfratto rfratto commented Jul 4, 2025

This PR 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 10-15% speed improvement in all queries.

rfratto added 2 commits July 4, 2025 13:58
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.
@rfratto rfratto requested a review from a team as a code owner July 4, 2025 18:31
@rfratto
Copy link
Member Author

rfratto commented Jul 4, 2025

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.

@rfratto
Copy link
Member Author

rfratto commented Jul 4, 2025

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.

Copy link
Contributor

@ashwanthgoli ashwanthgoli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

rfratto and others added 2 commits July 8, 2025 09:23
@rfratto rfratto merged commit 26cb50e into main Jul 8, 2025
65 checks passed
@rfratto rfratto deleted the dataset-value-perf branch July 8, 2025 13:40
andrejshapal pushed a commit to andrejshapal/loki that referenced this pull request Jul 8, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants