Skip to content

⚡ perf: Improve performance of RebuildTree() by 68%#3895

Merged
ReneWerner87 merged 1 commit intomainfrom
refactor-buildtree-for-route-bucket-collection
Nov 24, 2025
Merged

⚡ perf: Improve performance of RebuildTree() by 68%#3895
ReneWerner87 merged 1 commit intomainfrom
refactor-buildtree-for-route-bucket-collection

Conversation

@gaby
Copy link
Member

@gaby gaby commented Nov 24, 2025

Summary

  • This pull request focuses on improving the efficiency and memory management of the routing tree construction within the application. It refactors the buildTree function to simplify bucket allocation and ensure precise preallocation of data structures. Additionally, a new benchmark is added to track the performance of the tree rebuilding operation, providing a baseline for future optimizations.

Before

goos: linux
goarch: amd64
pkg: github.com/gofiber/fiber/v3
cpu: AMD Ryzen 7 7800X3D 8-Core Processor           
Benchmark_App_RebuildTree
Benchmark_App_RebuildTree-4   	   32095	     37059 ns/op	   26768 B/op	     290 allocs/op
Benchmark_App_RebuildTree-4   	   32386	     37005 ns/op	   26768 B/op	     290 allocs/op
Benchmark_App_RebuildTree-4   	   33099	     36389 ns/op	   26768 B/op	     290 allocs/op
Benchmark_App_RebuildTree-4   	   31860	     36865 ns/op	   26768 B/op	     290 allocs/op
Benchmark_App_RebuildTree-4   	   32568	     37237 ns/op	   26768 B/op	     290 allocs/op
Benchmark_App_RebuildTree-4   	   32446	     36411 ns/op	   26768 B/op	     290 allocs/op
Benchmark_App_RebuildTree-4   	   33307	     36342 ns/op	   26768 B/op	     290 allocs/op
Benchmark_App_RebuildTree-4   	   32185	     36923 ns/op	   26768 B/op	     290 allocs/op
Benchmark_App_RebuildTree-4   	   33567	     36420 ns/op	   26768 B/op	     290 allocs/op
Benchmark_App_RebuildTree-4   	   31746	     36938 ns/op	   26768 B/op	     290 allocs/op
PASS
ok  	github.com/gofiber/fiber/v3	11.972s

After

goos: linux
goarch: amd64
pkg: github.com/gofiber/fiber/v3
cpu: AMD Ryzen 7 7800X3D 8-Core Processor           
Benchmark_App_RebuildTree
Benchmark_App_RebuildTree-4   	   88698	     13380 ns/op	   17952 B/op	      93 allocs/op
Benchmark_App_RebuildTree-4   	   87627	     13122 ns/op	   17952 B/op	      93 allocs/op
Benchmark_App_RebuildTree-4   	   89563	     13367 ns/op	   17952 B/op	      93 allocs/op
Benchmark_App_RebuildTree-4   	   90487	     13846 ns/op	   17952 B/op	      93 allocs/op
Benchmark_App_RebuildTree-4   	   83514	     14268 ns/op	   17952 B/op	      93 allocs/op
Benchmark_App_RebuildTree-4   	   81895	     13929 ns/op	   17952 B/op	      93 allocs/op
Benchmark_App_RebuildTree-4   	   84560	     14110 ns/op	   17952 B/op	      93 allocs/op
Benchmark_App_RebuildTree-4   	   81997	     13364 ns/op	   17952 B/op	      93 allocs/op
Benchmark_App_RebuildTree-4   	   87638	     13045 ns/op	   17952 B/op	      93 allocs/op
Benchmark_App_RebuildTree-4   	   91272	     13427 ns/op	   17952 B/op	      93 allocs/op
PASS
ok  	github.com/gofiber/fiber/v3	11.795s

Benchstat

$ go run golang.org/x/perf/cmd/benchstat@latest old.txt new.txt 
goos: linux
goarch: amd64
pkg: github.com/gofiber/fiber/v3
cpu: AMD Ryzen 7 7800X3D 8-Core Processor           
                   │   old.txt   │               new.txt               │
                   │   sec/op    │   sec/op     vs base                │
_App_RebuildTree-4   36.89µ ± 1%   13.40µ ± 5%  -63.67% (p=0.000 n=10)

                   │   old.txt    │               new.txt                │
                   │     B/op     │     B/op      vs base                │
_App_RebuildTree-4   26.14Ki ± 0%   17.53Ki ± 0%  -32.93% (p=0.000 n=10)

                   │   old.txt   │              new.txt               │
                   │  allocs/op  │ allocs/op   vs base                │
_App_RebuildTree-4   290.00 ± 0%   93.00 ± 0%  -67.93% (p=0.000 n=10)

Copilot AI review requested due to automatic review settings November 24, 2025 02:46
@gaby gaby requested a review from a team as a code owner November 24, 2025 02:46
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 24, 2025

Walkthrough

Refactors the buildTree function in router.go from a two-pass algorithm to a single-pass approach per method, introducing per-route treePaths computation and consolidated tsMap initialization. Routes are appended directly to buckets with special handling for global routes. Also adds a new benchmark for tree rebuilding operations.

Changes

Cohort / File(s) Summary
Router tree algorithm optimization
router.go
Reworks buildTree from two-pass to single-pass per method; introduces treePaths array for per-route computation, consolidates prefixCounts aggregation and tsMap initialization, eliminates intermediate deduplication loop, handles global routes (treePath == 0) via direct bucket appends, resets refresh flag post-rebuild.
Benchmark addition
router_test.go
Adds Benchmark_App_RebuildTree function to measure rebuild performance with allocation reporting across multiple RebuildTree iterations.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45–60 minutes

  • buildTree algorithm transition: Verify two-pass to single-pass conversion preserves routing correctness and functional equivalence
  • Global route handling: Confirm treePath == 0 routes are properly appended to all existing buckets
  • Data structure population: Validate tsMap initialization and treeStack[method] assignment in consolidated flow
  • Edge cases: Test boundary conditions with mixed global and prefixed routes
  • Benchmark validity: Ensure benchmark accurately measures rebuild performance without artificial bottlenecks

Possibly related PRs

Suggested labels

🧹 Updates, v3, ⚡️ Performance

Suggested reviewers

  • sixcolors
  • efectn

Poem

🐰 One pass, not two, through routing's way,
Buckets bloom where treePaths play,
Global routes hop every lane,
Faster trees, less refrain,
Efficient hops through fiber's domain! ✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The PR description covers the key changes and testing steps, though it lacks detailed completion of several template sections like benchmarks documentation, changelog entries, and migration guide.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title directly describes the main change: optimizing RebuildTree() performance by 68%, which aligns with the refactored single-pass approach replacing the two-pass algorithm.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor-buildtree-for-route-bucket-collection

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @gaby, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request focuses on improving the efficiency and memory management of the routing tree construction within the application. It refactors the buildTree function to simplify bucket allocation and ensure precise preallocation of data structures. Additionally, a new benchmark is added to track the performance of the tree rebuilding operation, providing a baseline for future optimizations.

Highlights

  • Optimized Tree Bucket Allocation: The logic for allocating routing buckets has been refactored to directly use prefix counts and global routes, eliminating the need for an intermediate capacity map and streamlining the process.
  • Improved Slice Preallocation: The treeStack slices for both global and per-prefix routes are now accurately preallocated with their required capacities during the tree rebuilding process, enhancing memory efficiency.
  • New Performance Benchmark: A new benchmark, Benchmark_App_RebuildTree, has been introduced to measure and monitor the performance of the App.RebuildTree function.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@ReneWerner87 ReneWerner87 added this to v3 Nov 24, 2025
@ReneWerner87 ReneWerner87 added this to the v3 milestone Nov 24, 2025
@gaby gaby changed the title Simplify tree bucket allocation and add RebuildTree benchmark ⚡ perf: Improve performance of RebuildTree() by 68% Nov 24, 2025
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a significant performance improvement to the buildTree function by refactoring the logic for allocating and populating routing buckets. The new implementation is more efficient, using fewer loops and pre-allocating memory for slices, which eliminates the need for a separate deduplication step. The code is now cleaner and easier to understand. The addition of the Benchmark_App_RebuildTree benchmark is also a valuable contribution, allowing for easy measurement of the performance gains from this optimization. Overall, this is an excellent change that improves both performance and maintainability.

@codecov
Copy link

codecov bot commented Nov 24, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.66%. Comparing base (76576dc) to head (ee1466c).
⚠️ Report is 19 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3895      +/-   ##
==========================================
- Coverage   91.71%   91.66%   -0.05%     
==========================================
  Files         115      115              
  Lines        9967     9982      +15     
==========================================
+ Hits         9141     9150       +9     
- Misses        527      532       +5     
- Partials      299      300       +1     
Flag Coverage Δ
unittests 91.66% <100.00%> (-0.05%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR optimizes the tree bucket allocation algorithm in the routing system by eliminating redundant intermediate data structures and improving memory efficiency. The refactoring removes the need for deduplication while preserving optimal preallocation.

Key changes:

  • Replaced two-pass allocation strategy with single-pass approach using computed prefix paths
  • Eliminated uniqueRouteStack call by preventing duplicates during distribution
  • Added Benchmark_App_RebuildTree to measure tree rebuild performance

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
router.go Refactored buildTree() to compute treePaths upfront and preallocate buckets with exact capacities, eliminating intermediate map and deduplication step
router_test.go Added benchmark for RebuildTree() method to measure performance of tree reconstruction

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
router_test.go (1)

1457-1469: Benchmark correctly exercises repeated RebuildTree calls

The benchmark setup looks sound: routes are registered once, alloc reporting is enabled, and each b.Loop() iteration forces a full rebuild by flipping routesRefreshed before calling RebuildTree(). This should give a clean signal on rebuild cost without extra noise.

If you ever want to make the intent clearer, you could wrap the routesRefreshed assignment in a tiny helper (e.g. markRoutesDirtyForTest) rather than poking the field directly, but that’s purely cosmetic here.

router.go (1)

712-754: Single-pass buildTree preserves routing semantics and ordering

The new single-pass buildTree logic looks correct:

  • Per-method, every route ends up in exactly the buckets it needs (global routes in all buckets including 0, prefix-specific routes only in their 3‑byte prefix bucket).
  • For each bucket, routes appear in the same relative order as in app.stack[method] (globals + matching-prefix routes), so handler/middleware ordering is preserved.
  • Bucket 0 intentionally contains only “global” routes, and the next / nextCustom fallback to treeStack[method][0] stays correct because any route that could match a path whose hash doesn’t share its 3‑byte static prefix is treated as global (treePath == 0).

If you want to make this behavior future-proof for maintainers, consider adding a short comment near the tsMap[0] initialization explaining that bucket 0 is reserved for prefix-agnostic routes and is the fallback when no prefix bucket exists.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5caa478 and ee1466c.

📒 Files selected for processing (2)
  • router.go (1 hunks)
  • router_test.go (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Agent
  • GitHub Check: lint
  • GitHub Check: unit (1.25.x, macos-latest)
  • GitHub Check: unit (1.25.x, windows-latest)
  • GitHub Check: Compare
  • GitHub Check: repeated

@ReneWerner87
Copy link
Member

@gaby the range benchmark checks are failing because of the data type

image

@ReneWerner87
Copy link
Member

I need to debug the local version. Unfortunately, we don't have any tests that check the internal structure, so we can't be sure that nothing has been applied incorrectly here.

@ReneWerner87
Copy link
Member

image

its almost the same
image

perfect
image

just the "0" is a different dataType
is this "0" even needed ?

@ReneWerner87
Copy link
Member

@ksw2000 Is the entry for 0 still necessary? I don't think it's used anymore, but I'm not sure.

I think I added that back then for routes that have less than 3 characters or the root "/" route.

@ReneWerner87 ReneWerner87 merged commit a477afd into main Nov 24, 2025
27 of 28 checks passed
@ReneWerner87 ReneWerner87 deleted the refactor-buildtree-for-route-bucket-collection branch November 24, 2025 12:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants