Optimizing Hermes Bytecode Bundle Size and SourceMap Handling in React Native
To counter the 40‑100 % size increase of Hermes bytecode bundles after upgrading to React Native 0.70, the team switched to xz compression (cutting zip size 20‑26 %), enabled hermesc ‑O optimization (shrinking bundles 10‑22 % while requiring a two‑step source‑map merge), and used the ‑base‑bytecode option with bsdiff to reduce incremental OTA patches by up to 85 %, collectively improving download and update efficiency.
Background : After upgrading React Native to 0.70 the Hermes engine is used. Hermes provides pre‑compilation and bytecode execution, but converting a JS bundle to a Hermes Bytecode Bundle (HBC) increases the ZIP size by 40%‑100% and the incremental package size by 2‑3×.
To mitigate the size growth, two main approaches were explored:
Changing the compression method of the final product.
Optimizing the export of the bundle itself.
Compression Method Comparison
Historically a simple zip was used. Three higher‑ratio algorithms were evaluated: gzip , bzip2 and xz .
Algorithm Details
gzip : uses the DEFLATE algorithm.
bzip2 : uses Burrows‑Wheeler transform + Huffman coding.
xz : uses the LZMA algorithm.
Performance Metrics (compression level 6)
Compression speed: xz = 1 min 27 s, gzip = 5 s, bzip2 = 8 s.
Memory usage during compression: xz = 97 656 KB, gzip = 2 048 KB, bzip2 = 6 164 KB.
Compression ratio: xz = 73.62 %, bzip2 = 70.32 %, gzip = 63.48 %.
Decompression speed: gzip = 0.8 s, xz = 1 s 9, bzip2 = 5 s 5.
Decompression memory: xz = 10 580 KB, gzip = 1 876 KB, bzip2 = 3 812 KB.
Considering that compression speed and memory are not user‑visible (compression runs on the build server), the highest compression ratio and acceptable decompression speed lead to choosing xz as the new compression method.
Applying xz reduced the ZIP‑compressed HBC bundle size by 20‑26 % across several product variants.
Bundle Export Optimizations
When converting a plain text bundle to an HBC bundle, the Hermes compiler ( hermesc ) offers an -O flag for highest‑level optimization. Using -O yields a 10 %‑22 % reduction in bundle size, mainly by stripping the Symbol Table (SourceMap) from the output.
Example command to generate a plain bundle with a SourceMap:
npx react-native bundle --platform ios --dev false --entry-file index.js --bundle-output ./build/index.ios.bundle --sourcemap-output ./build/index.ios.bundle.packager.mapGenerating an optimized HBC bundle:
hermesc -O -emit-binary -output-source-map -out=./build/index.ios.bundle.hbc ./build/index.ios.bundleBecause the -O flag removes the Symbol Table, stack traces from a production HBC bundle contain only line 1 information, making debugging impossible.
Solution: export a separate SourceMap for the HBC bundle using the --sourcemap-output option, then combine it with the original plain‑bundle SourceMap. The combined map allows a two‑step resolution:
Use the HBC bundle SourceMap to map the optimized bytecode location back to the original plain‑bundle line/column.
Use the plain‑bundle SourceMap to resolve that line/column to the original source file and symbol.
Both maps can be merged into a single file with:
./node_modules/react-native/scripts/compose-source-maps.js ./build/index.ios.bundle.packager.map ./build/index.ios.bundle.hbc.map -o ./build/index.ios.bundle.mapIncremental Package Reduction
Incremental updates are generated with bsdiff . The Hermes compiler provides a -base-bytecode option to specify a base bytecode file that contains shared code. When this option is used, only the delta between the new bundle and the base is stored, dramatically shrinking the diff.
Example without -base-bytecode :
// Generate new HBC (≈2.63 MB)
hermes -emit-binary ./test1.bundle -out ./test1.hbc
// Generate patch (≈65 KB)
bsdiff test.hbc test1.hbc patchfileExample with -base-bytecode (using a pre‑built base file):
// Generate new HBC (size unchanged ≈2.65 MB)
hermes -emit-binary -base-bytecode='baseBytecodeTest.hbc' ./test1.bundle -out ./test1.hbc
// Generate patch (≈9 KB)
bsdiff baseBytecodeTest.hbc test1.hbc patchfileUsing -base-bytecode reduces the incremental package size by 80 %‑85 % compared with the naïve approach.
Conclusions
Applying -O during HBC bundle generation reduces bundle size by 10 %‑22 %.
When -O is used, the HBC bundle’s stack traces must be resolved via a two‑step SourceMap process (HBC → plain bundle → source).
Alternatively, merge the two SourceMaps into a single map for one‑step resolution.
Using -base-bytecode together with bsdiff cuts incremental package size by up to 85 %.
These optimizations improve download size and OTA update efficiency for React Native applications.
NetEase Cloud Music Tech Team
Official account of NetEase Cloud Music Tech Team
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.