Skip to content

Optimize decoding CBOR text string by up to 16% - #812

Merged
fxamacker merged 3 commits into
masterfrom
fxamacker/optimize-decoding-text-string
Aug 28, 2026
Merged

Optimize decoding CBOR text string by up to 16%#812
fxamacker merged 3 commits into
masterfrom
fxamacker/optimize-decoding-text-string

Conversation

@fxamacker

Copy link
Copy Markdown
Owner

This PR reduces decoding duration for CBOR text string by up to 16% (depending on the data structure and values being decoded) on existing benchmarks.

This PR optimizes decoding of CBOR text string by:

  • using fast path for parsing CBOR text strings up to 23 bytes, which covers most map string keys and struct field names
  • using fast path to set Go strings
  • using fast path for map string keys to bypass the generic decoding path (e.g. map[string]any)

Benchmarks

Benchmark results for Unmarshal() omit allocs/op and B/op because they are unchanged:

goos: linux
goarch: amd64
pkg: github.com/fxamacker/cbor/v2

NOTE: These benchmark results were produced on a PC running other processes.
Please measure on your own hardware with appropriate settings and workloads.

                                                            │     old     │                 new                 │
                                                            │   sec/op    │   sec/op     vs base                │
Unmarshal/CBOR_map_to_Go_interface_{}                        1.303µ ± 0%   1.231µ ± 0%   -5.56% (p=0.000 n=10)
Unmarshal/CBOR_map_to_Go_map[string]interface_{}             1.433µ ± 0%   1.275µ ± 0%  -11.06% (p=0.000 n=10)
Unmarshal/CBOR_map_to_Go_map[string]string                   993.6n ± 0%   832.5n ± 0%  -16.21% (p=0.000 n=10)
Unmarshal/CBOR_indef-len_map_to_Go_interface_{}              1.410µ ± 0%   1.319µ ± 0%   -6.46% (p=0.000 n=10)
Unmarshal/CBOR_indef-len_map_to_Go_map[string]interface_{}   1.770µ ± 0%   1.561µ ± 0%  -11.78% (p=0.000 n=10)
Unmarshal/CBOR_indef-len_map_to_Go_map[string]string         1.288µ ± 0%   1.099µ ± 0%  -14.67% (p=0.000 n=10)
Unmarshal/CBOR_map_to_Go_map[string]any                      2.688µ ± 0%   2.539µ ± 0%   -5.53% (p=0.000 n=10)
Unmarshal/CBOR_map_to_Go_struct                              1.916µ ± 0%   1.701µ ± 0%  -11.22% (p=0.000 n=10)
Unmarshal/CBOR_map_to_Go_map[int]any                         2.592µ ± 0%   2.553µ ± 0%   -1.49% (p=0.000 n=10)
Unmarshal/CBOR_map_to_Go_struct_keyasint                     1.825µ ± 0%   1.637µ ± 0%  -10.25% (p=0.000 n=10)
Unmarshal/CBOR_array_to_Go_[]any                             2.156µ ± 0%   2.097µ ± 0%   -2.76% (p=0.000 n=10)
Unmarshal/CBOR_array_to_Go_struct_toarray                    1.730µ ± 0%   1.558µ ± 0%   -9.89% (p=0.000 n=10)
UnmarshalCWTClaims                                           301.2n ± 0%   292.2n ± 0%   -3.02% (p=0.000 n=10)
UnmarshalSenML                                               1.280µ ± 0%   1.169µ ± 0%   -8.67% (p=0.000 n=10)
UnmarshalWebAuthn                                            376.2n ± 0%   341.2n ± 1%   -9.30% (p=0.000 n=10)
MapToStruct/default_options/all_known_fields                 385.4n ± 4%   328.6n ± 1%  -14.73% (p=0.000 n=10)
MapToStruct/default_options/all_known_dup_fields             297.8n ± 0%   258.2n ± 0%  -13.28% (p=0.000 n=10)
MapToStruct/default_options/all_unknown_fields               927.2n ± 6%   864.2n ± 5%   -6.79% (p=0.019 n=10)
MapToStruct/default_options/all_unknown_dup_fields           866.8n ± 2%   819.0n ± 2%   -5.52% (p=0.000 n=10)
MapToStruct/default_options/many_fields_one_key_per_field    2.804µ ± 3%   2.427µ ± 3%  -13.48% (p=0.000 n=10)
MapToStruct/reject_unknown/all_known_fields                  385.9n ± 4%   329.0n ± 2%  -14.75% (p=0.000 n=10)
MapToStruct/reject_unknown/all_known_dup_fields              297.2n ± 0%   258.2n ± 0%  -13.12% (p=0.000 n=10)
MapToStruct/reject_unknown/all_unknown_fields                207.7n ± 0%   207.3n ± 1%        ~ (p=0.528 n=10)
MapToStruct/reject_unknown/all_unknown_dup_fields            206.4n ± 1%   205.4n ± 1%        ~ (p=0.255 n=10)
MapToStruct/reject_dup/all_known_fields                      385.7n ± 4%   331.7n ± 2%  -14.00% (p=0.000 n=10)
MapToStruct/reject_dup/all_known_dup_fields                  210.8n ± 0%   207.5n ± 1%   -1.54% (p=0.000 n=10)
MapToStruct/reject_dup/all_unknown_fields                    1.536µ ± 3%   1.486µ ± 3%   -3.22% (p=0.009 n=10)
MapToStruct/reject_dup/all_unknown_dup_fields                386.6n ± 0%   379.6n ± 0%   -1.81% (p=0.000 n=10)
MapToStruct/reject_unknown_and_dup/all_known_fields          385.6n ± 4%   328.4n ± 1%  -14.82% (p=0.000 n=10)
MapToStruct/reject_unknown_and_dup/all_known_dup_fields      212.6n ± 0%   208.8n ± 1%   -1.76% (p=0.000 n=10)
MapToStruct/reject_unknown_and_dup/all_unknown_fields        206.9n ± 1%   206.4n ± 1%        ~ (p=0.148 n=10)
MapToStruct/reject_unknown_and_dup/all_unknown_dup_fields    205.8n ± 1%   205.3n ± 1%   -0.22% (p=0.041 n=10)

The next four benchmarks decode text strings that cannot take 
the new fast path for parsing, so they isolate its cost.

                                                            │     old     │                 new                 │
                                                            │   sec/op    │   sec/op     vs base                │
Unmarshal/CBOR_text_string_to_Go_interface_{}                104.1n ± 0%   105.6n ± 0%   +1.39% (p=0.000 n=10)
Unmarshal/CBOR_text_string_to_Go_string                      74.89n ± 1%   74.62n ± 1%        ~ (p=0.165 n=10)
Unmarshal/CBOR_indef-len_text_string_to_Go_interface_{}      509.7n ± 0%   505.7n ± 0%   -0.78% (p=0.000 n=10)
Unmarshal/CBOR_indef-len_text_string_to_Go_string            474.2n ± 0%   469.6n ± 0%   -0.95% (p=0.000 n=10)

To reduce scrolling, benchmark names were shortened:

  • "indefinite-length" to "indef-len"
  • "duplicate" to "dup"
  • "UnmarshalMapToStruct" to "MapToStruct"

@fxamacker fxamacker self-assigned this Aug 27, 2026

@x448 x448 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🚀

@fxamacker
fxamacker merged commit ef88c17 into master Aug 28, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants