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

Fix: Switch to lazy init() in decoder and encoder #490

Merged
merged 2 commits into from
Dec 11, 2024
Merged

Conversation

toddtreece
Copy link
Contributor

@toddtreece toddtreece commented Dec 19, 2023

Why

See: grafana/grafana#78651 (edit: new PR grafana/grafana#95969)

There is additional memory overhead in Grafana due the import of go-json in Apache Arrow. It looks like this is caused by init() initializing global variables in the encoder and decoder.

Grafana uses Apache Arrow, but does not currently use Arrow's JSON encoding/decoding features, so it would be ideal if we could avoid the additional memory overhead until JSON encoding/decoding is used.

This shows the impact of rolling this out to a couple of our rolling release channels in a production cluster:

image

What

Switch to lazy initialization of global variables in decoder and encoder.

@toddtreece
Copy link
Contributor Author

@goccy i apologize for pinging you directly, but could you take a quick look at this one when you have a chance?

@codecov-commenter
Copy link

codecov-commenter commented Nov 12, 2024

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

Attention: Patch coverage is 55.55556% with 8 lines in your changes missing coverage. Please review.

Project coverage is 76.72%. Comparing base (3e9769d) to head (a119ee5).

❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #490      +/-   ##
==========================================
- Coverage   76.74%   76.72%   -0.03%     
==========================================
  Files          55       55              
  Lines       18925    18931       +6     
==========================================
  Hits        14524    14524              
- Misses       3769     3774       +5     
- Partials      632      633       +1     

@goccy
Copy link
Owner

goccy commented Nov 12, 2024

Thank you for your contribution ! I'm concerned about the performance overhead caused by deferred execution. Could you check how the benchmark results change before and after the modification ?

DennisRasey pushed a commit to DennisRasey/forgejo that referenced this pull request Nov 13, 2024
- This uses a forked version of https://github.com/goccy/go-json, that
has [this pull request](goccy/go-json#490)
applied. It reduces the heap memory usage by 8MiB (idle heap usage from
startup: 40126.59kB -> 32073.56kB). This should be generally safe to
replace as goccy/go-json doesn't see frequent updates and the other user
of this fork is grafana which is another big Go project.
- The only user of this library is minio, but having a configuration
with minio is not a common setup, AFAIK, so this is essentialy wasted
memory for most Forgejo instances. Having it lazy-loaded solves that
problem.
@toddtreece
Copy link
Contributor Author

toddtreece commented Nov 15, 2024

Could you check how the benchmark results change before and after the modification?

@goccy the commands and results are below. i used a regex to exclude the benchmarks for the other json libraries.

i ran this on the grafana/go-json fork:

go test -bench="Benchmark(.*GoJson[a-zA-Z0-9]*|[a-zA-Z0-9]+)" -count=10 -timeout 30m | tee grafana.txt

and this on goccy/go-json:

go test -bench="Benchmark(.*GoJson[a-zA-Z0-9]*|[a-zA-Z0-9]+)" -count=10 -timeout 30m | tee goccy.txt

and then running benchstat:

benchstat goccy.txt grafana.txt > benchstat.txt
goos: linux
goarch: amd64
pkg: benchmark
cpu: AMD Ryzen 9 7950X 16-Core Processor            
                                                            │  goccy.txt   │              grafana.txt              │
                                                            │    sec/op    │    sec/op      vs base                │
_EncodeBigData_GoJson-32                                      98.33µ ±  0%   102.77µ ±  0%   +4.52% (p=0.000 n=10)
_MarshalBigData_GoJson-32                                     144.4µ ±  5%    156.0µ ±  2%   +8.02% (p=0.000 n=10)
_MarshalBytes_GoJson/32-32                                    94.55n ±  3%    95.98n ±  4%        ~ (p=0.165 n=10)
_MarshalBytes_GoJson/256-32                                   263.2n ±  1%    268.0n ±  1%   +1.80% (p=0.008 n=10)
_MarshalBytes_GoJson/4096-32                                  3.121µ ±  1%    3.356µ ±  1%   +7.53% (p=0.000 n=10)
_EncodeRawMessage_GoJson-32                                   5.914n ± 20%    5.937n ± 21%        ~ (p=0.143 n=10)
_MarshalString_GoJson-32                                      10.39n ±  1%    10.62n ±  1%   +2.21% (p=0.000 n=10)
CodeDecoder-32                                                309.7µ ±  0%    308.5µ ±  0%   -0.40% (p=0.023 n=10)
UnicodeDecoder-32                                             103.2n ±  4%    101.1n ±  3%        ~ (p=0.324 n=10)
DecoderStream-32                                              61.43n ±  4%    62.14n ±  4%        ~ (p=0.256 n=10)
CodeUnmarshal-32                                              201.5µ ±  2%    207.4µ ±  4%   +2.93% (p=0.043 n=10)
CodeUnmarshalReuse-32                                         171.5µ ±  1%    174.2µ ±  1%   +1.54% (p=0.001 n=10)
UnmarshalString-32                                            5.482n ±  2%    5.497n ±  1%        ~ (p=0.696 n=10)
UnmarshalFloat64-32                                           5.995n ±  4%    5.893n ±  3%        ~ (p=0.105 n=10)
UnmarshalInt64-32                                             4.501n ± 24%    4.590n ±  4%        ~ (p=0.971 n=10)
Issue10335-32                                                 5.404n ±  1%    5.443n ±  1%   +0.73% (p=0.004 n=10)
Unmapped-32                                                   16.11n ±  0%    15.91n ±  1%   -1.24% (p=0.000 n=10)
_Compact_GoJson-32                                            3.842m ±  2%    3.904m ±  4%        ~ (p=0.075 n=10)
_Indent_GoJson-32                                             8.413m ±  3%    8.619m ±  2%   +2.45% (p=0.001 n=10)
_Decode_SmallStruct_Unmarshal_GoJson-32                       277.9n ±  2%    285.1n ±  2%   +2.55% (p=0.004 n=10)
_Decode_SmallStruct_Unmarshal_GoJsonNoEscape-32               213.5n ±  2%    215.3n ±  3%        ~ (p=0.542 n=10)
_Decode_SmallStruct_Stream_GoJson-32                          476.6n ±  1%    474.1n ±  1%        ~ (p=0.305 n=10)
_Decode_MediumStruct_Unmarshal_GoJson-32                      1.968µ ±  1%    1.977µ ±  1%        ~ (p=0.255 n=10)
_Decode_MediumStruct_Unmarshal_GoJsonNoEscape-32              1.944µ ±  1%    1.948µ ±  1%        ~ (p=0.382 n=10)
_Decode_MediumStruct_Stream_GoJson-32                         2.991µ ±  1%    3.036µ ±  1%   +1.49% (p=0.006 n=10)
_Decode_LargeStruct_Unmarshal_GoJson-32                       26.25µ ±  1%    26.97µ ±  1%   +2.75% (p=0.000 n=10)
_Decode_LargeStruct_Unmarshal_GoJsonNoEscape-32               26.26µ ±  2%    26.88µ ±  1%   +2.39% (p=0.000 n=10)
_Decode_LargeStruct_Unmarshal_GoJsonFirstWinMode-32           25.77µ ±  1%    26.84µ ±  1%   +4.19% (p=0.000 n=10)
_Decode_LargeStruct_Unmarshal_GoJsonNoEscapeFirstWinMode-32   25.70µ ±  1%    26.85µ ±  1%   +4.49% (p=0.000 n=10)
_Decode_LargeStruct_Stream_GoJson-32                          36.99µ ±  1%    37.41µ ±  1%   +1.14% (p=0.035 n=10)
_Decode_LargeStruct_Stream_GoJsonFirstWinMode-32              35.10µ ±  1%    35.18µ ±  2%        ~ (p=0.928 n=10)
_Decode_LargeSlice_EscapedString_GoJson-32                    532.9µ ±  1%    529.8µ ±  1%        ~ (p=0.190 n=10)
_Encode_SmallStruct_GoJsonColored-32                          312.6n ±  1%    317.3n ±  1%   +1.50% (p=0.002 n=10)
_Encode_SmallStruct_GoJson-32                                 191.6n ±  2%    198.5n ±  2%   +3.60% (p=0.000 n=10)
_Encode_SmallStruct_GoJsonNoEscape-32                         148.4n ±  3%    152.4n ±  3%   +2.66% (p=0.003 n=10)
_Encode_SmallStructCached_GoJsonColored-32                    261.1n ±  1%    267.4n ±  1%   +2.39% (p=0.000 n=10)
_Encode_SmallStructCached_GoJson-32                           145.9n ±  3%    154.5n ±  2%   +5.89% (p=0.000 n=10)
_Encode_SmallStructCached_GoJsonNoEscape-32                   145.9n ±  3%    151.8n ±  2%   +4.04% (p=0.000 n=10)
_Encode_MediumStruct_GoJsonColored-32                         903.9n ±  2%    917.2n ±  1%   +1.47% (p=0.022 n=10)
_Encode_MediumStruct_GoJson-32                                715.9n ±  3%    724.0n ±  1%        ~ (p=0.085 n=10)
_Encode_MediumStruct_GoJsonNoEscape-32                        308.6n ±  1%    315.1n ±  2%   +2.12% (p=0.001 n=10)
_Encode_MediumStructCached_GoJsonColored-32                   489.0n ±  1%    499.4n ±  1%   +2.12% (p=0.000 n=10)
_Encode_MediumStructCached_GoJson-32                          308.2n ±  1%    316.3n ±  1%   +2.63% (p=0.000 n=10)
_Encode_MediumStructCached_GoJsonNoEscape-32                  304.2n ±  2%    313.3n ±  1%   +2.99% (p=0.000 n=10)
_Encode_LargeStruct_GoJsonColored-32                          18.63µ ±  1%    18.86µ ±  1%   +1.25% (p=0.023 n=10)
_Encode_LargeStruct_GoJson-32                                 14.60µ ±  1%    14.93µ ±  2%   +2.28% (p=0.007 n=10)
_Encode_LargeStruct_GoJsonNoEscape-32                         14.45µ ±  2%    14.63µ ±  2%   +1.22% (p=0.035 n=10)
_Encode_LargeStructCached_GoJsonColored-32                    8.377µ ±  1%    8.983µ ±  1%   +7.23% (p=0.000 n=10)
_Encode_LargeStructCached_GoJson-32                           4.360µ ±  2%    5.031µ ±  1%  +15.40% (p=0.000 n=10)
_Encode_LargeStructCached_GoJsonNoEscape-32                   4.356µ ±  2%    4.746µ ±  1%   +8.97% (p=0.000 n=10)
_Encode_MapInterface_GoJson-32                                318.0n ±  1%    333.3n ±  3%   +4.80% (p=0.000 n=10)
_Encode_Interface_GoJson-32                                   66.95n ±  3%    68.88n ±  2%   +2.88% (p=0.009 n=10)
_Encode_Bool_GoJson-32                                        25.09n ±  1%    26.89n ±  3%   +7.15% (p=0.001 n=10)
_Marshal_Bool_GoJson-32                                       26.85n ±  3%    27.78n ±  2%   +3.45% (p=0.002 n=10)
_Encode_Int_GoJson-32                                         24.21n ±  2%    26.18n ±  1%   +8.18% (p=0.000 n=10)
_Encode_MarshalJSON_GoJson-32                                 63.62n ±  1%    64.80n ±  4%        ~ (p=0.063 n=10)
_Decode_SmallStruct_UnmarshalPath_GoJson-32                   536.6n ±  3%    506.8n ±  2%   -5.55% (p=0.001 n=10)
_Decode_SlowReader_GoJson/chunksize_16384-32                  56.29µ ±  1%    56.66µ ±  1%   +0.65% (p=0.015 n=10)
_Decode_SlowReader_GoJson/chunksize_4096-32                   58.70µ ±  1%    58.96µ ±  0%        ~ (p=0.123 n=10)
_Decode_SlowReader_GoJson/chunksize_1024-32                   58.05µ ±  1%    58.49µ ±  1%        ~ (p=0.143 n=10)
_Decode_SlowReader_GoJson/chunksize_256-32                    62.92µ ±  1%    63.34µ ±  1%        ~ (p=0.190 n=10)
_Decode_SlowReader_GoJson/chunksize_64-32                     77.27µ ±  1%    77.99µ ±  2%        ~ (p=0.052 n=10)
geomean                                                       1.549µ          1.585µ         +2.34%

                          │  goccy.txt   │             grafana.txt             │
                          │     B/s      │     B/s       vs base               │
_EncodeBigData_GoJson-32    18.38Gi ± 0%   17.58Gi ± 0%  -4.33% (p=0.000 n=10)
_MarshalBigData_GoJson-32   12.51Gi ± 4%   11.59Gi ± 2%  -7.42% (p=0.000 n=10)
CodeDecoder-32              5.836Gi ± 0%   5.859Gi ± 0%  +0.40% (p=0.023 n=10)
UnicodeDecoder-32           129.4Mi ± 4%   132.0Mi ± 3%       ~ (p=0.353 n=10)
CodeUnmarshal-32            8.970Gi ± 3%   8.715Gi ± 4%  -2.84% (p=0.043 n=10)
CodeUnmarshalReuse-32       10.54Gi ± 1%   10.38Gi ± 1%  -1.52% (p=0.001 n=10)
geomean                     5.021Gi        4.904Gi       -2.33%

                                                            │    goccy.txt     │                grafana.txt                │
                                                            │       B/op       │       B/op        vs base                 │
_EncodeBigData_GoJson-32                                        1.000 ± 87100%     1.000 ± 91400%       ~ (p=0.721 n=10)
_MarshalBigData_GoJson-32                                     1.857Mi ±     0%   1.859Mi ±     1%       ~ (p=0.393 n=10)
_MarshalBytes_GoJson/32-32                                      64.00 ±     0%     64.00 ±     0%       ~ (p=1.000 n=10) ¹
_MarshalBytes_GoJson/256-32                                     384.0 ±     0%     384.0 ±     0%       ~ (p=1.000 n=10) ¹
_MarshalBytes_GoJson/4096-32                                  6.007Ki ±     0%   6.007Ki ±     0%       ~ (p=0.087 n=10)
_EncodeRawMessage_GoJson-32                                     4.000 ±     0%     4.000 ±     0%       ~ (p=1.000 n=10) ¹
_MarshalString_GoJson-32                                        40.00 ±     0%     40.00 ±     0%       ~ (p=1.000 n=10) ¹
CodeDecoder-32                                                1.894Mi ±     0%   1.894Mi ±     0%       ~ (p=0.853 n=10)
UnicodeDecoder-32                                               32.00 ±     0%     32.00 ±     0%       ~ (p=1.000 n=10) ¹
DecoderStream-32                                                16.00 ±     0%     16.00 ±     0%       ~ (p=1.000 n=10) ¹
CodeUnmarshal-32                                              2.932Mi ±     0%   2.932Mi ±     0%       ~ (p=0.754 n=10)
CodeUnmarshalReuse-32                                         1.857Mi ±     0%   1.857Mi ±     0%       ~ (p=0.481 n=10)
UnmarshalString-32                                              16.00 ±     0%     16.00 ±     0%       ~ (p=1.000 n=10) ¹
UnmarshalFloat64-32                                             5.000 ±     0%     5.000 ±     0%       ~ (p=1.000 n=10) ¹
UnmarshalInt64-32                                               2.000 ±     0%     2.000 ±     0%       ~ (p=1.000 n=10) ¹
Issue10335-32                                                   16.00 ±     0%     16.00 ±     0%       ~ (p=1.000 n=10) ¹
Unmapped-32                                                     64.00 ±     0%     64.00 ±     0%       ~ (p=1.000 n=10) ¹
_Compact_GoJson-32                                            2.042Mi ±     3%   2.055Mi ±     3%       ~ (p=0.481 n=10)
_Indent_GoJson-32                                             4.766Mi ±     0%   4.770Mi ±     0%  +0.08% (p=0.019 n=10)
_Decode_SmallStruct_Unmarshal_GoJson-32                         256.0 ±     0%     256.0 ±     0%       ~ (p=1.000 n=10) ¹
_Decode_SmallStruct_Unmarshal_GoJsonNoEscape-32                 144.0 ±     0%     144.0 ±     0%       ~ (p=1.000 n=10) ¹
_Decode_SmallStruct_Stream_GoJson-32                            752.0 ±     0%     752.0 ±     0%       ~ (p=1.000 n=10) ¹
_Decode_MediumStruct_Unmarshal_GoJson-32                      2.371Ki ±     0%   2.371Ki ±     0%       ~ (p=1.000 n=10) ¹
_Decode_MediumStruct_Unmarshal_GoJsonNoEscape-32              2.348Ki ±     0%   2.348Ki ±     0%       ~ (p=1.000 n=10) ¹
_Decode_MediumStruct_Stream_GoJson-32                         3.745Ki ±     0%   3.745Ki ±     0%       ~ (p=1.000 n=10) ¹
_Decode_LargeStruct_Unmarshal_GoJson-32                       29.86Ki ±     0%   29.86Ki ±     0%       ~ (p=0.487 n=10)
_Decode_LargeStruct_Unmarshal_GoJsonNoEscape-32               29.83Ki ±     0%   29.83Ki ±     0%       ~ (p=0.143 n=10)
_Decode_LargeStruct_Unmarshal_GoJsonFirstWinMode-32           29.86Ki ±     0%   29.86Ki ±     0%       ~ (p=0.135 n=10)
_Decode_LargeStruct_Unmarshal_GoJsonNoEscapeFirstWinMode-32   29.83Ki ±     0%   29.83Ki ±     0%       ~ (p=0.363 n=10)
_Decode_LargeStruct_Stream_GoJson-32                          33.46Ki ±     0%   33.46Ki ±     0%       ~ (p=0.296 n=10)
_Decode_LargeStruct_Stream_GoJsonFirstWinMode-32              33.46Ki ±     0%   33.46Ki ±     0%       ~ (p=0.773 n=10)
_Decode_LargeSlice_EscapedString_GoJson-32                    567.8Ki ±     0%   565.6Ki ±     0%  -0.37% (p=0.002 n=10)
_Encode_SmallStruct_GoJsonColored-32                            432.0 ±     0%     432.0 ±     0%       ~ (p=1.000 n=10) ¹
_Encode_SmallStruct_GoJson-32                                   256.0 ±     0%     256.0 ±     0%       ~ (p=1.000 n=10) ¹
_Encode_SmallStruct_GoJsonNoEscape-32                           144.0 ±     0%     144.0 ±     0%       ~ (p=1.000 n=10) ¹
_Encode_SmallStructCached_GoJsonColored-32                      320.0 ±     0%     320.0 ±     0%       ~ (p=1.000 n=10) ¹
_Encode_SmallStructCached_GoJson-32                             144.0 ±     0%     144.0 ±     0%       ~ (p=1.000 n=10) ¹
_Encode_SmallStructCached_GoJsonNoEscape-32                     144.0 ±     0%     144.0 ±     0%       ~ (p=1.000 n=10) ¹
_Encode_MediumStruct_GoJsonColored-32                           865.0 ±     0%     865.0 ±     0%       ~ (p=1.000 n=10)
_Encode_MediumStruct_GoJson-32                                  609.0 ±     0%     609.0 ±     0%       ~ (p=1.000 n=10) ¹
_Encode_MediumStruct_GoJsonNoEscape-32                          320.0 ±     0%     320.0 ±     0%       ~ (p=1.000 n=10) ¹
_Encode_MediumStructCached_GoJsonColored-32                     576.0 ±     0%     576.0 ±     0%       ~ (p=1.000 n=10) ¹
_Encode_MediumStructCached_GoJson-32                            320.0 ±     0%     320.0 ±     0%       ~ (p=1.000 n=10) ¹
_Encode_MediumStructCached_GoJsonNoEscape-32                    320.0 ±     0%     320.0 ±     0%       ~ (p=1.000 n=10) ¹
_Encode_LargeStruct_GoJsonColored-32                          20.15Ki ±     0%   20.15Ki ±     0%       ~ (p=0.839 n=10)
_Encode_LargeStruct_GoJson-32                                 13.90Ki ±     0%   13.90Ki ±     0%       ~ (p=0.960 n=10)
_Encode_LargeStruct_GoJsonNoEscape-32                         13.90Ki ±     0%   13.90Ki ±     0%       ~ (p=0.810 n=10)
_Encode_LargeStructCached_GoJsonColored-32                    10.64Ki ±     0%   10.64Ki ±     0%       ~ (p=0.397 n=10)
_Encode_LargeStructCached_GoJson-32                           4.755Ki ±     0%   4.755Ki ±     0%       ~ (p=0.474 n=10)
_Encode_LargeStructCached_GoJsonNoEscape-32                   4.755Ki ±     0%   4.755Ki ±     0%       ~ (p=0.582 n=10)
_Encode_MapInterface_GoJson-32                                  48.00 ±     0%     48.00 ±     0%       ~ (p=1.000 n=10) ¹
_Encode_Interface_GoJson-32                                     27.00 ±     0%     27.00 ±     0%       ~ (p=1.000 n=10) ¹
_Encode_Bool_GoJson-32                                          10.00 ±    10%     11.00 ±    18%       ~ (p=0.052 n=10)
_Marshal_Bool_GoJson-32                                         4.000 ±     0%     4.000 ±     0%       ~ (p=1.000 n=10) ¹
_Encode_Int_GoJson-32                                           1.000 ±     0%     1.000 ±     0%       ~ (p=1.000 n=10) ¹
_Encode_MarshalJSON_GoJson-32                                   16.00 ±     0%     16.00 ±     0%       ~ (p=1.000 n=10) ¹
_Decode_SmallStruct_UnmarshalPath_GoJson-32                     272.0 ±     0%     272.0 ±     0%       ~ (p=1.000 n=10) ¹
_Decode_SlowReader_GoJson/chunksize_16384-32                  74.57Ki ±     0%   74.57Ki ±     0%       ~ (p=0.999 n=10)
_Decode_SlowReader_GoJson/chunksize_4096-32                   74.57Ki ±     0%   74.57Ki ±     0%  -0.01% (p=0.003 n=10)
_Decode_SlowReader_GoJson/chunksize_1024-32                   71.56Ki ±     0%   71.56Ki ±     0%       ~ (p=1.000 n=10)
_Decode_SlowReader_GoJson/chunksize_256-32                    70.64Ki ±     0%   70.64Ki ±     0%       ~ (p=0.513 n=10)
_Decode_SlowReader_GoJson/chunksize_64-32                     70.56Ki ±     0%   70.56Ki ±     0%       ~ (p=0.647 n=10)
geomean                                                       1.316Ki            1.318Ki           +0.16%
¹ all samples are equal

                                                            │   goccy.txt    │              grafana.txt              │
                                                            │   allocs/op    │  allocs/op    vs base                 │
_EncodeBigData_GoJson-32                                       0.000 ±  0%      0.000 ±  0%       ~ (p=1.000 n=10) ¹
_MarshalBigData_GoJson-32                                      1.000 ±  0%      1.000 ±  0%       ~ (p=1.000 n=10) ¹
_MarshalBytes_GoJson/32-32                                     1.000 ±  0%      1.000 ±  0%       ~ (p=1.000 n=10) ¹
_MarshalBytes_GoJson/256-32                                    1.000 ±  0%      1.000 ±  0%       ~ (p=1.000 n=10) ¹
_MarshalBytes_GoJson/4096-32                                   1.000 ±  0%      1.000 ±  0%       ~ (p=1.000 n=10) ¹
_EncodeRawMessage_GoJson-32                                    1.000 ±  0%      1.000 ±  0%       ~ (p=1.000 n=10) ¹
_MarshalString_GoJson-32                                       3.000 ±  0%      3.000 ±  0%       ~ (p=1.000 n=10) ¹
CodeDecoder-32                                                 113.0 ±  1%      114.0 ±  1%       ~ (p=0.271 n=10)
UnicodeDecoder-32                                              2.000 ±  0%      2.000 ±  0%       ~ (p=1.000 n=10) ¹
DecoderStream-32                                               1.000 ±  0%      1.000 ±  0%       ~ (p=1.000 n=10) ¹
CodeUnmarshal-32                                              13.51k ±  0%     13.51k ±  0%       ~ (p=1.000 n=10) ¹
CodeUnmarshalReuse-32                                          64.00 ±  2%      64.50 ±  2%       ~ (p=0.196 n=10)
UnmarshalString-32                                             1.000 ±  0%      1.000 ±  0%       ~ (p=1.000 n=10) ¹
UnmarshalFloat64-32                                            1.000 ±  0%      1.000 ±  0%       ~ (p=1.000 n=10) ¹
UnmarshalInt64-32                                              1.000 ±  0%      1.000 ±  0%       ~ (p=1.000 n=10) ¹
Issue10335-32                                                  1.000 ±  0%      1.000 ±  0%       ~ (p=1.000 n=10) ¹
Unmapped-32                                                    1.000 ±  0%      1.000 ±  0%       ~ (p=1.000 n=10) ¹
_Compact_GoJson-32                                             2.000 ± 50%      2.000 ± 50%       ~ (p=0.628 n=10)
_Indent_GoJson-32                                              2.000 ±  0%      2.000 ±  0%       ~ (p=1.000 n=10) ¹
_Decode_SmallStruct_Unmarshal_GoJson-32                        2.000 ±  0%      2.000 ±  0%       ~ (p=1.000 n=10) ¹
_Decode_SmallStruct_Unmarshal_GoJsonNoEscape-32                1.000 ±  0%      1.000 ±  0%       ~ (p=1.000 n=10) ¹
_Decode_SmallStruct_Stream_GoJson-32                           4.000 ±  0%      4.000 ±  0%       ~ (p=1.000 n=10) ¹
_Decode_MediumStruct_Unmarshal_GoJson-32                       8.000 ±  0%      8.000 ±  0%       ~ (p=1.000 n=10) ¹
_Decode_MediumStruct_Unmarshal_GoJsonNoEscape-32               7.000 ±  0%      7.000 ±  0%       ~ (p=1.000 n=10) ¹
_Decode_MediumStruct_Stream_GoJson-32                          12.00 ±  0%      12.00 ±  0%       ~ (p=1.000 n=10) ¹
_Decode_LargeStruct_Unmarshal_GoJson-32                        67.00 ±  0%      67.00 ±  0%       ~ (p=1.000 n=10) ¹
_Decode_LargeStruct_Unmarshal_GoJsonNoEscape-32                66.00 ±  0%      66.00 ±  0%       ~ (p=1.000 n=10) ¹
_Decode_LargeStruct_Unmarshal_GoJsonFirstWinMode-32            67.00 ±  0%      67.00 ±  0%       ~ (p=1.000 n=10) ¹
_Decode_LargeStruct_Unmarshal_GoJsonNoEscapeFirstWinMode-32    66.00 ±  0%      66.00 ±  0%       ~ (p=1.000 n=10) ¹
_Decode_LargeStruct_Stream_GoJson-32                           74.00 ±  0%      74.00 ±  0%       ~ (p=1.000 n=10) ¹
_Decode_LargeStruct_Stream_GoJsonFirstWinMode-32               74.00 ±  0%      74.00 ±  0%       ~ (p=1.000 n=10) ¹
_Decode_LargeSlice_EscapedString_GoJson-32                    10.00k ±  0%     10.00k ±  0%       ~ (p=1.000 n=10) ¹
_Encode_SmallStruct_GoJsonColored-32                           2.000 ±  0%      2.000 ±  0%       ~ (p=1.000 n=10) ¹
_Encode_SmallStruct_GoJson-32                                  2.000 ±  0%      2.000 ±  0%       ~ (p=1.000 n=10) ¹
_Encode_SmallStruct_GoJsonNoEscape-32                          1.000 ±  0%      1.000 ±  0%       ~ (p=1.000 n=10) ¹
_Encode_SmallStructCached_GoJsonColored-32                     1.000 ±  0%      1.000 ±  0%       ~ (p=1.000 n=10) ¹
_Encode_SmallStructCached_GoJson-32                            1.000 ±  0%      1.000 ±  0%       ~ (p=1.000 n=10) ¹
_Encode_SmallStructCached_GoJsonNoEscape-32                    1.000 ±  0%      1.000 ±  0%       ~ (p=1.000 n=10) ¹
_Encode_MediumStruct_GoJsonColored-32                          15.00 ±  0%      15.00 ±  0%       ~ (p=1.000 n=10) ¹
_Encode_MediumStruct_GoJson-32                                 15.00 ±  0%      15.00 ±  0%       ~ (p=1.000 n=10) ¹
_Encode_MediumStruct_GoJsonNoEscape-32                         1.000 ±  0%      1.000 ±  0%       ~ (p=1.000 n=10) ¹
_Encode_MediumStructCached_GoJsonColored-32                    1.000 ±  0%      1.000 ±  0%       ~ (p=1.000 n=10) ¹
_Encode_MediumStructCached_GoJson-32                           1.000 ±  0%      1.000 ±  0%       ~ (p=1.000 n=10) ¹
_Encode_MediumStructCached_GoJsonNoEscape-32                   1.000 ±  0%      1.000 ±  0%       ~ (p=1.000 n=10) ¹
_Encode_LargeStruct_GoJsonColored-32                           319.0 ±  0%      319.0 ±  0%       ~ (p=1.000 n=10) ¹
_Encode_LargeStruct_GoJson-32                                  319.0 ±  0%      319.0 ±  0%       ~ (p=1.000 n=10) ¹
_Encode_LargeStruct_GoJsonNoEscape-32                          319.0 ±  0%      319.0 ±  0%       ~ (p=1.000 n=10) ¹
_Encode_LargeStructCached_GoJsonColored-32                     1.000 ±  0%      1.000 ±  0%       ~ (p=1.000 n=10) ¹
_Encode_LargeStructCached_GoJson-32                            1.000 ±  0%      1.000 ±  0%       ~ (p=1.000 n=10) ¹
_Encode_LargeStructCached_GoJsonNoEscape-32                    1.000 ±  0%      1.000 ±  0%       ~ (p=1.000 n=10) ¹
_Encode_MapInterface_GoJson-32                                 1.000 ±  0%      1.000 ±  0%       ~ (p=1.000 n=10) ¹
_Encode_Interface_GoJson-32                                    2.000 ±  0%      2.000 ±  0%       ~ (p=1.000 n=10) ¹
_Encode_Bool_GoJson-32                                         0.000 ±  0%      0.000 ±  0%       ~ (p=1.000 n=10) ¹
_Marshal_Bool_GoJson-32                                        1.000 ±  0%      1.000 ±  0%       ~ (p=1.000 n=10) ¹
_Encode_Int_GoJson-32                                          1.000 ±  0%      1.000 ±  0%       ~ (p=1.000 n=10) ¹
_Encode_MarshalJSON_GoJson-32                                  2.000 ±  0%      2.000 ±  0%       ~ (p=1.000 n=10) ¹
_Decode_SmallStruct_UnmarshalPath_GoJson-32                    16.00 ±  0%      16.00 ±  0%       ~ (p=1.000 n=10) ¹
_Decode_SlowReader_GoJson/chunksize_16384-32                   91.00 ±  0%      91.00 ±  0%       ~ (p=1.000 n=10) ¹
_Decode_SlowReader_GoJson/chunksize_4096-32                    95.00 ±  0%      95.00 ±  0%       ~ (p=1.000 n=10) ¹
_Decode_SlowReader_GoJson/chunksize_1024-32                    113.0 ±  1%      114.0 ±  1%       ~ (p=0.656 n=10)
_Decode_SlowReader_GoJson/chunksize_256-32                     196.0 ±  1%      195.0 ±  1%       ~ (p=0.370 n=10)
_Decode_SlowReader_GoJson/chunksize_64-32                      525.0 ±  0%      525.0 ±  0%       ~ (p=1.000 n=10) ¹
geomean                                                                    ²                 +0.03%                ²
¹ all samples are equal
² summaries must be >0 to compute geomean

output:

This will prevent go-json from consuming heap unless it is used.
zeroshade pushed a commit to apache/arrow-go that referenced this pull request Dec 4, 2024
### Rationale for this change

Grafana and Grafana plugins both use arrow-go, but do not use arrow's
`internal/json`. A decent amount of used heap is initialized by
https://github.com/goccy/go-json, so it would be useful to be able to
prevent the initialization.

The example below shows that 60% of this heap profile in a Grafana
plugin is used by `go-json`'s encoder & decoder packages.


![image](https://github.com/user-attachments/assets/abd377c6-4510-4652-8801-88785f6c121d)

I have submitted a [PR to
go-json](goccy/go-json#490) to switch to lazy
initialization (additional details in the PR), but I am having a hard
time getting a response from the maintainer, so am attempting a
different approach to the problem with this PR.

### What changes are included in this PR?

Adds `arrow_json_stdlib` build tag so that it's possible to switch to
`encoding/json` and avoid the overhead of
https://github.com/goccy/go-json.

### Are these changes tested?

I tested this locally, but I can add coverage if this seems like an
acceptable approach.

### Are there any user-facing changes?
@ptodev
Copy link

ptodev commented Dec 10, 2024

Hi! I came across this issue in Grafana Alloy and in the OpenTelemetry Collector. It causes lots of unnecessary memory allocations for users who don't use functionality related to goccy/go-json but run hundreds of instances of a collector.

Thank you @toddtreece for opening the PR and @goccy for reviewing it, fingers crossed that it can be merged soon🤞

Copy link
Owner

@goccy goccy left a comment

Choose a reason for hiding this comment

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

I've reviewed everything, and the only reasonable optimizations were for initEncoder and initDecoder. I’m sorry, but the other changes involve processes that are called frequently or provide minimal optimization benefits, so they cannot be accepted.

@ptodev
Copy link

ptodev commented Dec 10, 2024

Hi @goccy, thank you for reviewing. Do you know how much memory would be allocated if init() still does the optimisations you mentioned are essential? If it's just a few KB, then that might be ok.

I also wonder if an environment variable could be a way to toggle the lazy cache on and off? It could be on by default, but applications which are affected by the memory usage could make optimisations lazy by hardcoding an environment variable.

@goccy
Copy link
Owner

goccy commented Dec 10, 2024

The memory-intensive processes can be improved with initDecoder and initEncoder, so I believe the objective can be achieved.

@toddtreece toddtreece requested a review from goccy December 10, 2024 21:19
@toddtreece
Copy link
Contributor Author

@goccy i reverted the additional changes in a119ee5

Copy link
Owner

@goccy goccy left a comment

Choose a reason for hiding this comment

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

LGTM 👍

@goccy goccy merged commit 279389a into goccy:master Dec 11, 2024
13 checks passed
@toddtreece
Copy link
Contributor Author

@goccy maintainers for open-telemetry/opentelemetry-collector-contrib (related comment) and apache/arrow-go (related comment) have indicated that this will need to wait for a release of go-json before the dependency can be updated in their projects. could you possibly create a new release with this change included when you get a chance? thanks for the help!

TylerHelmuth pushed a commit to open-telemetry/opentelemetry-collector-contrib that referenced this pull request Dec 12, 2024
<!--Ex. Fixing a bug - Describe the bug and how this fixes the issue.
Ex. Adding a feature - Explain what this achieves.-->
#### Description

Updates `github.com/goccy/go-json` with fix from
goccy/go-json#490

Additional details via
#36765:

> The github.com/goccy/go-json module contains an init() function which
warms up a cache even if the module is never used. I believe this causes
around 20 MB of memory per Collector instance. This is an issue for
users who run many instances of the Collector. If you run hundreds of
instances, 20 MB per instance adds up to a lot.
> 
> Currently, github.com/goccy/go-json seems to be used only by the
Splunk HEC Exporter, Stanza, and OTTL. I suppose all other functionality
doesn't need the cache.
> 
> There is a goccy/go-json#490 opened upstream
to improve the cache so that it is loaded lazily - only if goccy/go-json
is used.

<!-- Issue number (e.g. #1234) or full URL to issue, if applicable. -->
#### Link to tracking issue
Fixes
#36765
zeroshade pushed a commit to apache/arrow-go that referenced this pull request Dec 12, 2024
### Rationale for this change

A follow up to
#199 (comment)

> i will continue trying to get the go-json PR merged, and if i do, i'll
open another PR to update the dependency here.

### What changes are included in this PR?

Bumps go-json dependency to include changes from
goccy/go-json#490

### Are these changes tested?

I am assuming CI tests should cover this.

### Are there any user-facing changes?

No
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.

5 participants