8000 perf: make hot index helpers static inline for faster cellToChildren and gridDisk by justinhwang · Pull Request #1301 · uber/h3 · GitHub
[go: up one dir, main page]

Skip to content

perf: make hot index helpers static inline for faster cellToChildren and gridDisk - #1301

Open
justinhwang wants to merge 1 commit into
masterfrom
inline-hot-helpers
Open

justinhwang wants to merge 1 commit into
masterfrom
inline-hot-helpers

Conversation

@justinhwang
@justinhwang justinhwang commented Nov 10, 2026 •
Copy link
Copy Markdown
Collaborator

Summary

Several hot paths call tiny helpers that are defined in a different .c file, so the compiler cannot inline them without LTO:

  • cellToChildren (h3Index.c) calls iterStepChild (iterators.c) once per child.
  • h3NeighborRotations (algos.c), which dominates gridDisk, calls _h3LeadingNonZeroDigit and isResolutionClassIII (h3Index.c) and _isBaseCellPentagon, _isBaseCellPolarPentagon and _baseCellIsCwOffset (baseCells.c).
  • gridDisk, vertex and directed edge code calls the exported isPentagon.

This moves those helpers into their headers as static inline, the same approach as the header-only coordijk.h (#1154). isPentagon keeps its exported definition as a one-line wrapper around a new internal _h3IsPentagon, which library code now calls directly. Function bodies are moved unchanged; there is no behavior or public API change.

iterStepChild was DECLSPEC but is not part of h3api.h. It is no longer an exported symbol of the shared library; in-repo callers (utility.c, tests) get the inline version from iterators.h.

Benchmark

Apple M3 Max, CMAKE_BUILD_TYPE=Release, master vs. this branch, median of 10 alternating runs. Apple clang 21 and Homebrew GCC 16.2, static and shared builds.

Benchmark Clang static, µs Clang static Clang shared GCC static GCC shared
cellToChildren3 1.191 → 0.614 -48% -49% -46% -46%
cellToChildren5 59.49 → 29.60 -50% -51% -47% -47%
gridDisk20 15.20 → 10.97 -28% -29% -33% -33%
gridDisk40 56.88 → 39.75 -30% -30% -27% -30%
gridDiskPentagon20 679.8 → 665.3 -2% -27% -17% -17%
reverseDirectedEdge 0.222 → 0.149 -33% -34% -21% -22%
cellToVertexes 0.731 → 0.618 -15% -18% -8% -9%
cellToVertexesRing 12.56 → 10.62 -15% -20% -11% -12%
gridPathCellsFar 231.5 → 208.1 -10% 0% -13% -14%
polygonToCellsSF 455.5 → 417.4 -8% -10% -4% -4%
latLngToCell 0.201 → 0.200 -1% -1% 0% +1%
cellToBoundary 0.427 → 0.426 0% -2% -2% -3%
Geomean, 35 ben 8000 chmarks -16.7% -19.4% -16.7% -17.9%

Notes:

  • Benchmarks whose code is untouched move by a few percent between builds from code placement alone, so treat anything within about ±5% as flat.
  • gridDiskPentagon* improved less on Clang static (-2% to -6%) than in the other three configurations (-12% to -27%). I have not tracked down why.

Relation to LTO (#325, #326)

I measured #326 (LTO, applied to current master) on the same machine. On shared Release builds LTO alone gives about the same result as this change (-18% to -20% geomean), from the same call sites. With this change in place, adding LTO on top gave no further improvement in my runs (+1% on Clang, +3% on GCC, geomean). Unlike LTO, this also applies to static builds and needs no build-system change.

Closes #325

Test plan

  • make test, 326/326 passing, on macOS arm64:
    • Debug with -DWARNINGS_AS_ERRORS=ON (Apple clang 21)
    • Release, Apple clang 21 and GCC 16.2, static and shared
  • Coverage build (-DENABLE_COVERAGE=ON -DH3_PREFIX=testprefix_): all lines of the moved code in baseCells.h, h3Index.h and iterators.h are covered.
  • make format with clang-format-14 produces no further changes.
  • Not run locally: Linux and Windows (including the Windows shared-library build). Relying on CI for those.

🤖 Generated with Claude Code

…line them

cellToChildren, gridDisk and the vertex and edge functions spend much of
their time calling tiny helpers defined in a different .c file, which the
compiler cannot inline without LTO:

- cellToChildren (h3Index.c) calls iterStepChild (iterators.c) once per
  child.
- h3NeighborRotations (algos.c) calls _h3LeadingNonZeroDigit and
  isResolutionClassIII (h3Index.c) and _isBaseCellPentagon,
  _isBaseCellPolarPentagon and _baseCellIsCwOffset (baseCells.c).
- gridDisk, vertex and directed edge code calls the exported isPentagon.

Move those helpers into their headers as static inline, following the
approach of the header-only coordijk.h. isPentagon keeps its exported
definition as a wrapper around a new internal _h3IsPentagon, which library
code now calls directly.

No behavior change. In Release builds cellToChildren is about 2x faster and
gridDisk about 30% faster, in static and shared builds alike.

Co-Authored-By: Claude Code <noreply@anthropic.com>
@coderabbitai
coderabbitai Bot commented Nov 10, 2026 •
Copy link
Copy Markdown

Explain Change →

No actionable comments were generated in the recent review.

Walkthrough

Several helper functions now have inline definitions in headers. Child-iterator advancement also moves into a header implementation. Library call sites replace exported pentagon checks with the internal _h3IsPentagon helper.

Changes

Inline helper migration

Layer / File(s) Summary
Inline base-cell and index helpers
src/h3lib/include/baseCells.h, src/h3lib/include/h3Index.h, src/h3lib/lib/baseCells.c, src/h3lib/lib/h3Index.c
Base-cell checks, isResolutionClassIII, _h3LeadingNonZeroDigit, and _h3IsPentagon now have inline header definitions. Their former implementations were removed, and isPentagon delegates to _h3IsPentagon.
Inline child iterator
src/h3lib/include/iterators.h, src/h3lib/lib/iterators.c
Digit helpers, exhausted-state creation, and iterStepChild now have inline header implementations. The corresponding implementations were removed from iterators.c.
Internal pentagon checks
src/h3lib/lib/algos.c, src/h3lib/lib/directedEdge.c, src/h3lib/lib/h3Index.c, src/h3lib/lib/iterGosper.c, src/h3lib/lib/iterators.c, src/h3lib/lib/polyfill.c, src/h3lib/lib/vertex.c
Pentagon checks in these library paths now call _h3IsPentagon instead of the exported isPentagon function.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Refactor

Possibly related PRs

  • uber/h3#1272: Adds edgeToVertexes, which uses vertex-conversion logic also touched by this change.
Merge Risk: Minimal · up to 9699d

No actionable merge-blocking issue is established. The change is ready to merge after normal checks.

Pre-merge checks: Passed 5

Check name Status Explanation
Title check Passed The title clearly identifies the performance change and names the main affected paths, cellToChildren and gridDisk.
Docstring Coverage Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentat…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Description check Passed The description explains the helper inlining changes, their performance goals, and the reported tests. It matches the changeset.
Full details: Docstring Coverage

Explanation

Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 28 functions across 10 files.

Finishing Touches

Generate docstrings

  • Commit to this branch
  • Create a new PR
Generate unit tests (beta)

  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

Recent review info

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 751f3900-930c-4c48-964a-24d9d512733b
Commits

Reviewing files that changed from the base of the PR and between 1195856 and 9699de3.

Files selected for processing: 11

  • src/h3lib/include/baseCells.h
  • src/h3lib/include/h3Index.h
  • src/h3lib/include/iterators.h
  • src/h3lib/lib/algos.c
  • src/h3lib/lib/baseCells.c
  • src/h3lib/lib/directedEdge.c
  • src/h3lib/lib/h3Index.c
  • src/h3lib/lib/iterGosper.c
  • src/h3lib/lib/iterators.c
  • src/h3lib/lib/polyfill.c
  • src/h3lib/lib/vertex.c
Files with no reviewable changes: 1

  • src/h3lib/lib/baseCells.c

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 7 remain after this review.

@coveralls
Copy link
Copy Markdown

Coverage Status

coverage: 99.758%. remained the same — inline-hot-helpers into master

@justinhwang justinhwang changed the title Make hot index helpers static inline for faster cellToChildren and gridDisk perf: make hot index helpers static inline for faster cellToChildren and gridDisk Nov 10, 2026
@justinhwang
justinhwang marked this pull request as ready for review November 10, 2026 21:17

This branch has not been deployed

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

No inlining in library

2 participants

0