Skip to content

feat: replace Key internals with DefaultHasher for allocation-free comparison#4006

Merged
Madoshakalaka merged 1 commit into
masterfrom
key-defaulthasher
Mar 8, 2026
Merged

feat: replace Key internals with DefaultHasher for allocation-free comparison#4006
Madoshakalaka merged 1 commit into
masterfrom
key-defaulthasher

Conversation

@Madoshakalaka

@Madoshakalaka Madoshakalaka commented Feb 26, 2026

Copy link
Copy Markdown
Member

Description

Replaces Key's internal Rc with a NonZeroU64 hash computed via DefaultHasher, as discussed in #3205 and the earlier PR #2616.

  • Key comparison is now a single u64 == instead of string comparison
  • Numeric key creation avoids heap allocation in release builds
  • Option is niche-optimized to the same size as Key (8 bytes)
  • Debug builds retain the original string for diagnostics

closes #3205

Checklist

  • I have reviewed my own code
  • I have added tests

@github-actions

github-actions Bot commented Feb 26, 2026

Copy link
Copy Markdown

Visit the preview URL for this PR (updated for commit 61913af):

https://yew-rs-api--pr4006-key-defaulthasher-qyp24pfl.web.app

(expires Thu, 05 Mar 2026 08:37:08 GMT)

🔥 via Firebase Hosting GitHub Action 🌎

@github-actions

github-actions Bot commented Feb 26, 2026

Copy link
Copy Markdown

Benchmark - core

Yew Master

vnode           fastest       │ slowest       │ median        │ mean          │ samples │ iters
╰─ vnode_clone  2.796 ns      │ 3.339 ns      │ 2.8 ns        │ 2.809 ns      │ 100     │ 1000000000

Pull Request

vnode           fastest       │ slowest       │ median        │ mean          │ samples │ iters
╰─ vnode_clone  2.775 ns      │ 4.171 ns      │ 2.781 ns      │ 2.801 ns      │ 100     │ 1000000000

@github-actions

github-actions Bot commented Feb 26, 2026

Copy link
Copy Markdown

Benchmark - SSR

Yew Master

Details
Benchmark Round Min (ms) Max (ms) Mean (ms) Standard Deviation
Baseline 10 291.034 292.337 291.510 0.424
Hello World 10 493.406 497.952 495.679 1.547
Function Router 10 1656.386 1682.497 1667.861 8.404
Concurrent Task 10 1005.696 1007.875 1006.979 0.682
Many Providers 10 1091.915 1127.945 1105.751 12.546

Pull Request

Details
Benchmark Round Min (ms) Max (ms) Mean (ms) Standard Deviation
Baseline 10 310.748 311.931 311.148 0.332
Hello World 10 478.650 489.151 481.598 4.134
Function Router 10 1620.751 1652.191 1634.345 10.628
Concurrent Task 10 1005.796 1007.811 1006.942 0.684
Many Providers 10 1119.223 1157.298 1136.642 12.296

@github-actions

github-actions Bot commented Feb 26, 2026

Copy link
Copy Markdown

Size Comparison

Details
examples master (KB) pull request (KB) diff (KB) diff (%)
async_clock 96.823 96.289 -0.534 -0.552%
boids 165.482 164.997 -0.485 -0.293%
communication_child_to_parent 89.899 89.470 -0.430 -0.478%
communication_grandchild_with_grandparent 100.772 100.378 -0.395 -0.392%
communication_grandparent_to_grandchild 97.273 96.843 -0.431 -0.443%
communication_parent_to_child 87.314 86.878 -0.437 -0.500%
contexts 103.035 102.655 -0.380 -0.369%
counter 84.037 83.553 -0.484 -0.576%
counter_functional 85.410 84.940 -0.470 -0.550%
dyn_create_destroy_apps 87.122 86.666 -0.456 -0.523%
file_upload 96.566 96.013 -0.554 -0.573%
function_delayed_input 90.901 90.434 -0.468 -0.515%
function_memory_game 168.782 168.404 -0.378 -0.224%
function_router 326.337 325.397 -0.939 -0.288%
function_todomvc 160.736 160.485 -0.251 -0.156%
futures 232.128 231.617 -0.511 -0.220%
game_of_life 102.119 101.779 -0.340 -0.333%
immutable 244.170 242.980 -1.189 -0.487%
inner_html 78.616 78.141 -0.476 -0.605%
js_callback 107.492 107.026 -0.466 -0.433%
keyed_list 177.171 176.745 -0.426 -0.240%
mount_point 81.754 81.294 -0.460 -0.563%
nested_list 110.428 109.807 -0.621 -0.562%
node_refs 89.300 88.782 -0.518 -0.580%
password_strength 1725.986 1725.342 -0.645 -0.037%
portals 90.777 90.336 -0.441 -0.486%
router 296.723 296.444 -0.278 -0.094%
suspense 110.946 110.582 -0.364 -0.328%
timer 86.637 86.141 -0.496 -0.573%
timer_functional 96.147 94.212 -1.936 -2.013%
todomvc 139.338 139.111 -0.227 -0.163%
two_apps 83.903 83.471 -0.433 -0.516%
web_worker_fib 133.659 133.119 -0.540 -0.404%
web_worker_prime 184.450 183.801 -0.649 -0.352%
webgl 81.250 80.749 -0.501 -0.617%

⚠️ The following example has changed its size significantly:

examples master (KB) pull request (KB) diff (KB) diff (%)
timer_functional 96.147 94.212 -1.936 -2.013%

@Madoshakalaka

Copy link
Copy Markdown
Member Author

Looks like a pure win, every example shrunk, no performance regression, cleaner semantics. Recall that the original PR #2616 was rejected partly due to binary size increases of 1-8 KB. We all good here.

@Madoshakalaka Madoshakalaka marked this pull request as ready for review February 26, 2026 08:32
github-actions[bot]
github-actions Bot previously approved these changes Feb 26, 2026
…mparison (#3205)

BREAKING CHANGE: Key no longer implements Deref<Target=str>.
BREAKING CHANGE: Keys from different types with the same string representation
are no longer equal (e.g. Key::from("0") != Key::from(0u64)).
BREAKING CHANGE: Display in release mode shows #<hash> instead of the original value.
@Madoshakalaka Madoshakalaka merged commit b165a73 into master Mar 8, 2026
25 checks passed
@Madoshakalaka Madoshakalaka deleted the key-defaulthasher branch March 8, 2026 06:10
@WorldSEnder

Copy link
Copy Markdown
Member

Is there any protection against runtime collisions (in release mode)? This seems exactly as if saying to each user to find a way to map uniquely to an u64 and saying that hash collisions are a user error. I find this less convincing than I did 3 years ago. There is a reason a hashmap does a key comparison when it finds a matching hash and doesn't just assume.

There must be other ways to avoid the conversion from a String allocation to an Rc allocation and judging only on metrics seems misguided: this removes functionality. We might as well allow only u64 as key input now.

@Madoshakalaka Madoshakalaka restored the key-defaulthasher branch March 8, 2026 15:46
@Madoshakalaka

Copy link
Copy Markdown
Member Author

Is there any protection against runtime collisions (in release mode)?

No. I just trusted the consensus in the original discussion and regarded the low collision rate as acceptible.

I'll fix this in a later PR then.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make Key avoid an allocation, make it faster to compare than Strings

2 participants