Skip to content
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

Parquetquery join performance improvement #3604

Merged
merged 5 commits into from May 6, 2024
Merged

Conversation

mdisibio
Copy link
Contributor

@mdisibio mdisibio commented Apr 23, 2024

What this PR does:
This is a redo of the join/leftjoin iterator core loops. This new loop uses the first iterator as the "driving" iterator to which we try to find match amongst all the others. Instead of peeking the tip of them all, it starts the next pass as soon as iterator 2..N don't match iterator 1. There is also a dynamic re-sort of iterators at runtime. If iterators 2...N are able to filter further into the file than iterator 1, it is swapped to the top and becomes the new iterator 1.

This has a nice performance improvement on mixed traceql queries, and metrics queries. The 20% speed on mixed queries is interesting. To dig a bit: the new loop was responsible for ~15%, and the dynamic re-sort another 5%.

BenchmarkBackendBlockTraceQL
                                              │ before.txt  │              after.txt              │
                                              │   sec/op    │    sec/op     vs base               │
BackendBlockTraceQL/complex                     1.763m ± 1%   1.746m ± 11%        ~ (p=0.180 n=6)
BackendBlockTraceQL/spanAttValNoMatch           1.852m ± 1%   1.849m ±  1%        ~ (p=1.000 n=6)
BackendBlockTraceQL/spanAttIntrinsicNoMatch     1.442m ± 0%   1.443m ±  1%        ~ (p=0.937 n=6)
BackendBlockTraceQL/resourceAttValNoMatch       1.420m ± 1%   1.403m ±  2%        ~ (p=0.180 n=6)
BackendBlockTraceQL/resourceAttIntrinsicMatch   12.60m ± 3%   12.89m ±  4%        ~ (p=0.310 n=6)
BackendBlockTraceQL/mixedValNoMatch             131.9m ± 1%   103.7m ±  1%  -21.35% (p=0.002 n=6)
BackendBlockTraceQL/mixedValMixedMatchAnd       1.432m ± 1%   1.418m ±  1%        ~ (p=0.093 n=6)
BackendBlockTraceQL/mixedValMixedMatchOr        123.9m ± 1%   101.5m ±  1%  -18.06% (p=0.002 n=6)
BackendBlockTraceQL/count                       236.8m ± 1%   225.4m ±  1%   -4.82% (p=0.002 n=6)
BackendBlockTraceQL/struct                      349.7m ± 4%   338.9m ±  4%   -3.09% (p=0.041 n=6)
BackendBlockTraceQL/||                          131.4m ± 0%   130.4m ±  1%   -0.77% (p=0.002 n=6)
BackendBlockTraceQL/mixed                       20.76m ± 3%   20.31m ±  2%   -2.17% (p=0.009 n=6)
geomean                                         16.62m        15.87m         -4.55%

                                              │  before.txt   │               after.txt               │
                                              │      B/s      │      B/s        vs base               │
BackendBlockTraceQL/complex                     1011.6Mi ± 1%   1019.5Mi ± 10%        ~ (p=0.699 n=6)
BackendBlockTraceQL/spanAttValNoMatch            1.033Gi ± 1%    1.034Gi ±  1%        ~ (p=1.000 n=6)
BackendBlockTraceQL/spanAttIntrinsicNoMatch      980.6Mi ± 0%    979.8Mi ±  1%        ~ (p=0.937 n=6)
BackendBlockTraceQL/resourceAttValNoMatch        297.0Mi ± 1%    300.5Mi ±  2%        ~ (p=0.180 n=6)
BackendBlockTraceQL/resourceAttIntrinsicMatch    1.054Gi ± 3%    1.030Gi ±  4%        ~ (p=0.310 n=6)
BackendBlockTraceQL/mixedValNoMatch              17.08Mi ± 1%    21.50Mi ±  1%  +25.85% (p=0.002 n=6)
BackendBlockTraceQL/mixedValMixedMatchAnd        303.1Mi ± 1%    305.9Mi ±  1%        ~ (p=0.093 n=6)
BackendBlockTraceQL/mixedValMixedMatchOr         123.4Mi ± 1%    150.3Mi ±  1%  +21.85% (p=0.002 n=6)
BackendBlockTraceQL/count                        58.63Mi ± 1%    61.60Mi ±  1%   +5.07% (p=0.002 n=6)
BackendBlockTraceQL/struct                       8.135Mi ± 4%    8.392Mi ±  5%   +3.17% (p=0.037 n=6)
BackendBlockTraceQL/||                           108.2Mi ± 0%    109.0Mi ±  1%   +0.78% (p=0.002 n=6)
BackendBlockTraceQL/mixed                        673.6Mi ± 3%    688.5Mi ±  2%   +2.22% (p=0.009 n=6)
geomean                                          210.3Mi         220.0Mi         +4.64%

                                              │ before.txt  │              after.txt              │
                                              │  MB_io/op   │  MB_io/op    vs base                │
BackendBlockTraceQL/complex                      1.869 ± 2%    1.868 ± 1%       ~ (p=0.621 n=6)
BackendBlockTraceQL/spanAttValNoMatch            2.054 ± 0%    2.054 ± 0%       ~ (p=1.000 n=6) ¹
BackendBlockTraceQL/spanAttIntrinsicNoMatch      1.483 ± 0%    1.483 ± 0%       ~ (p=1.000 n=6) ¹
BackendBlockTraceQL/resourceAttValNoMatch       442.0m ± 0%   442.0m ± 0%       ~ (p=1.000 n=6) ¹
BackendBlockTraceQL/resourceAttIntrinsicMatch    14.25 ± 0%    14.25 ± 0%       ~ (p=1.000 n=6) ¹
BackendBlockTraceQL/mixedValNoMatch              2.363 ± 0%    2.339 ± 0%  -1.02% (p=0.002 n=6)
BackendBlockTraceQL/mixedValMixedMatchAnd       455.0m ± 0%   455.0m ± 0%       ~ (p=1.000 n=6) ¹
BackendBlockTraceQL/mixedValMixedMatchOr         16.02 ± 0%    16.00 ± 0%  -0.12% (p=0.002 n=6)
BackendBlockTraceQL/count                        14.56 ± 0%    14.56 ± 0%       ~ (p=1.000 n=6) ¹
BackendBlockTraceQL/struct                       2.982 ± 0%    2.982 ± 0%       ~ (p=1.000 n=6) ¹
BackendBlockTraceQL/||                           14.91 ± 0%    14.91 ± 0%       ~ (p=1.000 n=6) ¹
BackendBlockTraceQL/mixed                        14.66 ± 0%    14.66 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                          3.664         3.661       -0.10%
¹ all samples are equal

                                              │  before.txt  │             after.txt              │
                                              │     B/op     │     B/op      vs base              │
BackendBlockTraceQL/complex                     1.159Mi ± 1%   1.163Mi ± 1%       ~ (p=0.240 n=6)
BackendBlockTraceQL/spanAttValNoMatch           1.554Mi ± 0%   1.554Mi ± 0%  -0.01% (p=0.002 n=6)
BackendBlockTraceQL/spanAttIntrinsicNoMatch     1.112Mi ± 0%   1.111Mi ± 0%  -0.02% (p=0.002 n=6)
BackendBlockTraceQL/resourceAttValNoMatch       1.087Mi ± 0%   1.087Mi ± 0%  -0.02% (p=0.002 n=6)
BackendBlockTraceQL/resourceAttIntrinsicMatch   1.286Mi ± 0%   1.285Mi ± 0%       ~ (p=0.240 n=6)
BackendBlockTraceQL/mixedValNoMatch             1.605Mi ± 0%   1.601Mi ± 0%  -0.25% (p=0.002 n=6)
BackendBlockTraceQL/mixedValMixedMatchAnd       1.090Mi ± 0%   1.090Mi ± 0%  -0.02% (p=0.002 n=6)
BackendBlockTraceQL/mixedValMixedMatchOr        2.037Mi ± 3%   2.033Mi ± 3%       ~ (p=0.485 n=6)
BackendBlockTraceQL/count                       240.5Mi ± 0%   240.5Mi ± 1%       ~ (p=0.394 n=6)
BackendBlockTraceQL/struct                      7.511Mi ± 5%   7.511Mi ± 3%       ~ (p=0.485 n=6)
BackendBlockTraceQL/||                          104.5Mi ± 0%   104.5Mi ± 0%       ~ (p=0.818 n=6)
BackendBlockTraceQL/mixed                       1.330Mi ± 1%   1.330Mi ± 1%       ~ (p=0.937 n=6)
geomean                                         3.414Mi        3.413Mi       -0.02%

                                              │ before.txt  │             after.txt             │
                                              │  allocs/op  │  allocs/op   vs base              │
BackendBlockTraceQL/complex                     17.81k ± 0%   17.80k ± 0%  -0.04% (p=0.002 n=6)
BackendBlockTraceQL/spanAttValNoMatch           17.46k ± 0%   17.45k ± 0%  -0.04% (p=0.002 n=6)
BackendBlockTraceQL/spanAttIntrinsicNoMatch     17.38k ± 0%   17.38k ± 0%  -0.03% (p=0.002 n=6)
BackendBlockTraceQL/resourceAttValNoMatch       17.41k ± 0%   17.41k ± 0%  -0.03% (p=0.002 n=6)
BackendBlockTraceQL/resourceAttIntrinsicMatch   19.11k ± 0%   19.11k ± 0%  -0.03% (p=0.002 n=6)
BackendBlockTraceQL/mixedValNoMatch             18.25k ± 0%   18.21k ± 0%  -0.24% (p=0.002 n=6)
BackendBlockTraceQL/mixedValMixedMatchAnd       17.45k ± 0%   17.45k ± 0%  -0.04% (p=0.002 n=6)
BackendBlockTraceQL/mixedValMixedMatchOr        24.05k ± 0%   24.01k ± 0%  -0.17% (p=0.002 n=6)
BackendBlockTraceQL/count                       1.408M ± 0%   1.408M ± 0%       ~ (p=0.788 n=6)
BackendBlockTraceQL/struct                      39.00k ± 8%   38.99k ± 4%       ~ (p=0.169 n=6)
BackendBlockTraceQL/||                          630.3k ± 0%   630.3k ± 0%  -0.00% (p=0.004 n=6)
BackendBlockTraceQL/mixed                       20.07k ± 0%   20.04k ± 0%  -0.14% (p=0.002 n=6)
geomean                                         38.17k        38.15k       -0.07%
BenchmarkBackendBlockQueryRange
                                                                          │ before_range.txt │      after_range_allpass.txt       │
                                                                          │      sec/op      │   sec/op     vs base               │
BackendBlockQueryRange/{}_|_rate()/7                                             368.1m ± 3%   333.3m ± 4%  -9.45% (p=0.000 n=10)
BackendBlockQueryRange/{}_|_rate()_by_(name)/7                                  1008.2m ± 2%   986.7m ± 2%  -2.13% (p=0.011 n=10)
BackendBlockQueryRange/{}_|_rate()_by_(resource.service.name)/7                  813.6m ± 1%   759.8m ± 2%  -6.62% (p=0.000 n=10)
BackendBlockQueryRange/{}_|_rate()_by_(span.http.url)/7                           1.178 ± 4%    1.124 ± 3%  -4.60% (p=0.000 n=10)
BackendBlockQueryRange/{resource.service.name=`loki-ingester`}_|_rate()/7        188.6m ± 5%   178.9m ± 7%  -5.14% (p=0.023 n=10)
BackendBlockQueryRange/{status=error}_|_rate()/7                                 113.2m ± 1%   112.8m ± 0%       ~ (p=0.247 n=10)
geomean                                                                          443.4m        422.2m       -4.76%

                                                                          │ before_range.txt │       after_range_allpass.txt       │
                                                                          │     MB_IO/op     │  MB_IO/op   vs base                 │
BackendBlockQueryRange/{}_|_rate()/7                                              24.42 ± 0%   24.42 ± 0%       ~ (p=1.000 n=10) ¹
BackendBlockQueryRange/{}_|_rate()_by_(name)/7                                    30.06 ± 0%   30.06 ± 0%       ~ (p=1.000 n=10) ¹
BackendBlockQueryRange/{}_|_rate()_by_(resource.service.name)/7                   24.72 ± 0%   24.72 ± 0%       ~ (p=1.000 n=10) ¹
BackendBlockQueryRange/{}_|_rate()_by_(span.http.url)/7                           26.74 ± 0%   26.74 ± 0%       ~ (p=1.000 n=10) ¹
BackendBlockQueryRange/{resource.service.name=`loki-ingester`}_|_rate()/7         24.72 ± 0%   24.72 ± 0%       ~ (p=1.000 n=10) ¹
BackendBlockQueryRange/{status=error}_|_rate()/7                                  25.69 ± 0%   25.69 ± 0%       ~ (p=1.000 n=10) ¹
geomean                                                                           25.99        25.99       +0.00%
¹ all samples are equal

                                                                          │ before_range.txt │       after_range_allpass.txt        │
                                                                          │     spans/op     │  spans/op    vs base                 │
BackendBlockQueryRange/{}_|_rate()/7                                             2.638M ± 0%   2.638M ± 0%       ~ (p=1.000 n=10) ¹
BackendBlockQueryRange/{}_|_rate()_by_(name)/7                                   2.638M ± 0%   2.638M ± 0%       ~ (p=1.000 n=10) ¹
BackendBlockQueryRange/{}_|_rate()_by_(resource.service.name)/7                  2.638M ± 0%   2.638M ± 0%       ~ (p=1.000 n=10) ¹
BackendBlockQueryRange/{}_|_rate()_by_(span.http.url)/7                          2.638M ± 0%   2.638M ± 0%       ~ (p=1.000 n=10) ¹
BackendBlockQueryRange/{resource.service.name=`loki-ingester`}_|_rate()/7        857.7k ± 0%   857.7k ± 0%       ~ (p=1.000 n=10) ¹
BackendBlockQueryRange/{status=error}_|_rate()/7                                 50.27k ± 0%   50.27k ± 0%       ~ (p=1.000 n=10) ¹
geomean                                                                          1.131M        1.131M       +0.00%
¹ all samples are equal

                                                                          │ before_range.txt │       after_range_allpass.txt       │
                                                                          │     spans/s      │   spans/s    vs base                │
BackendBlockQueryRange/{}_|_rate()/7                                             7.169M ± 3%   7.916M ± 4%  +10.43% (p=0.000 n=10)
BackendBlockQueryRange/{}_|_rate()_by_(name)/7                                   2.617M ± 2%   2.674M ± 2%   +2.18% (p=0.011 n=10)
BackendBlockQueryRange/{}_|_rate()_by_(resource.service.name)/7                  3.243M ± 1%   3.473M ± 2%   +7.09% (p=0.000 n=10)
BackendBlockQueryRange/{}_|_rate()_by_(span.http.url)/7                          2.239M ± 4%   2.347M ± 3%   +4.83% (p=0.000 n=10)
BackendBlockQueryRange/{resource.service.name=`loki-ingester`}_|_rate()/7        4.547M ± 5%   4.793M ± 7%   +5.42% (p=0.023 n=10)
BackendBlockQueryRange/{status=error}_|_rate()/7                                 444.0k ± 1%   445.6k ± 0%        ~ (p=0.247 n=10)
geomean                                                                          2.550M        2.678M        +5.00%

                                                                          │ before_range.txt │       after_range_allpass.txt        │
                                                                          │       B/op       │     B/op       vs base               │
BackendBlockQueryRange/{}_|_rate()/7                                           24.22Mi ±  0%   23.55Mi ±  3%  -2.75% (p=0.000 n=10)
BackendBlockQueryRange/{}_|_rate()_by_(name)/7                                 28.08Mi ± 16%   23.70Mi ± 19%       ~ (p=0.280 n=10)
BackendBlockQueryRange/{}_|_rate()_by_(resource.service.name)/7                22.99Mi ±  0%   22.99Mi ±  0%       ~ (p=0.184 n=10)
BackendBlockQueryRange/{}_|_rate()_by_(span.http.url)/7                        177.5Mi ± 20%   144.8Mi ± 29%       ~ (p=0.579 n=10)
BackendBlockQueryRange/{resource.service.name=`loki-ingester`}_|_rate()/7      23.65Mi ±  3%   23.07Mi ±  2%       ~ (p=0.067 n=10)
BackendBlockQueryRange/{status=error}_|_rate()/7                               20.91Mi ±  0%   20.89Mi ±  0%  -0.10% (p=0.043 n=10)
geomean                                                                        33.34Mi         31.05Mi        -6.87%

                                                                          │ before_range.txt │       after_range_allpass.txt       │
                                                                          │    allocs/op     │  allocs/op    vs base               │
BackendBlockQueryRange/{}_|_rate()/7                                            326.5k ±  0%   326.5k ±  0%  -0.00% (p=0.000 n=10)
BackendBlockQueryRange/{}_|_rate()_by_(name)/7                                  339.7k ±  2%   334.1k ±  2%  -1.65% (p=0.023 n=10)
BackendBlockQueryRange/{}_|_rate()_by_(resource.service.name)/7                 327.4k ±  0%   327.4k ±  0%  -0.00% (p=0.000 n=10)
BackendBlockQueryRange/{}_|_rate()_by_(span.http.url)/7                         901.1k ± 19%   733.1k ± 27%       ~ (p=0.197 n=10)
BackendBlockQueryRange/{resource.service.name=`loki-ingester`}_|_rate()/7       303.8k ±  0%   303.8k ±  0%  -0.00% (p=0.000 n=10)
BackendBlockQueryRange/{status=error}_|_rate()/7                                300.5k ±  0%   300.5k ±  0%  -0.00% (p=0.003 n=10)
geomean                                                                         379.5k         365.6k        -3.65%

Which issue(s) this PR fixes:
Fixes n/a

Checklist

  • Tests updated
  • Documentation added
  • CHANGELOG.md updated - the order of entries should be [CHANGE], [FEATURE], [ENHANCEMENT], [BUGFIX]

Copy link
Member

@joe-elliott joe-elliott left a comment

Choose a reason for hiding this comment

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

it would be easy to go down a rabbit hole on this change. i include a suggestion on line 1405. i was also wondering what happens if we force seek all iters once and then do an initial sort by distance into the file.

dunno.

sometimes it's best just to merge a simple and powerful thing w/o turning over every stone. nice improvement!

pkg/parquetquery/iters.go Show resolved Hide resolved
@mdisibio
Copy link
Contributor Author

mdisibio commented May 6, 2024

i was also wondering what happens if we force seek all iters once and then do an initial sort by distance into the file.

Yes, I did experiment with that and sorting once after first peek (no dynamic reordering) wasn't beneficial. It was either neutral or slightly worse depending on the query. I think basically it's too brittle and has a high chance of getting locked into a poor pattern from the first matches. Combining both (sort on first peek and in the loop) didn't help but also logically it wasn't expected to since the initial sort is immediately overwritten.

@mdisibio mdisibio merged commit 174b428 into grafana:main May 6, 2024
14 checks passed
joe-elliott pushed a commit to joe-elliott/tempo that referenced this pull request May 7, 2024
* change for loop

* Dynamic reordering of iterators

* fix regression on complex query by peeking all iters at start and exiting early

* Add new complex query test

* move test
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants