Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

hbc-diff-benchmark

中文说明

An empirical benchmark of binary diff algorithms for React Native OTA updates, comparing:

  1. bsdiff 4.3 — the classic binary diff (bzip2-compressed), used by many OTA systems
  2. bsdiff + lzma — the identical bsdiff delta streams (control/diff/extra), recompressed with xz -9e instead of bzip2, to separate the compressor effect from the algorithm effect
  3. hdiffHDiffPatch via node-hdiffpatch 2.2.1 (lzma2), the default diff engine of react-native-update
  4. hdiff + HBC transform — the same hdiff, applied after a Hermes-bytecode-aware delta-friendly reversible transform (the production path of pushy diff --hbcTransform in react-native-update-cli)

Why Hermes bytecode needs a dedicated transform

Hermes bytecode (HBC) is full of tables carrying absolute offsets (functionHeaders, smallStringTable, overflowStringTable, …). A one-line JS change inserts a few bytes in the middle of the file — and every offset after the insertion point shifts by N. Exact-match diff engines then see thousands of "changed" table entries that are really the same entry plus a constant.

The transform delta-encodes those offset bitfields (wrapping subtraction, modulo field width), so a global shift collapses into a single-point change. It is:

  • exactly reversible: T⁻¹(T(x)) === x for any input, verified on every run
  • layout-table driven: the layout description ships inside each patch (__diff.json), so the client-side C interpreter has zero Hermes-version-specific code — new Hermes versions only need a new table entry on the CLI side
  • fail-safe: every section bound and bitfield range is validated before any byte is touched; on mismatch the pipeline falls back to plain hdiff

Test data

Fixtures are real bundles of react-native-update's example app (React Native 0.86.0, Hermes HBC v98, dependencies include react-native-paper, react-native-svg, @sentry/react-native, react-native-camera-kit), built with react-native bundle --dev false --minify true + hermesc -O -emit-binary:

Scenario Change against base
S1 One-line text change (edit a string literal)
S2 Small feature: +1 new component, ~60 LOC (react-native-paper Card/Chip/Divider)
S3 Medium feature: +2 new screens, ~300 LOC (DataTable, SegmentedButtons, Searchbar, …)

Base bundle: 3.24 MB JS / 4.42 MB HBC.

Results

Measured on Apple M2, Node v24.11.1. Every patch is verified by an actual round-trip (bspatch / hdiffpatch.patch + inverse transform must reproduce the exact new bytes) before its size is reported.

Hermes bytecode (.hbc) patches — what actually ships in production

Scenario Full (gzip) bsdiff bsdiff+lzma hdiff hdiff + HBC transform vs bsdiff+lzma vs hdiff
S1: one-line text change 1901.5 KB 93.7 KB 87.7 KB 89.0 KB 63.5 KB −28% −29%
S2: small feature (~60 LOC) 1913.9 KB 411.6 KB 330.9 KB 328.0 KB 285.5 KB −14% −13%
S3: medium feature (~300 LOC) 1973.7 KB 551.6 KB 430.6 KB 431.6 KB 398.4 KB −7% −8%

Plain JS bundle patches (for reference, non-Hermes apps)

Scenario Full (gzip) bsdiff bsdiff+lzma hdiff
S1: one-line text change 807.2 KB 0.3 KB 0.7 KB 0.1 KB
S2: small feature (~60 LOC) 813.1 KB 7.4 KB 7.4 KB 5.8 KB
S3: medium feature (~300 LOC) 837.7 KB 38.4 KB 37.4 KB 28.7 KB

Diff generation time (Hermes bytecode)

Scenario bsdiff hdiff hdiff + HBC transform
S1 774 ms 254 ms 221 ms
S2 1099 ms 414 ms 369 ms
S3 1212 ms 436 ms 418 ms

The transform costs single-digit milliseconds; diffing the transformed pair is actually faster than diffing the raw bytes, because the inputs match better.

Takeaways

  • Incremental updates beat full-bundle OTA by ~5–30× on bandwidth (e.g. 63.5 KB vs 1.9 MB for a one-line change).
  • With the compressor normalized, bsdiff+lzma and hdiff land within ~1% of each other on raw HBC — the remaining 7–29% cut delivered by the HBC-aware transform is pure algorithmic gain, not a compressor artifact, with the biggest wins exactly where OTA updates are most frequent: small changes.
  • hdiff generates patches 2–4× faster than bsdiff on these inputs; the transform itself costs single-digit milliseconds.

Reproduce

npm install --ignore-scripts   # node-hdiffpatch ships prebuilds
brew install bsdiff            # or any bsdiff 4.3
node bench.mjs

Fixtures are committed, so the benchmark itself needs no React Native toolchain. To regenerate fixtures from source, see fixtures/GENERATION.md.

Layout

  • bench.mjs — the benchmark: runs all four algorithm variants on every scenario, verifies every patch round-trip, prints the tables above
  • vendor/hbcTransform.mjs — the HBC transform, vendored (types stripped) from react-native-update-cli src/utils/hbcTransform.ts (BSD-3-Clause)
  • fixtures/ — the real app bundles described above
  • out/ — generated patches and results.json / results.md

中文说明

本仓库对 React Native 热更新场景下的二进制 diff 算法做实证评测:bsdiff 4.3、bsdiff+lzma 对照组、hdiff(HDiffPatch/lzma2,react-native-update 的默认 diff 引擎)、以及 hdiff + Hermes 字节码(HBC)针对性可逆变换pushy diff --hbcTransform 的生产路径)。

原理:HBC 中大量表结构携带绝对偏移,JS 一处小改动会让其后所有偏移整体位移,精确匹配型 diff 无法复用这些条目。对偏移位域做前项差分(wrapping)后,整体位移在差分域退化为单点变化,diff 显著变小。变换严格可逆、按布局描述表驱动(随补丁下发,客户端零版本分支)、变换前全量校验、失败自动回退。

测试数据:react-native-update 示例应用(RN 0.86.0,HBC v98)的真实产物,三个典型迭代场景:S1 单行文案修改、S2 小功能(+1 组件约 60 行)、S3 中等功能(+2 页面约 300 行)。另设 bsdiff+lzma 对照组(同一份 bsdiff 差分流改用 xz -9e 压缩),用于区分"压缩器差异"与"算法差异"。

结论(Apple M2 实测,所有补丁均经过往返校验):

  • 增量更新比全量 OTA 节省 约 5~30 倍流量(单行改动 63.5 KB vs 全量 1.9 MB);
  • 压缩器拉平后(bsdiff+lzma 与 hdiff 在原始 HBC 上差距不到 1%),HBC 针对性变换带来的 7%~29% 缩减是纯算法收益,越是高频的小改动收益越大;
  • hdiff 生成补丁比 bsdiff 快 2~4 倍,变换本身开销仅个位数毫秒。

复现方式见上文 Reproduce

License

MIT (vendored vendor/hbcTransform.mjs retains its original BSD-3-Clause license).

About

Empirical benchmark: bsdiff vs hdiff vs Hermes-bytecode-aware hdiff for React Native OTA updates

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages