Skip to content

perf: Optimize UUID hex parsing and formatting#894

Merged
KodrAus merged 1 commit into
uuid-rs:mainfrom
geeknoid:main
Jul 11, 2026
Merged

perf: Optimize UUID hex parsing and formatting#894
KodrAus merged 1 commit into
uuid-rs:mainfrom
geeknoid:main

Conversation

@geeknoid

@geeknoid geeknoid commented Jul 10, 2026

Copy link
Copy Markdown

Replace the byte-at-a-time lookup-table hex encode/decode in the parser and formatter with branchless arithmetic. Both stay const-evaluable and free of unsafe, and the loops now auto-vectorize (e.g. SSE2 on x86_64, NEON on aarch64), roughly halving instruction counts on the hot paths.

Benchmarks below compare before/after using the gungraun (Callgrind instruction counts) and libtest (wall-clock) harnesses added here.

  Benchmark                  Instructions       Time (ns/iter)
                             before  after      before  after
  ------------------------  -------  -----      ------  -----
  parse_random                  261    112          40     14
  parse_nil                     261    113          40     14
  parse_random_hyphenated       271    134          37     29
  parse_nil_hyphenated          271    134          37     29
  parse_urn                     290    152          38     30
  encode_simple                 131     61        12.4    0.7
  encode_hyphen                 139     81         6.6    1.8
  encode_urn                    152    127         7.1    2.0
  Display (simple)              296    236        25.0   16.5
  Display (hyphenated)          304    252          26     22
  Display (urn)                 303    254          29     27

@KodrAus KodrAus 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 for working on this @geeknoid! We previously had looked at manually vectorized routines but didn't keep them here just because I didn't want to complicate the project or our CI too much to maintain them or try port them across ISAs. If this autovectorizes nicely on x86 and ARM then that's a big win.

Comment thread Cargo.toml Outdated
[dev-dependencies.rustversion]
version = "1"

[target.'cfg(not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none"))))'.dev-dependencies.gungraun]

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.

I wasn't familiar with gungraun, it looks interesting 👀

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Yes, it's a game changer. You get deterministic benchmark results instantaneously.

@KodrAus

KodrAus commented Jul 11, 2026

Copy link
Copy Markdown
Member

Looks like we'll need to cfg gungraun out on WASM:

error[E0277]: the trait bound `OsString: serde::Serialize` is not satisfied
    --> /home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/gungraun-runner-0.19.2/src/api.rs:1110:44
     |
1110 |   #[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
     |                                              ^^^^^^^^^ the trait `Serialize` is not implemented for `OsString`
...
1118 | /     /// The environment variables to set or pass through to the binary
1119 | |     pub envs: Vec<(OsString, Option<OsString>)>,
     | |_______________________________________________- required by a bound introduced by this call

@geeknoid

Copy link
Copy Markdown
Author

@KodrAus The WASM build issue should be fixed. I don't know how to trigger your CI though...

Replace the byte-at-a-time lookup-table hex encode/decode in the parser
and formatter with branchless arithmetic. Both stay const-evaluable and
free of unsafe, and the loops now auto-vectorize (e.g. SSE2 on x86_64,
NEON on aarch64), roughly halving instruction counts on the hot paths.

Benchmarks below compare before/after using the gungraun (Callgrind
instruction counts) and libtest (wall-clock) harnesses added here.

  Benchmark                  Instructions       Time (ns/iter)
                             before  after      before  after
  ------------------------  -------  -----      ------  -----
  parse_random                  261    112          40     14
  parse_nil                     261    113          40     14
  parse_random_hyphenated       271    134          37     29
  parse_nil_hyphenated          271    134          37     29
  parse_urn                     290    152          38     30
  encode_simple                 131     61        12.4    0.7
  encode_hyphen                 139     81         6.6    1.8
  encode_urn                    152    127         7.1    2.0
  Display (simple)              296    236        25.0   16.5
  Display (hyphenated)          304    252          26     22
  Display (urn)                 303    254          29     27
@KodrAus
KodrAus merged commit 9b4bfc8 into uuid-rs:main Jul 11, 2026
23 checks passed
kodiakhq Bot pushed a commit to pdylanross/fatigue that referenced this pull request Jul 13, 2026
Bumps uuid from 1.23.4 to 1.23.5.

Release notes
Sourced from uuid's releases.

v1.23.5
What's Changed

doc: Fix broken link by @​frostyplanet in uuid-rs/uuid#891
perf: Optimize UUID hex parsing and formatting by @​geeknoid in uuid-rs/uuid#894
Prepare for 1.23.5 release by @​KodrAus in uuid-rs/uuid#895

New Contributors

@​geeknoid made their first contribution in uuid-rs/uuid#894

Full Changelog: uuid-rs/uuid@v1.23.4...v1.23.5



Commits

5dc6b3d Merge pull request #895 from uuid-rs/cargo/v1.23.5
5a7dfe5 prepare for 1.23.5 release
9b4bfc8 Merge pull request #894 from geeknoid/main
5acc5a5 perf: Optimize UUID hex parsing and formatting
1e5d867 Merge pull request #891 from frostyplanet/doc
49310f0 doc: Fix broken link
See full diff in compare view




Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


Dependabot commands and options

You can trigger Dependabot actions by commenting on this PR:

@dependabot rebase will rebase this PR
@dependabot recreate will recreate this PR, overwriting any edits that have been made to it
@dependabot show <dependency name> ignore conditions will show all of the ignore conditions of the specified dependency
@dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
@dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
@dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
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