Skip to content

Conversation

charleskorn
Copy link
Contributor

@charleskorn charleskorn commented Apr 11, 2025

What this PR does

This PR implements common subexpression elimination in MQE.

Prior to this PR, when given an expression like sum(foo) / (sum(foo) + sum(bar)), MQE would evaluate sum(foo) twice. This is not necessary: we can instead compute sum(foo) once and use the result in both places, and that's what this PR implements.

This improves overall query performance, and also saves CPU time in queriers, ingesters and store-gateways. However, the drawback is that queriers must buffer unused parts of common results until they are consumed by all places that need them. For queries where the size of the common result is small, this is not an issue, but in other cases this can increase the peak memory consumption of queries significantly.

This could be addressed somewhat by consuming from both sides of binary operations concurrently, but this is out of scope for this PR - for now, given the substantial performance improvements and relative cost of CPU and memory, we can provision more memory for queriers if needed. In any case, query memory consumption will be no worse than Prometheus' engine.

Benchmark results

Overall, latency improves roughly in line with the ratio of eliminated selectors to original selectors. (For example, if there were two selectors originally, and they're both the same, then latency improves around 50%, or if there were three selectors and two are the same, then latency improves around 33%.)

goos: darwin
goarch: arm64
pkg: github.com/grafana/mimir/pkg/streamingpromql/benchmarks
cpu: Apple M1 Pro
                                                                                │    Mimir     │        MimirWithQueryPlanner         │
                                                                                │    sec/op    │    sec/op      vs base               │
Query/sum(a_1),_instant_query-10                                                   144.7µ ± 3%    146.8µ ±  1%        ~ (p=0.180 n=6)
Query/sum(a_1),_range_query_with_100_steps-10                                      149.8µ ± 1%    153.6µ ±  2%   +2.51% (p=0.004 n=6)
Query/sum(a_1),_range_query_with_1000_steps-10                                     202.9µ ± 1%    208.0µ ±  0%   +2.47% (p=0.002 n=6)
Query/sum(a_100),_instant_query-10                                                 757.4µ ± 1%    768.9µ ±  2%   +1.52% (p=0.015 n=6)
Query/sum(a_100),_range_query_with_100_steps-10                                    1.270m ± 1%    1.273m ±  1%        ~ (p=0.394 n=6)
Query/sum(a_100),_range_query_with_1000_steps-10                                   5.541m ± 1%    5.538m ±  1%        ~ (p=0.937 n=6)
Query/sum(a_2000),_instant_query-10                                                9.997m ± 2%   10.067m ±  1%        ~ (p=0.132 n=6)
Query/sum(a_2000),_range_query_with_100_steps-10                                   19.41m ± 1%    19.46m ±  1%        ~ (p=0.394 n=6)
Query/sum(a_2000),_range_query_with_1000_steps-10                                  100.0m ± 1%    100.3m ±  1%        ~ (p=0.485 n=6)
Query/a_1_+_a_1,_instant_query-10                                                  273.4µ ± 1%    150.8µ ±  1%  -44.85% (p=0.002 n=6)
Query/a_1_+_a_1,_range_query_with_100_steps-10                                     286.9µ ± 2%    159.1µ ±  1%  -44.54% (p=0.002 n=6)
Query/a_1_+_a_1,_range_query_with_1000_steps-10                                    397.2µ ± 2%    219.3µ ±  1%  -44.79% (p=0.002 n=6)
Query/a_100_+_a_100,_instant_query-10                                             1482.5µ ± 1%    814.3µ ±  1%  -45.07% (p=0.002 n=6)
Query/a_100_+_a_100,_range_query_with_100_steps-10                                 2.549m ± 1%    1.414m ±  1%  -44.53% (p=0.002 n=6)
Query/a_100_+_a_100,_range_query_with_1000_steps-10                               11.587m ± 5%    6.552m ±  2%  -43.45% (p=0.002 n=6)
Query/a_2000_+_a_2000,_instant_query-10                                            21.76m ± 1%    11.82m ±  1%  -45.69% (p=0.002 n=6)
Query/a_2000_+_a_2000,_range_query_with_100_steps-10                               41.85m ± 3%    22.82m ±  1%  -45.47% (p=0.002 n=6)
Query/a_2000_+_a_2000,_range_query_with_1000_steps-10                              212.6m ± 1%    118.7m ±  1%  -44.16% (p=0.002 n=6)
Query/sum(a_1)_+_sum(a_1),_instant_query-10                                        283.2µ ± 1%    155.7µ ±  2%  -45.00% (p=0.002 n=6)
Query/sum(a_1)_+_sum(a_1),_range_query_with_100_steps-10                           306.8µ ± 3%    165.8µ ±  1%  -45.95% (p=0.002 n=6)
Query/sum(a_1)_+_sum(a_1),_range_query_with_1000_steps-10                          412.5µ ± 0%    230.0µ ±  2%  -44.23% (p=0.002 n=6)
Query/sum(a_100)_+_sum(a_100),_instant_query-10                                   1505.3µ ± 3%    785.8µ ±  3%  -47.80% (p=0.002 n=6)
Query/sum(a_100)_+_sum(a_100),_range_query_with_100_steps-10                       2.503m ± 3%    1.300m ±  2%  -48.05% (p=0.002 n=6)
Query/sum(a_100)_+_sum(a_100),_range_query_with_1000_steps-10                     11.011m ± 3%    5.704m ±  4%  -48.20% (p=0.002 n=6)
Query/sum(a_2000)_+_sum(a_2000),_instant_query-10                                  20.50m ± 2%    10.29m ±  4%  -49.79% (p=0.002 n=6)
Query/sum(a_2000)_+_sum(a_2000),_range_query_with_100_steps-10                     39.49m ± 5%    19.69m ±  1%  -50.14% (p=0.002 n=6)
Query/sum(a_2000)_+_sum(a_2000),_range_query_with_1000_steps-10                    201.0m ± 5%    100.3m ±  1%  -50.09% (p=0.002 n=6)
Query/max(a_1)_-_min(a_1),_instant_query-10                                        277.6µ ± 1%    156.0µ ±  3%  -43.80% (p=0.002 n=6)
Query/max(a_1)_-_min(a_1),_range_query_with_100_steps-10                           291.1µ ± 3%    164.7µ ±  1%  -43.40% (p=0.002 n=6)
Query/max(a_1)_-_min(a_1),_range_query_with_1000_steps-10                          408.8µ ± 0%    232.7µ ±  1%  -43.09% (p=0.002 n=6)
Query/max(a_100)_-_min(a_100),_instant_query-10                                   1448.0µ ± 1%    796.8µ ±  1%  -44.98% (p=0.002 n=6)
Query/max(a_100)_-_min(a_100),_range_query_with_100_steps-10                       2.461m ± 1%    1.361m ±  1%  -44.69% (p=0.002 n=6)
Query/max(a_100)_-_min(a_100),_range_query_with_1000_steps-10                     11.058m ± 0%    6.083m ±  0%  -44.99% (p=0.002 n=6)
Query/max(a_2000)_-_min(a_2000),_instant_query-10                                  20.21m ± 1%    10.49m ±  1%  -48.10% (p=0.002 n=6)
Query/max(a_2000)_-_min(a_2000),_range_query_with_100_steps-10                     38.97m ± 2%    20.53m ±  2%  -47.31% (p=0.002 n=6)
Query/max(a_2000)_-_min(a_2000),_range_query_with_1000_steps-10                    203.3m ± 0%    108.4m ±  0%  -46.68% (p=0.002 n=6)
Query/a_1_/_(a_1_+_b_1),_instant_query-10                                          399.9µ ± 2%    282.9µ ±  2%  -29.26% (p=0.002 n=6)
Query/a_1_/_(a_1_+_b_1),_range_query_with_100_steps-10                             422.6µ ± 1%    297.5µ ±  1%  -29.60% (p=0.002 n=6)
Query/a_1_/_(a_1_+_b_1),_range_query_with_1000_steps-10                            588.6µ ± 1%    419.6µ ±  3%  -28.72% (p=0.002 n=6)
Query/a_100_/_(a_100_+_b_100),_instant_query-10                                    2.229m ± 1%    1.571m ±  1%  -29.53% (p=0.002 n=6)
Query/a_100_/_(a_100_+_b_100),_range_query_with_100_steps-10                       3.822m ± 1%    2.723m ±  0%  -28.76% (p=0.002 n=6)
Query/a_100_/_(a_100_+_b_100),_range_query_with_1000_steps-10                      17.64m ± 0%    12.68m ±  0%  -28.12% (p=0.002 n=6)
Query/a_2000_/_(a_2000_+_b_2000),_instant_query-10                                 34.20m ± 1%    22.96m ±  1%  -32.87% (p=0.002 n=6)
Query/a_2000_/_(a_2000_+_b_2000),_range_query_with_100_steps-10                    64.82m ± 2%    45.91m ±  3%  -29.17% (p=0.002 n=6)
Query/a_2000_/_(a_2000_+_b_2000),_range_query_with_1000_steps-10                   326.8m ± 1%    235.0m ±  0%  -28.09% (p=0.002 n=6)
Query/sum(a_1)_/_(sum(a_1)_+_sum(b_1)),_instant_query-10                           407.8µ ± 1%    289.6µ ±  1%  -28.99% (p=0.002 n=6)
Query/sum(a_1)_/_(sum(a_1)_+_sum(b_1)),_range_query_with_100_steps-10              427.1µ ± 1%    302.4µ ±  1%  -29.20% (p=0.002 n=6)
Query/sum(a_1)_/_(sum(a_1)_+_sum(b_1)),_range_query_with_1000_steps-10             611.2µ ± 1%    433.1µ ± 14%  -29.14% (p=0.002 n=6)
Query/sum(a_100)_/_(sum(a_100)_+_sum(b_100)),_instant_query-10                     2.280m ± 2%    1.520m ±  4%  -33.36% (p=0.002 n=6)
Query/sum(a_100)_/_(sum(a_100)_+_sum(b_100)),_range_query_with_100_steps-10        3.636m ± 1%    2.469m ±  2%  -32.09% (p=0.002 n=6)
Query/sum(a_100)_/_(sum(a_100)_+_sum(b_100)),_range_query_with_1000_steps-10       16.08m ± 1%    11.11m ±  3%  -30.90% (p=0.002 n=6)
Query/sum(a_2000)_/_(sum(a_2000)_+_sum(b_2000)),_instant_query-10                  31.07m ± 7%    20.65m ±  5%  -33.54% (p=0.002 n=6)
Query/sum(a_2000)_/_(sum(a_2000)_+_sum(b_2000)),_range_query_with_100_steps-10     60.15m ± 2%    38.97m ±  2%  -35.21% (p=0.002 n=6)
Query/sum(a_2000)_/_(sum(a_2000)_+_sum(b_2000)),_range_query_with_1000_steps-10    300.0m ± 0%    200.4m ±  1%  -33.20% (p=0.002 n=6)
Query/min(a_1)_/_(max(a_1)_+_max(b_1)),_instant_query-10                           410.2µ ± 1%    293.5µ ±  1%  -28.43% (p=0.002 n=6)
Query/min(a_1)_/_(max(a_1)_+_max(b_1)),_range_query_with_100_steps-10              429.2µ ± 2%    309.6µ ±  2%  -27.85% (p=0.002 n=6)
Query/min(a_1)_/_(max(a_1)_+_max(b_1)),_range_query_with_1000_steps-10             615.3µ ± 1%    443.6µ ±  2%  -27.90% (p=0.002 n=6)
Query/min(a_100)_/_(max(a_100)_+_max(b_100)),_instant_query-10                     2.177m ± 1%    1.517m ±  1%  -30.33% (p=0.002 n=6)
Query/min(a_100)_/_(max(a_100)_+_max(b_100)),_range_query_with_100_steps-10        3.659m ± 1%    2.568m ±  5%  -29.83% (p=0.002 n=6)
Query/min(a_100)_/_(max(a_100)_+_max(b_100)),_range_query_with_1000_steps-10       16.52m ± 1%    11.54m ±  1%  -30.15% (p=0.002 n=6)
Query/min(a_2000)_/_(max(a_2000)_+_max(b_2000)),_instant_query-10                  29.94m ± 1%    20.97m ±  1%  -29.97% (p=0.002 n=6)
Query/min(a_2000)_/_(max(a_2000)_+_max(b_2000)),_range_query_with_100_steps-10     60.46m ± 1%    40.88m ±  2%  -32.38% (p=0.002 n=6)
Query/min(a_2000)_/_(max(a_2000)_+_max(b_2000)),_range_query_with_1000_steps-10    306.0m ± 1%    209.1m ±  0%  -31.67% (p=0.002 n=6)
geomean                                                                            4.254m         2.804m        -34.09%

                                                                                │    Mimir     │        MimirWithQueryPlanner         │
                                                                                │      B       │       B        vs base               │
Query/sum(a_1),_instant_query-10                                                  64.78Mi ± 1%    64.65Mi ± 1%        ~ (p=0.781 n=6)
Query/sum(a_1),_range_query_with_100_steps-10                                     65.02Mi ± 2%    65.15Mi ± 1%        ~ (p=0.729 n=6)
Query/sum(a_1),_range_query_with_1000_steps-10                                    64.42Mi ± 2%    64.80Mi ± 2%        ~ (p=0.394 n=6)
Query/sum(a_100),_instant_query-10                                                60.60Mi ± 2%    60.54Mi ± 1%        ~ (p=0.461 n=6)
Query/sum(a_100),_range_query_with_100_steps-10                                   60.86Mi ± 0%    60.93Mi ± 1%        ~ (p=0.818 n=6)
Query/sum(a_100),_range_query_with_1000_steps-10                                  61.23Mi ± 1%    61.10Mi ± 1%        ~ (p=0.894 n=6)
Query/sum(a_2000),_instant_query-10                                               62.08Mi ± 1%    62.10Mi ± 2%        ~ (p=0.974 n=6)
Query/sum(a_2000),_range_query_with_100_steps-10                                  61.98Mi ± 1%    61.66Mi ± 2%        ~ (p=0.699 n=6)
Query/sum(a_2000),_range_query_with_1000_steps-10                                 66.87Mi ± 2%    66.79Mi ± 4%        ~ (p=0.589 n=6)
Query/a_1_+_a_1,_instant_query-10                                                 64.73Mi ± 1%    63.14Mi ± 2%   -2.46% (p=0.015 n=6)
Query/a_1_+_a_1,_range_query_with_100_steps-10                                    63.73Mi ± 2%    63.35Mi ± 1%        ~ (p=0.258 n=6)
Query/a_1_+_a_1,_range_query_with_1000_steps-10                                   63.87Mi ± 2%    63.03Mi ± 2%   -1.31% (p=0.045 n=6)
Query/a_100_+_a_100,_instant_query-10                                             60.95Mi ± 1%    60.48Mi ± 2%        ~ (p=0.119 n=6)
Query/a_100_+_a_100,_range_query_with_100_steps-10                                61.92Mi ± 1%    61.39Mi ± 1%   -0.86% (p=0.011 n=6)
Query/a_100_+_a_100,_range_query_with_1000_steps-10                               64.84Mi ± 3%    64.33Mi ± 1%        ~ (p=0.589 n=6)
Query/a_2000_+_a_2000,_instant_query-10                                           64.12Mi ± 1%    63.77Mi ± 2%        ~ (p=0.132 n=6)
Query/a_2000_+_a_2000,_range_query_with_100_steps-10                              72.92Mi ± 3%    73.05Mi ± 2%        ~ (p=1.000 n=6)
Query/a_2000_+_a_2000,_range_query_with_1000_steps-10                             135.9Mi ± 3%    128.4Mi ± 1%   -5.55% (p=0.002 n=6)
Query/sum(a_1)_+_sum(a_1),_instant_query-10                                       63.82Mi ± 1%    63.26Mi ± 1%   -0.88% (p=0.002 n=6)
Query/sum(a_1)_+_sum(a_1),_range_query_with_100_steps-10                          64.19Mi ± 1%    63.12Mi ± 2%   -1.66% (p=0.037 n=6)
Query/sum(a_1)_+_sum(a_1),_range_query_with_1000_steps-10                         63.64Mi ± 1%    63.38Mi ± 1%        ~ (p=0.589 n=6)
Query/sum(a_100)_+_sum(a_100),_instant_query-10                                   61.14Mi ± 1%    60.96Mi ± 1%        ~ (p=0.818 n=6)
Query/sum(a_100)_+_sum(a_100),_range_query_with_100_steps-10                      61.50Mi ± 1%    60.95Mi ± 1%        ~ (p=0.071 n=6)
Query/sum(a_100)_+_sum(a_100),_range_query_with_1000_steps-10                     61.45Mi ± 0%    61.46Mi ± 2%        ~ (p=1.000 n=6)
Query/sum(a_2000)_+_sum(a_2000),_instant_query-10                                 63.97Mi ± 2%    62.30Mi ± 1%   -2.60% (p=0.002 n=6)
Query/sum(a_2000)_+_sum(a_2000),_range_query_with_100_steps-10                    62.81Mi ± 2%    61.77Mi ± 2%   -1.67% (p=0.015 n=6)
Query/sum(a_2000)_+_sum(a_2000),_range_query_with_1000_steps-10                   72.16Mi ± 2%    66.46Mi ± 2%   -7.90% (p=0.002 n=6)
Query/max(a_1)_-_min(a_1),_instant_query-10                                       63.54Mi ± 2%    63.20Mi ± 1%        ~ (p=0.513 n=6)
Query/max(a_1)_-_min(a_1),_range_query_with_100_steps-10                          63.63Mi ± 2%    63.33Mi ± 2%        ~ (p=0.310 n=6)
Query/max(a_1)_-_min(a_1),_range_query_with_1000_steps-10                         63.67Mi ± 1%    63.30Mi ± 2%        ~ (p=0.509 n=6)
Query/max(a_100)_-_min(a_100),_instant_query-10                                   61.09Mi ± 1%    60.88Mi ± 2%        ~ (p=0.619 n=6)
Query/max(a_100)_-_min(a_100),_range_query_with_100_steps-10                      61.45Mi ± 1%    61.32Mi ± 0%        ~ (p=0.900 n=6)
Query/max(a_100)_-_min(a_100),_range_query_with_1000_steps-10                     61.66Mi ± 3%    64.75Mi ± 1%   +5.00% (p=0.002 n=6)
Query/max(a_2000)_-_min(a_2000),_instant_query-10                                 63.40Mi ± 0%    62.30Mi ± 2%   -1.74% (p=0.015 n=6)
Query/max(a_2000)_-_min(a_2000),_range_query_with_100_steps-10                    63.20Mi ± 2%    70.59Mi ± 1%  +11.68% (p=0.002 n=6)
Query/max(a_2000)_-_min(a_2000),_range_query_with_1000_steps-10                   73.17Mi ± 4%   128.59Mi ± 1%  +75.73% (p=0.002 n=6)
Query/a_1_/_(a_1_+_b_1),_instant_query-10                                         64.17Mi ± 1%    63.75Mi ± 2%        ~ (p=0.310 n=6)
Query/a_1_/_(a_1_+_b_1),_range_query_with_100_steps-10                            64.41Mi ± 1%    63.42Mi ± 1%   -1.54% (p=0.011 n=6)
Query/a_1_/_(a_1_+_b_1),_range_query_with_1000_steps-10                           63.26Mi ± 1%    63.19Mi ± 1%        ~ (p=0.777 n=6)
Query/a_100_/_(a_100_+_b_100),_instant_query-10                                   61.45Mi ± 1%    61.40Mi ± 1%        ~ (p=0.485 n=6)
Query/a_100_/_(a_100_+_b_100),_range_query_with_100_steps-10                      61.73Mi ± 1%    61.42Mi ± 1%        ~ (p=0.310 n=6)
Query/a_100_/_(a_100_+_b_100),_range_query_with_1000_steps-10                     65.74Mi ± 2%    64.52Mi ± 1%   -1.87% (p=0.015 n=6)
Query/a_2000_/_(a_2000_+_b_2000),_instant_query-10                                66.45Mi ± 1%    64.16Mi ± 1%   -3.43% (p=0.002 n=6)
Query/a_2000_/_(a_2000_+_b_2000),_range_query_with_100_steps-10                   77.25Mi ± 2%    73.34Mi ± 2%   -5.06% (p=0.002 n=6)
Query/a_2000_/_(a_2000_+_b_2000),_range_query_with_1000_steps-10                  141.9Mi ± 6%    139.6Mi ± 2%        ~ (p=0.818 n=6)
Query/sum(a_1)_/_(sum(a_1)_+_sum(b_1)),_instant_query-10                          64.63Mi ± 1%    63.60Mi ± 1%   -1.60% (p=0.004 n=6)
Query/sum(a_1)_/_(sum(a_1)_+_sum(b_1)),_range_query_with_100_steps-10             64.25Mi ± 1%    63.61Mi ± 1%        ~ (p=0.071 n=6)
Query/sum(a_1)_/_(sum(a_1)_+_sum(b_1)),_range_query_with_1000_steps-10            63.80Mi ± 1%    63.47Mi ± 1%   -0.51% (p=0.041 n=6)
Query/sum(a_100)_/_(sum(a_100)_+_sum(b_100)),_instant_query-10                    61.36Mi ± 1%    61.10Mi ± 1%        ~ (p=0.195 n=6)
Query/sum(a_100)_/_(sum(a_100)_+_sum(b_100)),_range_query_with_100_steps-10       61.53Mi ± 2%    61.16Mi ± 1%        ~ (p=0.394 n=6)
Query/sum(a_100)_/_(sum(a_100)_+_sum(b_100)),_range_query_with_1000_steps-10      62.01Mi ± 1%    61.67Mi ± 1%        ~ (p=0.093 n=6)
Query/sum(a_2000)_/_(sum(a_2000)_+_sum(b_2000)),_instant_query-10                 63.94Mi ± 1%    63.09Mi ± 1%   -1.33% (p=0.026 n=6)
Query/sum(a_2000)_/_(sum(a_2000)_+_sum(b_2000)),_range_query_with_100_steps-10    65.85Mi ± 1%    63.16Mi ± 2%   -4.08% (p=0.002 n=6)
Query/sum(a_2000)_/_(sum(a_2000)_+_sum(b_2000)),_range_query_with_1000_steps-10   75.27Mi ± 4%    72.55Mi ± 4%   -3.61% (p=0.039 n=6)
Query/min(a_1)_/_(max(a_1)_+_max(b_1)),_instant_query-10                          63.62Mi ± 1%    63.49Mi ± 2%        ~ (p=0.255 n=6)
Query/min(a_1)_/_(max(a_1)_+_max(b_1)),_range_query_with_100_steps-10             63.91Mi ± 2%    63.62Mi ± 1%        ~ (p=0.420 n=6)
Query/min(a_1)_/_(max(a_1)_+_max(b_1)),_range_query_with_1000_steps-10            63.81Mi ± 1%    63.41Mi ± 1%        ~ (p=0.100 n=6)
Query/min(a_100)_/_(max(a_100)_+_max(b_100)),_instant_query-10                    61.72Mi ± 1%    61.20Mi ± 1%   -0.84% (p=0.011 n=6)
Query/min(a_100)_/_(max(a_100)_+_max(b_100)),_range_query_with_100_steps-10       61.48Mi ± 1%    61.85Mi ± 1%        ~ (p=0.485 n=6)
Query/min(a_100)_/_(max(a_100)_+_max(b_100)),_range_query_with_1000_steps-10      61.28Mi ± 1%    64.51Mi ± 2%   +5.27% (p=0.002 n=6)
Query/min(a_2000)_/_(max(a_2000)_+_max(b_2000)),_instant_query-10                 64.19Mi ± 1%    63.72Mi ± 1%        ~ (p=0.132 n=6)
Query/min(a_2000)_/_(max(a_2000)_+_max(b_2000)),_range_query_with_100_steps-10    65.00Mi ± 1%    72.51Mi ± 2%  +11.55% (p=0.002 n=6)
Query/min(a_2000)_/_(max(a_2000)_+_max(b_2000)),_range_query_with_1000_steps-10   75.03Mi ± 5%   133.38Mi ± 2%  +77.76% (p=0.002 n=6)
geomean                                                                           65.78Mi         66.63Mi        +1.29%

Which issue(s) this PR fixes or relates to

(none)

Checklist

  • Tests updated.
  • [n/a] Documentation added.
  • [covered by Mimir Query Engine #10067] CHANGELOG.md updated - the order of entries should be [CHANGE], [FEATURE], [ENHANCEMENT], [BUGFIX].
  • [n/a] about-versioning.md updated with experimental features.

Copy link
Contributor

github-actions bot commented Apr 11, 2025

💻 Deploy preview deleted.

@charleskorn charleskorn force-pushed the charleskorn/mqe-common-subexpression-elimination branch 6 times, most recently from 18942bc to 3fda6ae Compare April 14, 2025 05:42
@charleskorn charleskorn marked this pull request as ready for review April 14, 2025 05:59
@charleskorn charleskorn requested review from tacole02 and a team as code owners April 14, 2025 05:59
Copy link
Contributor

@tacole02 tacole02 left a comment

Choose a reason for hiding this comment

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

Thank you!

@charleskorn charleskorn force-pushed the charleskorn/mqe-common-subexpression-elimination branch from 3fda6ae to 1a81f9e Compare April 16, 2025 00:37
Copy link
Contributor

@jhesketh jhesketh left a comment

Choose a reason for hiding this comment

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

Looks solid, thanks for that! Just small things/nits :-).

@charleskorn charleskorn force-pushed the charleskorn/mqe-common-subexpression-elimination branch from 052def7 to 8e35d43 Compare April 16, 2025 06:14
Copy link
Contributor

@jhesketh jhesketh left a comment

Choose a reason for hiding this comment

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

lgtm, but a nit on the naming of the new metric.

Perhaps in the future we can also consider replacing the active query tracking with something that can hold the state of the query (eg planning, executing, etc), instead of needing to add a comment at the end of it. That would also remove the race for when the query is removed and readded to the tracker.

@charleskorn
Copy link
Contributor Author

Perhaps in the future we can also consider replacing the active query tracking with something that can hold the state of the query (eg planning, executing, etc), instead of needing to add a comment at the end of it. That would also remove the race for when the query is removed and readded to the tracker.

This is a good point - I'll tackle this separately.

@charleskorn charleskorn enabled auto-merge (squash) May 1, 2025 04:03

This comment has been minimized.

Copy link
Contributor

github-actions bot commented May 1, 2025

😢 zizmor failed with exit code 13.

Expand for full output
warning[excessive-permissions]: overly broad permissions
  --> ./.github/workflows/update-make-docs.yml:7:3
   |
 7 | /   main:
 8 | |     if: github.repository == 'grafana/mimir'
...  |
16 | |           pr_options: >
17 | |             --label type/docs
   | |                              -
   | |______________________________|
   |                                this job
   |                                default permissions used due to no permissions: block
   |
   = note: audit confidence → Medium

24 findings (11 ignored, 12 suppressed): 0 unknown, 0 informational, 0 low, 1 medium, 0 high

@charleskorn charleskorn merged commit 81240d4 into main May 1, 2025
30 checks passed
@charleskorn charleskorn deleted the charleskorn/mqe-common-subexpression-elimination branch May 1, 2025 04:48
charleskorn added a commit that referenced this pull request Jun 24, 2025
…imestamp()` produce incorrect results (#11830)

#### What this PR does

This PR fixes an issue in common subexpression elimination where an
expression like `timestamp(foo_time) - foo_time` would produce incorrect
results.

This happens because the instant vector selector is incorrectly
deduplicated in this case: at present, the instant vector selector
operator behaves differently if it is wrapped in `timestamp()`, but the
query planner did not take this into consideration when identifying
duplicate expressions.

A future improvement would be to allow the instant vector selector
operator to return both the sample value and sample timestamp to
different consumers, but given queries like this are rare, I've opted
for the simpler approach.

#### Which issue(s) this PR fixes or relates to

#11189

#### Checklist

- [x] Tests updated.
- [n/a] Documentation added.
- [covered by #10067] `CHANGELOG.md` updated - the order of entries
should be `[CHANGE]`, `[FEATURE]`, `[ENHANCEMENT]`, `[BUGFIX]`. If
changelog entry is not needed, please add the `changelog-not-needed`
label to the PR.
- [n/a]
[`about-versioning.md`](https://github.com/grafana/mimir/blob/main/docs/sources/mimir/configure/about-versioning.md)
updated with experimental features.
mimir-github-bot bot pushed a commit that referenced this pull request Jun 24, 2025
…imestamp()` produce incorrect results (#11830)

#### What this PR does

This PR fixes an issue in common subexpression elimination where an
expression like `timestamp(foo_time) - foo_time` would produce incorrect
results.

This happens because the instant vector selector is incorrectly
deduplicated in this case: at present, the instant vector selector
operator behaves differently if it is wrapped in `timestamp()`, but the
query planner did not take this into consideration when identifying
duplicate expressions.

A future improvement would be to allow the instant vector selector
operator to return both the sample value and sample timestamp to
different consumers, but given queries like this are rare, I've opted
for the simpler approach.

#### Which issue(s) this PR fixes or relates to

#11189

#### Checklist

- [x] Tests updated.
- [n/a] Documentation added.
- [covered by #10067] `CHANGELOG.md` updated - the order of entries
should be `[CHANGE]`, `[FEATURE]`, `[ENHANCEMENT]`, `[BUGFIX]`. If
changelog entry is not needed, please add the `changelog-not-needed`
label to the PR.
- [n/a]
[`about-versioning.md`](https://github.com/grafana/mimir/blob/main/docs/sources/mimir/configure/about-versioning.md)
updated with experimental features.

(cherry picked from commit dd17406)
charleskorn added a commit that referenced this pull request Jun 29, 2025
…ith multiple children, and add metric on number of selectors eliminated (#11879)

#### What this PR does

This PR makes two changes to common subexpression elimination:

* Tests: I was worried we weren't handling the case where a expression
has multiple children correctly, so added some tests to check. Turns out
this case is OK, but I think it'd be valuable to retain so it's not
broken in the future.
* Metric: the existing
`cortex_mimir_query_engine_common_subexpression_elimination_duplication_nodes_introduced`
metric is useful for gauging the impact of CSE, but doesn't tell us how
much load on ingesters and store-gateways we've avoided. The new metric
tells us the number of selectors eliminated and therefore gives us a
better idea of the number of avoided ingester and store-gateway
requests.

#### Which issue(s) this PR fixes or relates to

#11189 

#### Checklist

- [x] Tests updated.
- [n/a] Documentation added.
- [covered by #10067] `CHANGELOG.md` updated - the order of entries
should be `[CHANGE]`, `[FEATURE]`, `[ENHANCEMENT]`, `[BUGFIX]`. If
changelog entry is not needed, please add the `changelog-not-needed`
label to the PR.
- [n/a]
[`about-versioning.md`](https://github.com/grafana/mimir/blob/main/docs/sources/mimir/configure/about-versioning.md)
updated with experimental features.
charleskorn added a commit that referenced this pull request Jul 7, 2025
… incorrect results (#11989)

#### What this PR does

This PR fixes an issue where common subexpression elimination (CSE) can
cause a query to return incorrect query results.

The issue would occur if the ring buffer used to buffer data was in a
state where the head of the buffer is at the end of the slice, and the
tail was at the beginning of the slice. In this case, calling `Remove`
for the item at the end of the slice would return the correct value, as
would the subsequent call for the item at the beginning of the slice,
but all following calls would return incorrect values.

#### Which issue(s) this PR fixes or relates to

#11189 

#### Checklist

- [x] Tests updated.
- [n/a] Documentation added.
- [covered by #10067] `CHANGELOG.md` updated - the order of entries
should be `[CHANGE]`, `[FEATURE]`, `[ENHANCEMENT]`, `[BUGFIX]`. If
changelog entry is not needed, please add the `changelog-not-needed`
label to the PR.
- [n/a]
[`about-versioning.md`](https://github.com/grafana/mimir/blob/main/docs/sources/mimir/configure/about-versioning.md)
updated with experimental features.
mimir-github-bot bot pushed a commit that referenced this pull request Jul 7, 2025
… incorrect results (#11989)

#### What this PR does

This PR fixes an issue where common subexpression elimination (CSE) can
cause a query to return incorrect query results.

The issue would occur if the ring buffer used to buffer data was in a
state where the head of the buffer is at the end of the slice, and the
tail was at the beginning of the slice. In this case, calling `Remove`
for the item at the end of the slice would return the correct value, as
would the subsequent call for the item at the beginning of the slice,
but all following calls would return incorrect values.

#### Which issue(s) this PR fixes or relates to

#11189

#### Checklist

- [x] Tests updated.
- [n/a] Documentation added.
- [covered by #10067] `CHANGELOG.md` updated - the order of entries
should be `[CHANGE]`, `[FEATURE]`, `[ENHANCEMENT]`, `[BUGFIX]`. If
changelog entry is not needed, please add the `changelog-not-needed`
label to the PR.
- [n/a]
[`about-versioning.md`](https://github.com/grafana/mimir/blob/main/docs/sources/mimir/configure/about-versioning.md)
updated with experimental features.

(cherry picked from commit 8c9ffff)
mimir-github-bot bot pushed a commit that referenced this pull request Jul 7, 2025
… incorrect results (#11989)

#### What this PR does

This PR fixes an issue where common subexpression elimination (CSE) can
cause a query to return incorrect query results.

The issue would occur if the ring buffer used to buffer data was in a
state where the head of the buffer is at the end of the slice, and the
tail was at the beginning of the slice. In this case, calling `Remove`
for the item at the end of the slice would return the correct value, as
would the subsequent call for the item at the beginning of the slice,
but all following calls would return incorrect values.

#### Which issue(s) this PR fixes or relates to

#11189

#### Checklist

- [x] Tests updated.
- [n/a] Documentation added.
- [covered by #10067] `CHANGELOG.md` updated - the order of entries
should be `[CHANGE]`, `[FEATURE]`, `[ENHANCEMENT]`, `[BUGFIX]`. If
changelog entry is not needed, please add the `changelog-not-needed`
label to the PR.
- [n/a]
[`about-versioning.md`](https://github.com/grafana/mimir/blob/main/docs/sources/mimir/configure/about-versioning.md)
updated with experimental features.

(cherry picked from commit 8c9ffff)
mimir-github-bot bot pushed a commit that referenced this pull request Jul 7, 2025
… incorrect results (#11989)

#### What this PR does

This PR fixes an issue where common subexpression elimination (CSE) can
cause a query to return incorrect query results.

The issue would occur if the ring buffer used to buffer data was in a
state where the head of the buffer is at the end of the slice, and the
tail was at the beginning of the slice. In this case, calling `Remove`
for the item at the end of the slice would return the correct value, as
would the subsequent call for the item at the beginning of the slice,
but all following calls would return incorrect values.

#### Which issue(s) this PR fixes or relates to

#11189

#### Checklist

- [x] Tests updated.
- [n/a] Documentation added.
- [covered by #10067] `CHANGELOG.md` updated - the order of entries
should be `[CHANGE]`, `[FEATURE]`, `[ENHANCEMENT]`, `[BUGFIX]`. If
changelog entry is not needed, please add the `changelog-not-needed`
label to the PR.
- [n/a]
[`about-versioning.md`](https://github.com/grafana/mimir/blob/main/docs/sources/mimir/configure/about-versioning.md)
updated with experimental features.

(cherry picked from commit 8c9ffff)
mimir-github-bot bot pushed a commit that referenced this pull request Jul 7, 2025
… incorrect results (#11989)

#### What this PR does

This PR fixes an issue where common subexpression elimination (CSE) can
cause a query to return incorrect query results.

The issue would occur if the ring buffer used to buffer data was in a
state where the head of the buffer is at the end of the slice, and the
tail was at the beginning of the slice. In this case, calling `Remove`
for the item at the end of the slice would return the correct value, as
would the subsequent call for the item at the beginning of the slice,
but all following calls would return incorrect values.

#### Which issue(s) this PR fixes or relates to

#11189

#### Checklist

- [x] Tests updated.
- [n/a] Documentation added.
- [covered by #10067] `CHANGELOG.md` updated - the order of entries
should be `[CHANGE]`, `[FEATURE]`, `[ENHANCEMENT]`, `[BUGFIX]`. If
changelog entry is not needed, please add the `changelog-not-needed`
label to the PR.
- [n/a]
[`about-versioning.md`](https://github.com/grafana/mimir/blob/main/docs/sources/mimir/configure/about-versioning.md)
updated with experimental features.

(cherry picked from commit 8c9ffff)
mimir-github-bot bot pushed a commit that referenced this pull request Jul 7, 2025
… incorrect results (#11989)

#### What this PR does

This PR fixes an issue where common subexpression elimination (CSE) can
cause a query to return incorrect query results.

The issue would occur if the ring buffer used to buffer data was in a
state where the head of the buffer is at the end of the slice, and the
tail was at the beginning of the slice. In this case, calling `Remove`
for the item at the end of the slice would return the correct value, as
would the subsequent call for the item at the beginning of the slice,
but all following calls would return incorrect values.

#### Which issue(s) this PR fixes or relates to

#11189

#### Checklist

- [x] Tests updated.
- [n/a] Documentation added.
- [covered by #10067] `CHANGELOG.md` updated - the order of entries
should be `[CHANGE]`, `[FEATURE]`, `[ENHANCEMENT]`, `[BUGFIX]`. If
changelog entry is not needed, please add the `changelog-not-needed`
label to the PR.
- [n/a]
[`about-versioning.md`](https://github.com/grafana/mimir/blob/main/docs/sources/mimir/configure/about-versioning.md)
updated with experimental features.

(cherry picked from commit 8c9ffff)
mimir-github-bot bot pushed a commit that referenced this pull request Jul 7, 2025
… incorrect results (#11989)

#### What this PR does

This PR fixes an issue where common subexpression elimination (CSE) can
cause a query to return incorrect query results.

The issue would occur if the ring buffer used to buffer data was in a
state where the head of the buffer is at the end of the slice, and the
tail was at the beginning of the slice. In this case, calling `Remove`
for the item at the end of the slice would return the correct value, as
would the subsequent call for the item at the beginning of the slice,
but all following calls would return incorrect values.

#### Which issue(s) this PR fixes or relates to

#11189

#### Checklist

- [x] Tests updated.
- [n/a] Documentation added.
- [covered by #10067] `CHANGELOG.md` updated - the order of entries
should be `[CHANGE]`, `[FEATURE]`, `[ENHANCEMENT]`, `[BUGFIX]`. If
changelog entry is not needed, please add the `changelog-not-needed`
label to the PR.
- [n/a]
[`about-versioning.md`](https://github.com/grafana/mimir/blob/main/docs/sources/mimir/configure/about-versioning.md)
updated with experimental features.

(cherry picked from commit 8c9ffff)
charleskorn added a commit that referenced this pull request Aug 1, 2025
…12236)

#### What this PR does

In #11189, we added common
subexpression elimination (CSE) to MQE's query planner. However, this
only supported expressions that produced instant vectors - so this would
apply to expressions like `sum(foo)` in `sum(foo) + sum(foo)` or
`rate(foo[5m])` in `rate(foo[5m]) + rate(foo[5m])`.

In this PR, I'm adding support for expressions that produce range
vectors, provided the parent query is an instant query. For example, CSE
will now apply to `foo[5m]` in `sum_over_time(foo[5m]) /
count_over_time(foo[5m])`.

I've limited this to instant queries for the time being as supporting
range queries adds a lot of complexity. The vast majority of queries we
see in production Mimir clusters are instant queries from rules, so we
get most of the benefit without the extra complexity.

Benchmarks show a 40-50% latency improvement for affected queries,
albeit at the cost of higher peak memory consumption due to the
buffering required, similar to what we see for instant vector
expressions:

```
goos: darwin
goarch: arm64
pkg: github.com/grafana/mimir/pkg/streamingpromql/benchmarks
cpu: Apple M1 Pro
                                                                                                        │  before.txt  │             after.txt              │
                                                                                                        │    sec/op    │   sec/op     vs base               │
Query/sum(sum_over_time(a_1[1m]))_/_sum(count_over_time(a_1[1m])),_instant_query/engine=Mimir-10           274.1µ ± 1%   152.4µ ± 1%  -44.41% (p=0.002 n=6)
Query/sum(sum_over_time(a_100[1m]))_/_sum(count_over_time(a_100[1m])),_instant_query/engine=Mimir-10       838.4µ ± 2%   495.7µ ± 0%  -40.87% (p=0.002 n=6)
Query/sum(sum_over_time(a_2000[1m]))_/_sum(count_over_time(a_2000[1m])),_instant_query/engine=Mimir-10     8.555m ± 1%   5.356m ± 0%  -37.40% (p=0.002 n=6)
Query/sum(sum_over_time(a_1[1d]))_/_sum(count_over_time(a_1[1d])),_instant_query/engine=Mimir-10          1136.0µ ± 1%   606.9µ ± 2%  -46.58% (p=0.002 n=6)
Query/sum(sum_over_time(a_100[1d]))_/_sum(count_over_time(a_100[1d])),_instant_query/engine=Mimir-10       68.34m ± 1%   36.16m ± 2%  -47.09% (p=0.002 n=6)
Query/sum(sum_over_time(a_2000[1d]))_/_sum(count_over_time(a_2000[1d])),_instant_query/engine=Mimir-10    1306.9m ± 0%   666.7m ± 1%  -48.98% (p=0.002 n=6)

                                                                                                        │  before.txt  │               after.txt               │
                                                                                                        │      B       │       B        vs base                │
Query/sum(sum_over_time(a_1[1m]))_/_sum(count_over_time(a_1[1m])),_instant_query/engine=Mimir-10          56.76Mi ± 1%    56.53Mi ± 0%         ~ (p=0.223 n=6)
Query/sum(sum_over_time(a_100[1m]))_/_sum(count_over_time(a_100[1m])),_instant_query/engine=Mimir-10      56.73Mi ± 1%    56.52Mi ± 1%         ~ (p=0.509 n=6)
Query/sum(sum_over_time(a_2000[1m]))_/_sum(count_over_time(a_2000[1m])),_instant_query/engine=Mimir-10    58.97Mi ± 1%    59.06Mi ± 1%         ~ (p=0.420 n=6)
Query/sum(sum_over_time(a_1[1d]))_/_sum(count_over_time(a_1[1d])),_instant_query/engine=Mimir-10          62.27Mi ± 1%    62.34Mi ± 1%         ~ (p=0.554 n=6)
Query/sum(sum_over_time(a_100[1d]))_/_sum(count_over_time(a_100[1d])),_instant_query/engine=Mimir-10      69.50Mi ± 3%   116.27Mi ± 3%   +67.30% (p=0.002 n=6)
Query/sum(sum_over_time(a_2000[1d]))_/_sum(count_over_time(a_2000[1d])),_instant_query/engine=Mimir-10    75.62Mi ± 4%   430.17Mi ± 0%  +468.88% (p=0.002 n=6)

```

Benchmarks show no noticable impact for the existing instant vector
implementation due to making `SeriesDataRingBuffer` generic.

#### Which issue(s) this PR fixes or relates to

#11189

#### Checklist

- [x] Tests updated.
- [n/a] Documentation added.
- [x] `CHANGELOG.md` updated - the order of entries should be
`[CHANGE]`, `[FEATURE]`, `[ENHANCEMENT]`, `[BUGFIX]`. If changelog entry
is not needed, please add the `changelog-not-needed` label to the PR.
- [n/a]
[`about-versioning.md`](https://github.com/grafana/mimir/blob/main/docs/sources/mimir/configure/about-versioning.md)
updated with experimental features.
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.

3 participants