Skip to content

Sync headers from barry 0.2.2: fix FreqTable/vecHasher hash collisions - #8

Merged
gvegayon merged 2 commits into
mainfrom
fix/freqtable-hash-collisions
Jul 15, 2026
Merged

gvegayon merged 2 commits into
mainfrom
fix/freqtable-hash-collisions

Conversation

@gvegayon

Copy link
Copy Markdown
Member

Summary

Syncs inst/include/barry/ with USCbiostats/barry#24 and bumps the package to 0.2.2, following barry's versioning.

What the upstream fix does: std::hash of small-integer doubles (0.0, 1.0, ...) is a bit-cast with ~50 zero low bits, and the boost-style combine in FreqTable::make_hash/vecHasher never propagated entropy into the low bits. libc++'s unordered_map masks the hash with a power-of-two bucket count — exactly those bits — so all keys landed in a single bucket and support computation degenerated from O(2^ncol) to O(4^ncol). In practice, defm::init_defm() on a 16-outcome model appeared to hang indefinitely (this is how the bug surfaced, via a UofUEpiBio/defm user). Both hash sites now apply a splitmix64 finalizer per element, and FreqTable::add() additionally verifies the stored row on a hash hit so true 64-bit collisions chain instead of silently merging distinct statistics.

After the sync, the vendored tree is byte-identical to barry master + PR 24 (diff -rq clean).

Measurements (via defm rebuilt against these headers)

Scenario Before After
init_defm, 16 outcome columns, 1 unique Markov state 14.4 s 0.05 s
init_defm, 16 outcome columns, 20 unique Markov states >5 min (timed out) ~1.4 s

Testing

  • R CMD INSTALL clean
  • Downstream: defm recompiled against these headers passes its full tinytest suite (25 results)
  • Upstream: barry's make test-portable passes (122 assertions, 21 test cases) on the same header set

Merge order: ideally after USCbiostats/barry#24, so the vendored headers correspond to a tagged upstream state. A companion PR updates defm to require barry (>= 0.2.2).

🤖 Generated with Claude Code

…sher

Syncs inst/include/barry/ with USCbiostats/barry#24, which fixes a
catastrophic hash-collision bug: std::hash of small-integer doubles has
no entropy in its low bits, and the boost-style combine never propagated
any down, so unordered_map (which masks the hash with a power-of-two
bucket count) placed all keys in a single bucket. Support computation
degenerated to O(4^ncol) and models with many response columns (e.g.,
the 16-outcome DEFM that surfaced this) appeared to hang in init.

Both hash sites now apply a splitmix64 finalizer per element, and
FreqTable::add() verifies the stored row on a hash hit, chaining true
64-bit collisions instead of silently merging distinct statistics.

Downstream check: defm rebuilt against these headers passes its full
tinytest suite (25 results); a 16-column model init drops from >5
minutes (never observed to finish on real data) to ~1.4s.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 14, 2026 •

Copy link
Copy Markdown

Thank you for your contribution @gvegayon 🚀! The pkgdown site preview is ready for review 👉 Download here 👈!
(The artifact expires on 2026-10-13T17:45:00Z. You can re-generate it by re-running the workflow here.)

…ards

Follows USCbiostats/barry#24 review: the splitmix64 finalizer now mixes
in an explicit uint64_t (identical avalanche on 32- and 64-bit targets),
make_hash/vecHasher guard against empty input, and a length_error typo
is fixed. Keeps inst/include/barry byte-identical to barry master.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@gvegayon
gvegayon merged commit fdd41d5 into main Jul 15, 2026
12 checks passed
gvegayon added a commit to UofUEpiBio/defm that referenced this pull request Jul 15, 2026
CI could not resolve `barry (>= 0.2.2)`: CRAN only has 0.2.1, and pak's
`deps::.` (used by setup-r-dependencies) does not consume the DESCRIPTION
`Remotes:` field when a satisfying-named package exists on CRAN, so the
GitHub source for the newer barry was never considered.

Add the barryr PR branch as an explicit `extra-packages` entry in every
workflow that installs dependencies. The action passes extra-packages
straight to `pak::lockfile_create()`, which does honor an explicit
remote spec; verified locally with pak 0.10.0 in a clean library that
the graph now solves with barry -> 0.2.2 from the branch.

All three entries are temporary and must be removed once barry 0.2.2 is
on CRAN (tracked via USCbiostats/barryr#8); each is commented as such.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
gvegayon added a commit to UofUEpiBio/defm that referenced this pull request Jul 15, 2026
USCbiostats/barryr#8 is merged, so barryr's main branch is barry 0.2.2.
Drop the PR-branch ref from the extra-packages remotes; plain
USCbiostats/barryr now resolves to 0.2.2 and won't break when the branch
is deleted. Verified with pak 0.10.0 in a clean library: the graph
solves with barry -> 0.2.2 from barryr main (commit fdd41d5).

Still temporary: remove these extra-packages entries entirely once
barry 0.2.2 reaches CRAN.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
gvegayon added a commit to UofUEpiBio/defm that referenced this pull request Jul 15, 2026
…uptible (#8)

* Require barry >= 0.2.2 and make long computations interruptible

barry 0.2.2 (USCbiostats/barry#24) fixes a catastrophic hash-collision
bug that made init_defm() appear to hang for models with many outcome
columns: all support statistics landed in a single unordered_map bucket,
degrading support enumeration to O(4^ncol). A 16-outcome, order-1 model
that previously never finished initializing now takes seconds. The
LinkingTo requirement ensures defm is compiled against fixed headers.

This also defines BARRY_USER_INTERRUPT as Rcpp::checkUserInterrupt() in
every translation unit that includes barry, enabling barry's built-in
interrupt checkpoint (every 1000 support iterations). Long-running calls
such as init_defm() can now be stopped with Ctrl-C from an interactive
session instead of requiring a force-kill. Verified via setTimeLimit(),
which uses the same R_CheckUserInterrupt checkpoint: a ~20s init aborts
at the 3s limit with a clean R error and no crash. A model interrupted
mid-initialization should be rebuilt before further use (same caveat as
any error thrown during init).

All 25 tinytest results pass against barry 0.2.2.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Adding the remotes field in the description (will remove once the package lands on CRAN)

* ci: install barry 0.2.2 from barryr PR branch until it reaches CRAN

CI could not resolve `barry (>= 0.2.2)`: CRAN only has 0.2.1, and pak's
`deps::.` (used by setup-r-dependencies) does not consume the DESCRIPTION
`Remotes:` field when a satisfying-named package exists on CRAN, so the
GitHub source for the newer barry was never considered.

Add the barryr PR branch as an explicit `extra-packages` entry in every
workflow that installs dependencies. The action passes extra-packages
straight to `pak::lockfile_create()`, which does honor an explicit
remote spec; verified locally with pak 0.10.0 in a clean library that
the graph now solves with barry -> 0.2.2 from the branch.

All three entries are temporary and must be removed once barry 0.2.2 is
on CRAN (tracked via USCbiostats/barryr#8); each is commented as such.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* ci: point barry remote at barryr main now that #8 is merged

USCbiostats/barryr#8 is merged, so barryr's main branch is barry 0.2.2.
Drop the PR-branch ref from the extra-packages remotes; plain
USCbiostats/barryr now resolves to 0.2.2 and won't break when the branch
is deleted. Verified with pak 0.10.0 in a clean library: the graph
solves with barry -> 0.2.2 from barryr main (commit fdd41d5).

Still temporary: remove these extra-packages entries entirely once
barry 0.2.2 reaches CRAN.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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.

1 participant