Skip to content

arrow: monomorphize parser ingestion and shrink DecodeCursor - #46

Draft
bwplotka wants to merge 2 commits into
mainfrom
bwplotka/optimize-cooperative-decode
Draft

bwplotka wants to merge 2 commits into
mainfrom
bwplotka/optimize-cooperative-decode

Conversation

@bwplotka

Copy link
Copy Markdown
Member

Follow-up to prometheus/scrape.rs#30 and prometheus/scrape.rs#27, motivated by cooperative decode profiling in open-telemetry/otel-arrow#4703.

Reduces per-entry and per-step overhead in ArrowDecoder and DecodeStream so cooperative DecodeBudget decoding matches synchronous streaming throughput:

  • Replaces the per-entry ActiveParser enum wrapper with an internal ScrapeParser specialization trait and marks hot Layer 0 entry accessors #[inline] so ingest_entries monomorphizes directly over PromParser and OpenMetricsParser.
  • Retains PromParserState and OpenMetricsParserState inside ArrowDecoder and stores a 1-byte ParserKind tag in DecodeCursor, shrinking DecodeCursor from 344 bytes to 80 scalar bytes and avoiding dummy parser initialization in init_cursor and 272-byte enum moves on every step.
  • Raises DECODE_BUDGET_DURATION_CHECK_INTERVAL from 16 to 64 entries, precomputes the per-step byte limit, and skips Instant::now() when a step cannot reach the first periodic clock check.

Commits in order:

  1. 0a5517a parse: inline entry accessors and add in-place parser state reset
  2. 328e8f0 arrow: monomorphize parser ingestion and shrink DecodeCursor
Benchmarks (benchstat)

Measured with 6 interleaved rounds of cargo bench --bench decode -- -bench '^BenchmarkDecode' -benchtime 1s -count 1 -cpu 2 -benchmem:

goos: linux
goarch: amd64
pkg: expfmt
cpu: Intel(R) Xeon(R) CPU @ 2.20GHz
                                                                        │  before.txt  │             after.txt             │
                                                                        │    sec/op    │   sec/op     vs base              │
Decode/case=alltypes.237mfs.nometa/mode=decode-2                          924.1µ ±  3%   911.8µ ± 2%       ~ (p=0.065 n=6)
Decode/case=alltypes.237mfs.nometa/mode=decode_cooperative-2              933.9µ ±  4%   900.7µ ± 1%  -3.56% (p=0.002 n=6)
Decode/case=alltypes.237mfs.nometa/mode=stream_16k-2                      923.9µ ±  5%   905.7µ ± 1%  -1.96% (p=0.002 n=6)
Decode/case=alltypes.237mfs.nometa/mode=stream_256-2                      948.3µ ±  1%   923.5µ ± 1%  -2.61% (p=0.002 n=6)
Decode/case=alltypes.237mfs.nometa/mode=stream_256_cooperative-2          981.3µ ±  2%   955.4µ ± 2%  -2.65% (p=0.026 n=6)
Decode/case=alltypes.209mfs.counter_gauge/mode=decode-2                   319.8µ ±  1%   314.6µ ± 4%       ~ (p=0.132 n=6)
Decode/case=alltypes.209mfs.counter_gauge/mode=decode_cooperative-2       329.4µ ±  1%   324.6µ ± 1%  -1.46% (p=0.002 n=6)
Decode/case=alltypes.209mfs.counter_gauge/mode=stream_16k-2               321.9µ ±  1%   319.9µ ± 1%       ~ (p=0.093 n=6)
Decode/case=alltypes.209mfs.counter_gauge/mode=stream_256-2               333.0µ ±  1%   329.5µ ± 2%       ~ (p=0.132 n=6)
Decode/case=alltypes.209mfs.counter_gauge/mode=stream_256_cooperative-2   343.8µ ±  1%   337.1µ ± 1%  -1.95% (p=0.002 n=6)
Decode/case=alltypes.237mfs/mode=decode-2                                 1.108m ±  2%   1.117m ± 2%       ~ (p=0.485 n=6)
Decode/case=alltypes.237mfs/mode=decode_cooperative-2                     1.149m ± 13%   1.134m ± 3%  -1.35% (p=0.002 n=6)
Decode/case=alltypes.237mfs/mode=stream_16k-2                             1.125m ± 53%   1.113m ± 2%       ~ (p=0.310 n=6)
Decode/case=alltypes.237mfs/mode=stream_256-2                             1.159m ± 54%   1.161m ± 3%       ~ (p=0.937 n=6)
Decode/case=alltypes.237mfs/mode=stream_256_cooperative-2                 1.196m ±  2%   1.178m ± 2%       ~ (p=0.093 n=6)
geomean                                                                   708.7µ         698.4µ       -1.46%

                                                                        │  before.txt   │             after.txt              │
                                                                        │      B/s      │     B/s       vs base              │
Decode/case=alltypes.237mfs.nometa/mode=decode-2                          152.9Mi ±  3%   154.9Mi ± 2%       ~ (p=0.065 n=6)
Decode/case=alltypes.237mfs.nometa/mode=decode_cooperative-2              151.3Mi ±  4%   156.8Mi ± 1%  +3.69% (p=0.002 n=6)
Decode/case=alltypes.237mfs.nometa/mode=stream_16k-2                      152.9Mi ±  5%   156.0Mi ± 1%  +2.01% (p=0.002 n=6)
Decode/case=alltypes.237mfs.nometa/mode=stream_256-2                      149.0Mi ±  1%   153.0Mi ± 1%  +2.68% (p=0.002 n=6)
Decode/case=alltypes.237mfs.nometa/mode=stream_256_cooperative-2          144.0Mi ±  2%   147.9Mi ± 3%  +2.72% (p=0.026 n=6)
Decode/case=alltypes.209mfs.counter_gauge/mode=decode-2                   217.9Mi ±  1%   221.5Mi ± 4%       ~ (p=0.132 n=6)
Decode/case=alltypes.209mfs.counter_gauge/mode=decode_cooperative-2       211.6Mi ±  1%   214.7Mi ± 1%  +1.48% (p=0.002 n=6)
Decode/case=alltypes.209mfs.counter_gauge/mode=stream_16k-2               216.5Mi ±  1%   217.9Mi ± 1%       ~ (p=0.093 n=6)
Decode/case=alltypes.209mfs.counter_gauge/mode=stream_256-2               209.3Mi ±  1%   211.5Mi ± 2%       ~ (p=0.132 n=6)
Decode/case=alltypes.209mfs.counter_gauge/mode=stream_256_cooperative-2   202.7Mi ±  1%   206.8Mi ± 1%  +1.99% (p=0.002 n=6)
Decode/case=alltypes.237mfs/mode=decode-2                                 163.7Mi ±  2%   162.4Mi ± 2%       ~ (p=0.485 n=6)
Decode/case=alltypes.237mfs/mode=decode_cooperative-2                     157.8Mi ± 12%   160.0Mi ± 3%  +1.36% (p=0.002 n=6)
Decode/case=alltypes.237mfs/mode=stream_16k-2                             161.3Mi ± 35%   163.0Mi ± 2%       ~ (p=0.310 n=6)
Decode/case=alltypes.237mfs/mode=stream_256-2                             156.5Mi ± 35%   156.2Mi ± 3%       ~ (p=0.981 n=6)
Decode/case=alltypes.237mfs/mode=stream_256_cooperative-2                 151.6Mi ±  2%   154.0Mi ± 2%       ~ (p=0.093 n=6)
geomean                                                                   171.2Mi         173.7Mi       +1.48%

                                                                        │  before.txt  │              after.txt               │
                                                                        │     B/op     │     B/op      vs base                │
Decode/case=alltypes.237mfs.nometa/mode=decode-2                          102.1Ki ± 0%   102.1Ki ± 0%       ~ (p=1.000 n=6) ¹
Decode/case=alltypes.237mfs.nometa/mode=decode_cooperative-2              102.1Ki ± 0%   102.1Ki ± 0%       ~ (p=1.000 n=6) ¹
Decode/case=alltypes.237mfs.nometa/mode=stream_16k-2                      102.1Ki ± 0%   102.1Ki ± 0%       ~ (p=1.000 n=6) ¹
Decode/case=alltypes.237mfs.nometa/mode=stream_256-2                      136.9Ki ± 0%   136.9Ki ± 0%       ~ (p=1.000 n=6) ¹
Decode/case=alltypes.237mfs.nometa/mode=stream_256_cooperative-2          136.9Ki ± 0%   136.9Ki ± 0%       ~ (p=1.000 n=6) ¹
Decode/case=alltypes.209mfs.counter_gauge/mode=decode-2                   50.58Ki ± 0%   50.58Ki ± 0%       ~ (p=1.000 n=6) ¹
Decode/case=alltypes.209mfs.counter_gauge/mode=decode_cooperative-2       50.58Ki ± 0%   50.58Ki ± 0%       ~ (p=1.000 n=6) ¹
Decode/case=alltypes.209mfs.counter_gauge/mode=stream_16k-2               50.58Ki ± 0%   50.58Ki ± 0%       ~ (p=1.000 n=6) ¹
Decode/case=alltypes.209mfs.counter_gauge/mode=stream_256-2               56.39Ki ± 0%   56.39Ki ± 0%       ~ (p=1.000 n=6) ¹
Decode/case=alltypes.209mfs.counter_gauge/mode=stream_256_cooperative-2   56.39Ki ± 0%   56.39Ki ± 0%       ~ (p=1.000 n=6) ¹
Decode/case=alltypes.237mfs/mode=decode-2                                 82.63Ki ± 0%   82.63Ki ± 0%       ~ (p=1.000 n=6) ¹
Decode/case=alltypes.237mfs/mode=decode_cooperative-2                     82.63Ki ± 0%   82.63Ki ± 0%       ~ (p=1.000 n=6) ¹
Decode/case=alltypes.237mfs/mode=stream_16k-2                             82.63Ki ± 0%   82.63Ki ± 0%       ~ (p=1.000 n=6) ¹
Decode/case=alltypes.237mfs/mode=stream_256-2                             105.6Ki ± 0%   105.6Ki ± 0%       ~ (p=1.000 n=6) ¹
Decode/case=alltypes.237mfs/mode=stream_256_cooperative-2                 105.6Ki ± 0%   105.6Ki ± 0%       ~ (p=1.000 n=6) ¹
geomean                                                                   82.07Ki        82.07Ki       +0.00%
¹ all samples are equal

                                                                        │ before.txt │             after.txt              │
                                                                        │ allocs/op  │ allocs/op   vs base                │
Decode/case=alltypes.237mfs.nometa/mode=decode-2                          76.00 ± 0%   76.00 ± 0%       ~ (p=1.000 n=6) ¹
Decode/case=alltypes.237mfs.nometa/mode=decode_cooperative-2              76.00 ± 0%   76.00 ± 0%       ~ (p=1.000 n=6) ¹
Decode/case=alltypes.237mfs.nometa/mode=stream_16k-2                      76.00 ± 0%   76.00 ± 0%       ~ (p=1.000 n=6) ¹
Decode/case=alltypes.237mfs.nometa/mode=stream_256-2                      601.0 ± 0%   601.0 ± 0%       ~ (p=1.000 n=6) ¹
Decode/case=alltypes.237mfs.nometa/mode=stream_256_cooperative-2          601.0 ± 0%   601.0 ± 0%       ~ (p=1.000 n=6) ¹
Decode/case=alltypes.209mfs.counter_gauge/mode=decode-2                   91.00 ± 0%   91.00 ± 0%       ~ (p=1.000 n=6) ¹
Decode/case=alltypes.209mfs.counter_gauge/mode=decode_cooperative-2       91.00 ± 0%   91.00 ± 0%       ~ (p=1.000 n=6) ¹
Decode/case=alltypes.209mfs.counter_gauge/mode=stream_16k-2               91.00 ± 0%   91.00 ± 0%       ~ (p=1.000 n=6) ¹
Decode/case=alltypes.209mfs.counter_gauge/mode=stream_256-2               181.0 ± 0%   181.0 ± 0%       ~ (p=1.000 n=6) ¹
Decode/case=alltypes.209mfs.counter_gauge/mode=stream_256_cooperative-2   181.0 ± 0%   181.0 ± 0%       ~ (p=1.000 n=6) ¹
Decode/case=alltypes.237mfs/mode=decode-2                                 182.0 ± 0%   182.0 ± 0%       ~ (p=1.000 n=6) ¹
Decode/case=alltypes.237mfs/mode=decode_cooperative-2                     182.0 ± 0%   182.0 ± 0%       ~ (p=1.000 n=6) ¹
Decode/case=alltypes.237mfs/mode=stream_16k-2                             182.0 ± 0%   182.0 ± 0%       ~ (p=1.000 n=6) ¹
Decode/case=alltypes.237mfs/mode=stream_256-2                             524.0 ± 0%   524.0 ± 0%       ~ (p=1.000 n=6) ¹
Decode/case=alltypes.237mfs/mode=stream_256_cooperative-2                 524.0 ± 0%   524.0 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                                                   179.5        179.5       +0.00%
¹ all samples are equal

Mark hot Parser entry and scratch accessors #[inline] so cross-CGU callers in arrow inline them without requiring whole-program LTO. Derive Default and add in-place reset() and retained_memory_bytes() helpers on PromParserState and OpenMetricsParserState so ArrowDecoder can reuse parser scratch buffers in place across cooperative decode steps.

Signed-off-by: Bartek Plotka <bwplotka@gmail.com>
Replace the per-entry ActiveParser enum wrapper with an internal ScrapeParser specialization trait so ingest_entries monomorphizes directly over PromParser and OpenMetricsParser without per-entry enum branching.

Retain PromParserState and OpenMetricsParserState directly inside ArrowDecoder and store a 1-byte ParserKind tag in DecodeCursor, shrinking DecodeCursor from 344 bytes (with 5 owned Vecs) to 80 scalar bytes and avoiding dummy parser initialization and 272-byte enum moves on every cooperative step.

Raise DECODE_BUDGET_DURATION_CHECK_INTERVAL from 16 to 64 entries, precompute the per-step byte limit, and skip Instant::now() when a step cannot reach the first periodic clock check.

Signed-off-by: Bartek Plotka <bwplotka@gmail.com>
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.

1 participant