Skip to content

optimize sample unmarshaling for json/v2 - #991

Merged
bwplotka merged 4 commits into
prometheus:mainfrom
liggitt:json-v2-unmarshal
Sep 23, 2026
Merged

bwplotka merged 4 commits into
prometheus:mainfrom
liggitt:json-v2-unmarshal

Conversation

@liggitt

@liggitt liggitt commented Sep 20, 2026 •

Copy link
Copy Markdown
Contributor

Part of prometheus/client_golang#2105

  • Adds UnmarshalJSONFrom optimized streaming unmarshal implementations
  • Implementations are in build-tagged Go 1.27 files, previous version of Go still use less efficient unmarshalers
  • Adds a copy of the sample unmarshaling benchmark from client_golang
  • Adds unit tests to check parity between UnmarshalJSON and UnmarshalJSONFrom implementations

Benchmarks at https://gist.github.com/liggitt/ad7346af10affd28a83881f42f851c07

Go 1.27 performance is significantly improved:

benchstat before-go1.27 after-go1.27 
goos: darwin
goarch: arm64
pkg: github.com/prometheus/common/model
cpu: Apple M4 Pro
                                                            │  1-go1.27   │              3-go1.27              │
                                                            │   sec/op    │   sec/op     vs base               │
SamplesJSONUnmarshal/series=10/dp=10/type=floats-14           56.57µ ± 2%   15.17µ ± 3%  -73.18% (p=0.002 n=6)
SamplesJSONUnmarshal/series=10/dp=10/type=histograms-14       538.6µ ± 1%   252.9µ ± 1%  -53.04% (p=0.002 n=6)
SamplesJSONUnmarshal/series=10/dp=100/type=floats-14          499.9µ ± 0%   106.1µ ± 4%  -78.78% (p=0.002 n=6)
SamplesJSONUnmarshal/series=10/dp=100/type=histograms-14      5.379m ± 1%   2.491m ± 1%  -53.68% (p=0.002 n=6)
SamplesJSONUnmarshal/series=10/dp=1000/type=floats-14         5.015m ± 1%   1.029m ± 1%  -79.48% (p=0.002 n=6)
SamplesJSONUnmarshal/series=10/dp=1000/type=histograms-14     54.06m ± 1%   25.23m ± 1%  -53.32% (p=0.002 n=6)
SamplesJSONUnmarshal/series=100/dp=10/type=floats-14          571.9µ ± 1%   148.8µ ± 1%  -73.98% (p=0.002 n=6)
SamplesJSONUnmarshal/series=100/dp=10/type=histograms-14      5.478m ± 1%   2.547m ± 5%  -53.50% (p=0.002 n=6)
SamplesJSONUnmarshal/series=100/dp=100/type=floats-14         5.132m ± 1%   1.127m ± 2%  -78.05% (p=0.002 n=6)
SamplesJSONUnmarshal/series=100/dp=100/type=histograms-14     54.76m ± 1%   26.00m ± 3%  -52.53% (p=0.002 n=6)
SamplesJSONUnmarshal/series=100/dp=1000/type=floats-14        50.94m ± 1%   11.03m ± 2%  -78.34% (p=0.002 n=6)
SamplesJSONUnmarshal/series=100/dp=1000/type=histograms-14    551.0m ± 1%   267.8m ± 3%  -51.39% (p=0.002 n=6)
SamplesJSONUnmarshal/series=1000/dp=10/type=floats-14         5.744m ± 1%   1.604m ± 4%  -72.07% (p=0.002 n=6)
SamplesJSONUnmarshal/series=1000/dp=10/type=histograms-14     55.89m ± 1%   26.66m ± 4%  -52.30% (p=0.002 n=6)
SamplesJSONUnmarshal/series=1000/dp=100/type=floats-14        51.48m ± 1%   11.36m ± 2%  -77.94% (p=0.002 n=6)
SamplesJSONUnmarshal/series=1000/dp=100/type=histograms-14    557.5m ± 1%   266.1m ± 3%  -52.27% (p=0.002 n=6)
SamplesJSONUnmarshal/series=1000/dp=1000/type=floats-14       511.5m ± 1%   112.4m ± 3%  -78.02% (p=0.002 n=6)
SamplesJSONUnmarshal/series=1000/dp=1000/type=histograms-14    5.584 ± 1%    2.638 ± 1%  -52.75% (p=0.002 n=6)
geomean                                                       17.03m        5.638m       -66.89%

                                                            │   1-go1.27    │              3-go1.27               │
                                                            │     B/op      │     B/op      vs base               │
SamplesJSONUnmarshal/series=10/dp=10/type=floats-14           14.816Ki ± 0%   9.490Ki ± 0%  -35.95% (p=0.002 n=6)
SamplesJSONUnmarshal/series=10/dp=10/type=histograms-14       122.76Ki ± 0%   44.67Ki ± 0%  -63.61% (p=0.002 n=6)
SamplesJSONUnmarshal/series=10/dp=100/type=floats-14           92.10Ki ± 0%   44.52Ki ± 0%  -51.67% (p=0.002 n=6)
SamplesJSONUnmarshal/series=10/dp=100/type=histograms-14      1175.4Ki ± 0%   400.1Ki ± 0%  -65.96% (p=0.002 n=6)
SamplesJSONUnmarshal/series=10/dp=1000/type=floats-14          967.2Ki ± 0%   497.2Ki ± 0%  -48.59% (p=0.002 n=6)
SamplesJSONUnmarshal/series=10/dp=1000/type=histograms-14     11.335Mi ± 0%   3.775Mi ± 0%  -66.70% (p=0.002 n=6)
SamplesJSONUnmarshal/series=100/dp=10/type=floats-14          147.68Ki ± 0%   94.40Ki ± 0%  -36.08% (p=0.002 n=6)
SamplesJSONUnmarshal/series=100/dp=10/type=histograms-14      1227.3Ki ± 0%   446.2Ki ± 0%  -63.64% (p=0.002 n=6)
SamplesJSONUnmarshal/series=100/dp=100/type=floats-14          920.1Ki ± 0%   444.5Ki ± 0%  -51.69% (p=0.002 n=6)
SamplesJSONUnmarshal/series=100/dp=100/type=histograms-14     11.471Mi ± 0%   3.905Mi ± 0%  -65.95% (p=0.002 n=6)
SamplesJSONUnmarshal/series=100/dp=1000/type=floats-14         9.438Mi ± 0%   4.853Mi ± 0%  -48.58% (p=0.002 n=6)
SamplesJSONUnmarshal/series=100/dp=1000/type=histograms-14    113.28Mi ± 0%   37.74Mi ± 0%  -66.69% (p=0.002 n=6)
SamplesJSONUnmarshal/series=1000/dp=10/type=floats-14         1471.7Ki ± 0%   939.6Ki ± 0%  -36.16% (p=0.002 n=6)
SamplesJSONUnmarshal/series=1000/dp=10/type=histograms-14     11.973Mi ± 0%   4.352Mi ± 0%  -63.65% (p=0.002 n=6)
SamplesJSONUnmarshal/series=1000/dp=100/type=floats-14         8.975Mi ± 0%   4.335Mi ± 0%  -51.69% (p=0.002 n=6)
SamplesJSONUnmarshal/series=1000/dp=100/type=histograms-14    114.63Mi ± 0%   39.04Mi ± 0%  -65.95% (p=0.002 n=6)
SamplesJSONUnmarshal/series=1000/dp=1000/type=floats-14        94.37Mi ± 0%   48.53Mi ± 0%  -48.58% (p=0.002 n=6)
SamplesJSONUnmarshal/series=1000/dp=1000/type=histograms-14   1132.7Mi ± 0%   377.3Mi ± 0%  -66.69% (p=0.002 n=6)
geomean                                                        3.521Mi        1.523Mi       -56.74%

                                                            │   1-go1.27    │              3-go1.27              │
                                                            │   allocs/op   │  allocs/op   vs base               │
SamplesJSONUnmarshal/series=10/dp=10/type=floats-14              426.0 ± 0%    116.0 ± 0%  -72.77% (p=0.002 n=6)
SamplesJSONUnmarshal/series=10/dp=10/type=histograms-14         5.226k ± 0%   1.216k ± 0%  -76.73% (p=0.002 n=6)
SamplesJSONUnmarshal/series=10/dp=100/type=floats-14            3156.0 ± 0%    146.0 ± 0%  -95.37% (p=0.002 n=6)
SamplesJSONUnmarshal/series=10/dp=100/type=histograms-14        51.16k ± 0%   11.15k ± 0%  -78.21% (p=0.002 n=6)
SamplesJSONUnmarshal/series=10/dp=1000/type=floats-14          30197.0 ± 0%    186.0 ± 0%  -99.38% (p=0.002 n=6)
SamplesJSONUnmarshal/series=10/dp=1000/type=histograms-14       510.2k ± 0%   110.2k ± 0%  -78.41% (p=0.002 n=6)
SamplesJSONUnmarshal/series=100/dp=10/type=floats-14            4.209k ± 0%   1.109k ± 0%  -73.65% (p=0.002 n=6)
SamplesJSONUnmarshal/series=100/dp=10/type=histograms-14        52.21k ± 0%   12.11k ± 0%  -76.81% (p=0.002 n=6)
SamplesJSONUnmarshal/series=100/dp=100/type=floats-14          31.510k ± 0%   1.409k ± 0%  -95.53% (p=0.002 n=6)
SamplesJSONUnmarshal/series=100/dp=100/type=histograms-14       511.5k ± 0%   111.4k ± 0%  -78.22% (p=0.002 n=6)
SamplesJSONUnmarshal/series=100/dp=1000/type=floats-14        301.911k ± 0%   1.809k ± 0%  -99.40% (p=0.002 n=6)
SamplesJSONUnmarshal/series=100/dp=1000/type=histograms-14      5.102M ± 0%   1.102M ± 0%  -78.41% (p=0.002 n=6)
SamplesJSONUnmarshal/series=1000/dp=10/type=floats-14           42.03k ± 0%   11.03k ± 0%  -73.76% (p=0.002 n=6)
SamplesJSONUnmarshal/series=1000/dp=10/type=histograms-14       522.1k ± 0%   121.0k ± 0%  -76.82% (p=0.002 n=6)
SamplesJSONUnmarshal/series=1000/dp=100/type=floats-14         315.03k ± 0%   14.03k ± 0%  -95.55% (p=0.002 n=6)
SamplesJSONUnmarshal/series=1000/dp=100/type=histograms-14      5.115M ± 0%   1.114M ± 0%  -78.22% (p=0.002 n=6)
SamplesJSONUnmarshal/series=1000/dp=1000/type=floats-14       3019.04k ± 0%   18.03k ± 0%  -99.40% (p=0.002 n=6)
SamplesJSONUnmarshal/series=1000/dp=1000/type=histograms-14     51.02M ± 0%   11.02M ± 0%  -78.41% (p=0.002 n=6)
geomean                                                         132.8k        12.77k       -90.38%

Go 1.26 CPU performance is slightly improved by dropping an unnecessary custom UnmarshalJSON method:

benchstat before-go1.26 after-go1.26 
goos: darwin
goarch: arm64
pkg: github.com/prometheus/common/model
cpu: Apple M4 Pro
                                                            │   1-go1.26   │              3-go1.26              │
                                                            │    sec/op    │   sec/op     vs base               │
SamplesJSONUnmarshal/series=10/dp=10/type=floats-14           59.13µ ±  3%   44.60µ ± 3%  -24.58% (p=0.002 n=6)
SamplesJSONUnmarshal/series=10/dp=10/type=histograms-14       760.4µ ±  1%   586.7µ ± 0%  -22.84% (p=0.002 n=6)
SamplesJSONUnmarshal/series=10/dp=100/type=floats-14          476.0µ ±  4%   382.1µ ± 1%  -19.73% (p=0.002 n=6)
SamplesJSONUnmarshal/series=10/dp=100/type=histograms-14      7.449m ±  1%   5.880m ± 1%  -21.07% (p=0.002 n=6)
SamplesJSONUnmarshal/series=10/dp=1000/type=floats-14         4.698m ±  1%   3.799m ± 2%  -19.13% (p=0.002 n=6)
SamplesJSONUnmarshal/series=10/dp=1000/type=histograms-14     76.79m ±  3%   59.98m ± 1%  -21.89% (p=0.002 n=6)
SamplesJSONUnmarshal/series=100/dp=10/type=floats-14          586.1µ ±  2%   451.7µ ± 1%  -22.94% (p=0.002 n=6)
SamplesJSONUnmarshal/series=100/dp=10/type=histograms-14      7.550m ±  1%   5.947m ± 0%  -21.22% (p=0.002 n=6)
SamplesJSONUnmarshal/series=100/dp=100/type=floats-14         4.714m ±  0%   3.855m ± 2%  -18.22% (p=0.002 n=6)
SamplesJSONUnmarshal/series=100/dp=100/type=histograms-14     75.83m ±  1%   59.81m ± 0%  -21.14% (p=0.002 n=6)
SamplesJSONUnmarshal/series=100/dp=1000/type=floats-14        47.63m ±  3%   38.68m ± 3%  -18.80% (p=0.002 n=6)
SamplesJSONUnmarshal/series=100/dp=1000/type=histograms-14    839.1m ± 43%   617.9m ± 0%  -26.36% (p=0.002 n=6)
SamplesJSONUnmarshal/series=1000/dp=10/type=floats-14         6.479m ± 11%   4.566m ± 1%  -29.53% (p=0.002 n=6)
SamplesJSONUnmarshal/series=1000/dp=10/type=histograms-14     81.97m ±  2%   60.11m ± 1%  -26.67% (p=0.002 n=6)
SamplesJSONUnmarshal/series=1000/dp=100/type=floats-14        51.16m ±  2%   39.35m ± 1%  -23.10% (p=0.002 n=6)
SamplesJSONUnmarshal/series=1000/dp=100/type=histograms-14    804.5m ±  1%   619.6m ± 1%  -22.98% (p=0.002 n=6)
SamplesJSONUnmarshal/series=1000/dp=1000/type=floats-14       478.6m ±  2%   394.4m ± 1%  -17.59% (p=0.002 n=6)
SamplesJSONUnmarshal/series=1000/dp=1000/type=histograms-14    7.861 ±  2%    6.218 ± 1%  -20.89% (p=0.002 n=6)
geomean                                                       20.15m         15.67m       -22.21%

                                                            │   1-go1.26   │              3-go1.26               │
                                                            │     B/op     │     B/op      vs base               │
SamplesJSONUnmarshal/series=10/dp=10/type=floats-14           31.16Ki ± 0%   28.02Ki ± 0%  -10.08% (p=0.002 n=6)
SamplesJSONUnmarshal/series=10/dp=10/type=histograms-14       220.9Ki ± 0%   217.1Ki ± 0%   -1.71% (p=0.002 n=6)
SamplesJSONUnmarshal/series=10/dp=100/type=floats-14          227.9Ki ± 0%   224.7Ki ± 0%   -1.38% (p=0.002 n=6)
SamplesJSONUnmarshal/series=10/dp=100/type=histograms-14      2.073Mi ± 0%   2.070Mi ± 0%   -0.18% (p=0.002 n=6)
SamplesJSONUnmarshal/series=10/dp=1000/type=floats-14         2.244Mi ± 0%   2.241Mi ± 0%   -0.14% (p=0.002 n=6)
SamplesJSONUnmarshal/series=10/dp=1000/type=histograms-14     20.56Mi ± 0%   20.56Mi ± 0%   -0.02% (p=0.002 n=6)
SamplesJSONUnmarshal/series=100/dp=10/type=floats-14          309.4Ki ± 0%   277.4Ki ± 0%  -10.33% (p=0.002 n=6)
SamplesJSONUnmarshal/series=100/dp=10/type=histograms-14      2.155Mi ± 0%   2.117Mi ± 0%   -1.73% (p=0.002 n=6)
SamplesJSONUnmarshal/series=100/dp=100/type=floats-14         2.223Mi ± 0%   2.192Mi ± 0%   -1.40% (p=0.002 n=6)
SamplesJSONUnmarshal/series=100/dp=100/type=histograms-14     20.73Mi ± 0%   20.69Mi ± 0%   -0.18% (p=0.002 n=6)
SamplesJSONUnmarshal/series=100/dp=1000/type=floats-14        22.43Mi ± 0%   22.40Mi ± 0%   -0.14% (p=0.002 n=6)
SamplesJSONUnmarshal/series=100/dp=1000/type=histograms-14    205.6Mi ± 0%   205.6Mi ± 0%   -0.02% (p=0.002 n=6)
SamplesJSONUnmarshal/series=1000/dp=10/type=floats-14         3.015Mi ± 0%   2.703Mi ± 0%  -10.37% (p=0.002 n=6)
SamplesJSONUnmarshal/series=1000/dp=10/type=histograms-14     21.54Mi ± 0%   21.17Mi ± 0%   -1.74% (p=0.002 n=6)
SamplesJSONUnmarshal/series=1000/dp=100/type=floats-14        22.23Mi ± 0%   21.91Mi ± 0%   -1.41% (p=0.002 n=6)
SamplesJSONUnmarshal/series=1000/dp=100/type=histograms-14    207.3Mi ± 0%   206.9Mi ± 0%   -0.18% (p=0.002 n=6)
SamplesJSONUnmarshal/series=1000/dp=1000/type=floats-14       224.3Mi ± 0%   224.0Mi ± 0%   -0.14% (p=0.002 n=6)
SamplesJSONUnmarshal/series=1000/dp=1000/type=histograms-14   2.008Gi ± 0%   2.008Gi ± 0%   -0.02% (p=0.002 n=6)
geomean                                                       7.195Mi        7.025Mi        -2.36%

                                                            │  1-go1.26   │              3-go1.26              │
                                                            │  allocs/op  │  allocs/op   vs base               │
SamplesJSONUnmarshal/series=10/dp=10/type=floats-14            530.0 ± 0%    462.0 ± 0%  -12.83% (p=0.002 n=6)
SamplesJSONUnmarshal/series=10/dp=10/type=histograms-14       4.541k ± 0%   4.463k ± 0%   -1.72% (p=0.002 n=6)
SamplesJSONUnmarshal/series=10/dp=100/type=floats-14          3.260k ± 0%   3.192k ± 0%   -2.09% (p=0.002 n=6)
SamplesJSONUnmarshal/series=10/dp=100/type=histograms-14      43.27k ± 0%   43.19k ± 0%   -0.18% (p=0.002 n=6)
SamplesJSONUnmarshal/series=10/dp=1000/type=floats-14         30.30k ± 0%   30.23k ± 0%   -0.22% (p=0.002 n=6)
SamplesJSONUnmarshal/series=10/dp=1000/type=histograms-14     430.3k ± 0%   430.2k ± 0%   -0.02% (p=0.002 n=6)
SamplesJSONUnmarshal/series=100/dp=10/type=floats-14          5.213k ± 0%   4.515k ± 0%  -13.39% (p=0.002 n=6)
SamplesJSONUnmarshal/series=100/dp=10/type=histograms-14      45.31k ± 0%   44.52k ± 0%   -1.76% (p=0.002 n=6)
SamplesJSONUnmarshal/series=100/dp=100/type=floats-14         32.51k ± 0%   31.82k ± 0%   -2.15% (p=0.002 n=6)
SamplesJSONUnmarshal/series=100/dp=100/type=histograms-14     432.6k ± 0%   431.8k ± 0%   -0.18% (p=0.002 n=6)
SamplesJSONUnmarshal/series=100/dp=1000/type=floats-14        302.9k ± 0%   302.2k ± 0%   -0.23% (p=0.002 n=6)
SamplesJSONUnmarshal/series=100/dp=1000/type=histograms-14    4.303M ± 0%   4.302M ± 0%   -0.02% (p=0.002 n=6)
SamplesJSONUnmarshal/series=1000/dp=10/type=floats-14         52.02k ± 0%   45.02k ± 0%  -13.45% (p=0.002 n=6)
SamplesJSONUnmarshal/series=1000/dp=10/type=histograms-14     453.0k ± 0%   445.0k ± 0%   -1.77% (p=0.002 n=6)
SamplesJSONUnmarshal/series=1000/dp=100/type=floats-14        325.0k ± 0%   318.0k ± 0%   -2.15% (p=0.002 n=6)
SamplesJSONUnmarshal/series=1000/dp=100/type=histograms-14    4.326M ± 0%   4.318M ± 0%   -0.18% (p=0.002 n=6)
SamplesJSONUnmarshal/series=1000/dp=1000/type=floats-14       3.029M ± 0%   3.022M ± 0%   -0.23% (p=0.002 n=6)
SamplesJSONUnmarshal/series=1000/dp=1000/type=histograms-14   43.03M ± 0%   43.02M ± 0%   -0.02% (p=0.002 n=6)
geomean                                                       127.8k        123.9k        -3.04%

cc @bwplotka

@liggitt
liggitt force-pushed the json-v2-unmarshal branch 2 times, most recently from 68760d9 to cef8d67 Compare September 21, 2026 14:44
liggitt added a commit to liggitt/client_golang that referenced this pull request Sep 21, 2026
Signed-off-by: Jordan Liggitt <liggitt@google.com>
@liggitt liggitt changed the title WIP: optimize sample unmarshaling for json/v2 optimize sample unmarshaling for json/v2 Sep 21, 2026
Comment thread .github/workflows/ci.yml
@liggitt
liggitt force-pushed the json-v2-unmarshal branch 2 times, most recently from 193afc6 to f9f7ae4 Compare September 21, 2026 19:32
liggitt added a commit to liggitt/client_golang that referenced this pull request Sep 21, 2026
Signed-off-by: Jordan Liggitt <liggitt@google.com>
liggitt added a commit to liggitt/client_golang that referenced this pull request Sep 22, 2026
Signed-off-by: Jordan Liggitt <liggitt@google.com>

@bwplotka bwplotka left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks! Generally ok, but I have some suggestion. Especially around error handling, see comments.

Comment thread model/value_histogram127.go Outdated
Comment thread model/value_histogram127.go
Comment thread model/value_float127.go Outdated
Comment thread model/value_float127_test.go Outdated
Comment thread model/value127_test.go Outdated
Comment thread model/time127_test.go
Comment thread model/value127_test.go
Comment thread model/value_float127.go
Comment thread model/value_histogram127.go
Comment thread model/value127_test.go Outdated
@liggitt
liggitt force-pushed the json-v2-unmarshal branch 2 times, most recently from bf10fd9 to 5d6d890 Compare September 22, 2026 16:57
@liggitt

liggitt commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor Author

all comments except #991 (comment) addressed in fixup! commits, will squash once re-reviewed

Let me know if you want me to break apart the if/else blocks for #991 (comment) ... I feel like it makes things less readable by letting token / value variables escape scope, but it's up to you

liggitt added a commit to liggitt/client_golang that referenced this pull request Sep 22, 2026
Signed-off-by: Jordan Liggitt <liggitt@google.com>

@bwplotka bwplotka left a comment •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks! I'd personally still unroll else cases, but not a big deal.

Comment thread model/value_float127.go
@bwplotka

Copy link
Copy Markdown
Member

Let's stick to your version #991 (comment)

@bwplotka

Copy link
Copy Markdown
Member

all comments except #991 (comment) addressed in fixup! commits, will squash once re-reviewed

I assume you want to clean the history before merging

Signed-off-by: Jordan Liggitt <liggitt@google.com>
Signed-off-by: Jordan Liggitt <liggitt@google.com>
5-20% CPU improvement
0-12% alloc/memory improvement

Signed-off-by: Jordan Liggitt <liggitt@google.com>
Signed-off-by: Jordan Liggitt <liggitt@google.com>
@liggitt

liggitt commented Sep 23, 2026

Copy link
Copy Markdown
Contributor Author

rebased and squashed

@bwplotka
bwplotka merged commit c57e58a into prometheus:main Sep 23, 2026
9 checks passed
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.

2 participants