@zip.js/zip.js 2.18.2 → 2.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BENCHMARKS.md +122 -118
- package/deno.json +1 -1
- package/dist/zip-core-external.js +54 -81
- package/dist/zip-core-external.min.js +1 -1
- package/dist/zip-core.js +54 -81
- package/dist/zip-core.min.js +1 -1
- package/dist/zip-fs-core-external.js +54 -81
- package/dist/zip-fs-core-external.min.js +1 -1
- package/dist/zip-fs-core.js +54 -81
- package/dist/zip-fs-core.min.js +1 -1
- package/dist/zip-fs-external.js +54 -81
- package/dist/zip-fs-external.min.js +1 -1
- package/dist/zip-fs-native.js +56 -83
- package/dist/zip-fs-native.min.js +1 -1
- package/dist/zip-fs.js +55 -82
- package/dist/zip-fs.min.js +1 -1
- package/dist/zip-legacy.js +56 -83
- package/dist/zip-legacy.min.js +1 -1
- package/dist/zip-native.js +56 -83
- package/dist/zip-native.min.js +1 -1
- package/dist/zip-web-worker-native.js +1 -1
- package/dist/zip-web-worker.js +1 -1
- package/dist/zip.js +55 -82
- package/dist/zip.min.js +1 -1
- package/index-native.cjs +56 -83
- package/index-native.min.js +1 -1
- package/index.cjs +55 -82
- package/index.d.cts +27 -18
- package/index.d.ts +27 -18
- package/index.min.js +1 -1
- package/lib/core/codec-pool.js +1 -2
- package/lib/core/codec-worker-web.js +11 -39
- package/lib/core/codec-worker.js +1 -2
- package/lib/core/configuration.js +1 -1
- package/lib/core/options.js +0 -2
- package/lib/core/streams/codec-stream.js +1 -1
- package/lib/core/version.js +1 -1
- package/lib/core/web-worker-base.js +2 -3
- package/lib/core/web-worker-inline-native.js +1 -1
- package/lib/core/web-worker-inline-wasm.js +1 -1
- package/lib/core/zip-reader.js +21 -23
- package/lib/core/zip-writer.js +18 -14
- package/package.json +1 -1
- package/worker-message-property-names.js +1 -1
package/BENCHMARKS.md
CHANGED
|
@@ -22,14 +22,14 @@ The page answers four questions, each from one or two scripts of the harness:
|
|
|
22
22
|
| | |
|
|
23
23
|
|---|---|
|
|
24
24
|
| Machine | Apple M2, 8 cores (4 performance + 4 efficiency), 16 GB RAM |
|
|
25
|
-
| OS | macOS
|
|
26
|
-
| Runtimes | Node.js v26.7.0, Bun 1.4.2, Deno 2.9.
|
|
27
|
-
| Browsers | Firefox
|
|
28
|
-
| zip.js | 2.
|
|
25
|
+
| OS | macOS 27.0 (arm64) |
|
|
26
|
+
| Runtimes | Node.js v26.7.0, Bun 1.4.2, Deno 2.9.7 |
|
|
27
|
+
| Browsers | Firefox 156, Chrome 153, headless (the browser encryption table only) |
|
|
28
|
+
| zip.js | 2.19.0, measured at 96084cc8; the later commits of the release change no codec or stream path |
|
|
29
29
|
| jszip | 3.10.2 |
|
|
30
30
|
| fflate | 0.8.3 |
|
|
31
31
|
| archiver | 8.0.0 |
|
|
32
|
-
| Measured | 2026-09-
|
|
32
|
+
| Measured | 2026-09-26 |
|
|
33
33
|
|
|
34
34
|
## Method
|
|
35
35
|
|
|
@@ -82,31 +82,31 @@ size each row produced:
|
|
|
82
82
|
|
|
83
83
|
| Library | Codec | Compressible text (20 MB) | Incompressible data (20 MB) | 5,000 files × ~2 KB |
|
|
84
84
|
|---|---|--:|--:|--:|
|
|
85
|
-
| @zip.js/zip.js | `CompressionStream` | **
|
|
86
|
-
| @zip.js/zip.js | WASM zlib |
|
|
87
|
-
| @zip.js/zip.js | pure-JS zlib |
|
|
88
|
-
| jszip | pako (JavaScript) |
|
|
89
|
-
| fflate | fflate (JavaScript) |
|
|
90
|
-
| archiver | Node `zlib` (C) | 702 ms / 6.1 MB |
|
|
85
|
+
| @zip.js/zip.js | `CompressionStream` | **698 ms** / 6.1 MB | 350 ms / 21.0 MB | 864 ms / 4.9 MB |
|
|
86
|
+
| @zip.js/zip.js | WASM zlib | 1049 ms / 6.2 MB | 461 ms / 21.0 MB | 666 ms / 4.9 MB |
|
|
87
|
+
| @zip.js/zip.js | pure-JS zlib | 1218 ms / 6.2 MB | 817 ms / 21.0 MB | 1107 ms / 4.9 MB |
|
|
88
|
+
| jszip | pako (JavaScript) | 1702 ms / 6.2 MB | 855 ms / 21.0 MB | 834 ms / 4.7 MB |
|
|
89
|
+
| fflate | fflate (JavaScript) | 908 ms / 6.3 MB | **296 ms** / 21.0 MB | **276 ms** / 4.8 MB |
|
|
90
|
+
| archiver | Node `zlib` (C) | 702 ms / 6.1 MB | 355 ms / 21.0 MB | 421 ms / 4.8 MB |
|
|
91
91
|
|
|
92
92
|
Peak memory for the same runs (Δ over baseline):
|
|
93
93
|
|
|
94
94
|
| Library | Codec | Compressible text (20 MB) | Incompressible data (20 MB) | 5,000 files × ~2 KB |
|
|
95
95
|
|---|---|--:|--:|--:|
|
|
96
|
-
| @zip.js/zip.js | `CompressionStream` |
|
|
97
|
-
| @zip.js/zip.js | WASM zlib |
|
|
98
|
-
| @zip.js/zip.js | pure-JS zlib |
|
|
99
|
-
| jszip | pako (JavaScript) | 69 MB | 93 MB |
|
|
100
|
-
| fflate | fflate (JavaScript) | **62 MB** | 108 MB | **
|
|
101
|
-
| archiver | Node `zlib` (C) |
|
|
102
|
-
|
|
103
|
-
On the text file zip.js with `CompressionStream` and archiver take the same time,
|
|
96
|
+
| @zip.js/zip.js | `CompressionStream` | 108 MB | 160 MB | 251 MB |
|
|
97
|
+
| @zip.js/zip.js | WASM zlib | 115 MB | 187 MB | 260 MB |
|
|
98
|
+
| @zip.js/zip.js | pure-JS zlib | 113 MB | 167 MB | 281 MB |
|
|
99
|
+
| jszip | pako (JavaScript) | 69 MB | 93 MB | 261 MB |
|
|
100
|
+
| fflate | fflate (JavaScript) | **62 MB** | 108 MB | **103 MB** |
|
|
101
|
+
| archiver | Node `zlib` (C) | 71 MB | **74 MB** | 136 MB |
|
|
102
|
+
|
|
103
|
+
On the text file zip.js with `CompressionStream` and archiver take the same time, 698 and
|
|
104
104
|
702 ms, for the same output size: both run Node's zlib. With the codecs that ship with each
|
|
105
|
-
library, fflate takes
|
|
106
|
-
pure-JavaScript port
|
|
105
|
+
library, fflate takes 908 ms for 2 % more bytes, zip.js's WebAssembly zlib 1049 ms, its
|
|
106
|
+
pure-JavaScript port 1218 ms and jszip 1702 ms. On incompressible data fflate is the fastest row
|
|
107
107
|
and archiver the smallest in memory. On 5,000 small files the WebAssembly backend is 23 % faster
|
|
108
108
|
than `CompressionStream`, so a native stream costs more per entry than a WebAssembly one; fflate
|
|
109
|
-
takes 41 % of the WebAssembly row's time there against
|
|
109
|
+
takes 41 % of the WebAssembly row's time there against 87 % on the 20 MB file, and the
|
|
110
110
|
difference is the work zip.js does per entry, a stream and a header each. The zip.js rows use
|
|
111
111
|
the most memory on the two 20 MB files.
|
|
112
112
|
|
|
@@ -117,31 +117,31 @@ excluded.
|
|
|
117
117
|
|
|
118
118
|
| Library | Codec | Compressible text (20 MB) | 5,000 files × ~2 KB |
|
|
119
119
|
|---|---|--:|--:|
|
|
120
|
-
| @zip.js/zip.js | `DecompressionStream` | **
|
|
121
|
-
| @zip.js/zip.js | WASM zlib |
|
|
122
|
-
| @zip.js/zip.js | pure-JS zlib |
|
|
123
|
-
| jszip | pako (JavaScript) |
|
|
124
|
-
| fflate | fflate (JavaScript) |
|
|
120
|
+
| @zip.js/zip.js | `DecompressionStream` | **61 ms** | 629 ms |
|
|
121
|
+
| @zip.js/zip.js | WASM zlib | 72 ms | 474 ms |
|
|
122
|
+
| @zip.js/zip.js | pure-JS zlib | 154 ms | 613 ms |
|
|
123
|
+
| jszip | pako (JavaScript) | 133 ms | 440 ms |
|
|
124
|
+
| fflate | fflate (JavaScript) | 123 ms | **102 ms** |
|
|
125
125
|
|
|
126
126
|
Peak memory for the same runs (Δ over baseline):
|
|
127
127
|
|
|
128
128
|
| Library | Codec | Compressible text (20 MB) | 5,000 files × ~2 KB |
|
|
129
129
|
|---|---|--:|--:|
|
|
130
|
-
| @zip.js/zip.js | `DecompressionStream` |
|
|
131
|
-
| @zip.js/zip.js | WASM zlib |
|
|
132
|
-
| @zip.js/zip.js | pure-JS zlib |
|
|
133
|
-
| jszip | pako (JavaScript) | 103 MB |
|
|
134
|
-
| fflate | fflate (JavaScript) | **92 MB** | **
|
|
135
|
-
|
|
136
|
-
On the 20 MB stream `DecompressionStream` takes
|
|
137
|
-
fflate
|
|
138
|
-
the module instantiation a fresh process pays once, about 20 ms. Warm, the
|
|
139
|
-
|
|
140
|
-
files the order reverses: fflate takes 102 ms, jszip
|
|
141
|
-
|
|
142
|
-
compression. fflate uses the least memory on both workloads, 34 % of the
|
|
143
|
-
row on the small files, and the WebAssembly and pure-JavaScript rows peak
|
|
144
|
-
`DecompressionStream`. The three zip.js rows allocate the same 800 MB of stream objects over the
|
|
130
|
+
| @zip.js/zip.js | `DecompressionStream` | 135 MB | 348 MB |
|
|
131
|
+
| @zip.js/zip.js | WASM zlib | 165 MB | 453 MB |
|
|
132
|
+
| @zip.js/zip.js | pure-JS zlib | 155 MB | 467 MB |
|
|
133
|
+
| jszip | pako (JavaScript) | 103 MB | 303 MB |
|
|
134
|
+
| fflate | fflate (JavaScript) | **92 MB** | **119 MB** |
|
|
135
|
+
|
|
136
|
+
On the 20 MB stream `DecompressionStream` takes 61 ms and the WebAssembly inflate 72 ms, then
|
|
137
|
+
fflate 123 ms, jszip 133 ms and the pure-JavaScript port 154 ms. The WebAssembly row includes
|
|
138
|
+
the module instantiation a fresh process pays once, about 20 ms. Warm, the WebAssembly inflate
|
|
139
|
+
is 24 % faster than `DecompressionStream` in the [Codecs](#codecs-at-equal-output-size) table.
|
|
140
|
+
On 5,000 small files the order reverses: fflate takes 102 ms, jszip 440 ms and the three zip.js
|
|
141
|
+
rows 474 to 629 ms, with `DecompressionStream` the slowest of them, the same per-entry cost as
|
|
142
|
+
in compression. fflate uses the least memory on both workloads, 34 % of the
|
|
143
|
+
`DecompressionStream` row on the small files, and the WebAssembly and pure-JavaScript rows peak
|
|
144
|
+
higher than `DecompressionStream`. The three zip.js rows allocate the same 800 MB of stream objects over the
|
|
145
145
|
5,000 entries, about 160 KB per entry. The WebAssembly and pure-JavaScript codecs run on the
|
|
146
146
|
JavaScript thread, so V8's incremental marking keeps more of that garbage alive between
|
|
147
147
|
collections, and that is the difference between the rows.
|
|
@@ -155,16 +155,16 @@ the Node streams directly.
|
|
|
155
155
|
|
|
156
156
|
| Library | Codec | Median time | Peak memory (Δ) | Output |
|
|
157
157
|
|---|---|--:|--:|--:|
|
|
158
|
-
| @zip.js/zip.js | `CompressionStream` | **
|
|
159
|
-
| archiver | Node `zlib` (C) |
|
|
160
|
-
| fflate | fflate (JavaScript) |
|
|
161
|
-
| @zip.js/zip.js | WASM zlib |
|
|
162
|
-
| @zip.js/zip.js | pure-JS zlib |
|
|
163
|
-
| jszip | pako (JavaScript) |
|
|
164
|
-
|
|
165
|
-
Every row streams: the peaks stay between
|
|
158
|
+
| @zip.js/zip.js | `CompressionStream` | **8927 ms** | 85 MB | 78.3 MB |
|
|
159
|
+
| archiver | Node `zlib` (C) | 9131 ms | 75 MB | 78.3 MB |
|
|
160
|
+
| fflate | fflate (JavaScript) | 12394 ms | **42 MB** | 80.2 MB |
|
|
161
|
+
| @zip.js/zip.js | WASM zlib | 13320 ms | 122 MB | 78.9 MB |
|
|
162
|
+
| @zip.js/zip.js | pure-JS zlib | 15082 ms | 116 MB | 78.9 MB |
|
|
163
|
+
| jszip | pako (JavaScript) | 22243 ms | 74 MB | 78.9 MB |
|
|
164
|
+
|
|
165
|
+
Every row streams: the peaks stay between 42 MB (fflate) and 122 MB (the WebAssembly zlib)
|
|
166
166
|
over baseline on the 256 MB input. zip.js with `CompressionStream` and archiver take the same
|
|
167
|
-
time within
|
|
167
|
+
time within 3 %; jszip takes 2.5× that. With its WebAssembly zlib zip.js takes 1.5× the
|
|
168
168
|
`CompressionStream` time and 1.1× fflate's, for 1.6 % fewer bytes; the pure-JavaScript port
|
|
169
169
|
takes 1.2× fflate's time.
|
|
170
170
|
|
|
@@ -187,17 +187,17 @@ fastest such row. A row with an empty last column is beaten by nothing on the ta
|
|
|
187
187
|
|
|
188
188
|
| Codec | Setting | Output | Ratio | Time | Throughput | Beaten by |
|
|
189
189
|
|---|---|--:|--:|--:|--:|---|
|
|
190
|
-
| WASM zlib | level 8 | 6,115,980 | 3.429 |
|
|
191
|
-
| `CompressionStream` | no level control | 6,120,503 | 3.426 |
|
|
192
|
-
| WASM zlib | level 6 | 6,165,103 | 3.402 |
|
|
193
|
-
| fflate | level 8, its best | 6,239,025 | 3.361 |
|
|
194
|
-
| fflate | level 6 | 6,257,881 | 3.351 |
|
|
195
|
-
| WASM zlib | level 5 | 6,562,182 | 3.196 |
|
|
196
|
-
| fflate | level 5 | 6,648,574 | 3.154 |
|
|
197
|
-
| WASM zlib | level 4 | 6,907,987 | 3.036 |
|
|
198
|
-
| fflate | level 1 | 7,261,700 | 2.888 |
|
|
199
|
-
| WASM zlib | level 2 | 7,359,387 | 2.850 | 195 ms | 102.
|
|
200
|
-
| WASM zlib | level 1 | 7,531,042 | 2.785 |
|
|
190
|
+
| WASM zlib | level 8 | 6,115,980 | 3.429 | 1479 ms | 13.5 MB/s | |
|
|
191
|
+
| `CompressionStream` | no level control | 6,120,503 | 3.426 | 693 ms | 28.9 MB/s | |
|
|
192
|
+
| WASM zlib | level 6 | 6,165,103 | 3.402 | 1026 ms | 19.5 MB/s | `CompressionStream` |
|
|
193
|
+
| fflate | level 8, its best | 6,239,025 | 3.361 | 755 ms | 26.5 MB/s | `CompressionStream` |
|
|
194
|
+
| fflate | level 6 | 6,257,881 | 3.351 | 732 ms | 27.3 MB/s | `CompressionStream` |
|
|
195
|
+
| WASM zlib | level 5 | 6,562,182 | 3.196 | 494 ms | 40.5 MB/s | |
|
|
196
|
+
| fflate | level 5 | 6,648,574 | 3.154 | 532 ms | 37.6 MB/s | WASM zlib level 5 |
|
|
197
|
+
| WASM zlib | level 4 | 6,907,987 | 3.036 | 275 ms | 72.7 MB/s | |
|
|
198
|
+
| fflate | level 1 | 7,261,700 | 2.888 | 315 ms | 63.6 MB/s | WASM zlib level 4 |
|
|
199
|
+
| WASM zlib | level 2 | 7,359,387 | 2.850 | 195 ms | 102.8 MB/s | |
|
|
200
|
+
| WASM zlib | level 1 | 7,531,042 | 2.785 | 171 ms | 116.7 MB/s | |
|
|
201
201
|
|
|
202
202
|
The pure-JavaScript port produces the same size as the WebAssembly codec at every level and
|
|
203
203
|
takes 1.0 to 1.8× its time, the gap closing at the higher levels; the full 28-row table is in
|
|
@@ -212,16 +212,16 @@ Decompression of the same stream:
|
|
|
212
212
|
|
|
213
213
|
| Codec | Time | Throughput |
|
|
214
214
|
|---|--:|--:|
|
|
215
|
-
| WASM zlib | **
|
|
216
|
-
| `DecompressionStream` |
|
|
217
|
-
| pure-JS zlib |
|
|
218
|
-
| fflate |
|
|
215
|
+
| WASM zlib | **42 ms** | 479.8 MB/s |
|
|
216
|
+
| `DecompressionStream` | 55 ms | 365.1 MB/s |
|
|
217
|
+
| pure-JS zlib | 109 ms | 182.7 MB/s |
|
|
218
|
+
| fflate | 121 ms | 165.6 MB/s |
|
|
219
219
|
|
|
220
|
-
The WebAssembly inflate
|
|
221
|
-
|
|
220
|
+
The WebAssembly inflate is 24 % faster than Node's `DecompressionStream`; fflate's inflate takes
|
|
221
|
+
2.9× the time of the WebAssembly one, the pure-JavaScript port 2.6×.
|
|
222
222
|
|
|
223
|
-
Alone, the WebAssembly backend deflates at level 6 in 1.
|
|
224
|
-
0.7 % more bytes and inflates
|
|
223
|
+
Alone, the WebAssembly backend deflates at level 6 in 1.48× the time of `CompressionStream` for
|
|
224
|
+
0.7 % more bytes and inflates 24 % faster than `DecompressionStream`. zip.js uses it by itself
|
|
225
225
|
when no `CompressionStream` exists or when a level other than the default is requested;
|
|
226
226
|
`useCompressionStream: false` selects it on every host, for an output that does not depend on
|
|
227
227
|
the host's zlib.
|
|
@@ -234,21 +234,21 @@ that buys with each backend.
|
|
|
234
234
|
|
|
235
235
|
| Configuration | Median time | Output | vs jszip |
|
|
236
236
|
|---|--:|--:|--:|
|
|
237
|
-
| **zip.js — `CompressionStream`, concurrent `add()`** | **
|
|
238
|
-
| fflate — async (its own worker pool) |
|
|
239
|
-
|
|
|
240
|
-
|
|
|
241
|
-
| fflate — `zipSync` (single thread) |
|
|
242
|
-
| zip.js — WASM zlib,
|
|
243
|
-
| zip.js — WASM zlib,
|
|
244
|
-
| zip.js — pure-JS zlib, concurrent `add()` |
|
|
245
|
-
| zip.js — pure-JS zlib, sequential `add()` |
|
|
246
|
-
| jszip (pako, single thread) |
|
|
247
|
-
|
|
248
|
-
With the host's `CompressionStream`, zip.js goes from
|
|
249
|
-
|
|
237
|
+
| **zip.js — `CompressionStream`, concurrent `add()`** | **598 ms** | 19.6 MB | **9.4×** |
|
|
238
|
+
| fflate — async (its own worker pool) | 764 ms | 20.0 MB | 7.4× |
|
|
239
|
+
| zip.js — `CompressionStream`, sequential `add()` | 2245 ms | 19.6 MB | 2.5× |
|
|
240
|
+
| archiver — Node zlib | 2270 ms | 19.6 MB | 2.5× |
|
|
241
|
+
| fflate — `zipSync` (single thread) | 3083 ms | 20.0 MB | 1.8× |
|
|
242
|
+
| zip.js — WASM zlib, concurrent `add()` | 3369 ms | 19.7 MB | 1.7× |
|
|
243
|
+
| zip.js — WASM zlib, sequential `add()` | 3389 ms | 19.7 MB | 1.7× |
|
|
244
|
+
| zip.js — pure-JS zlib, concurrent `add()` | 3822 ms | 19.7 MB | 1.5× |
|
|
245
|
+
| zip.js — pure-JS zlib, sequential `add()` | 3872 ms | 19.7 MB | 1.5× |
|
|
246
|
+
| jszip (pako, single thread) | 5629 ms | 19.7 MB | 1.0× |
|
|
247
|
+
|
|
248
|
+
With the host's `CompressionStream`, zip.js goes from 2245 ms with sequential `add()` calls to
|
|
249
|
+
598 ms with concurrent ones, 3.8×, without Web Workers: Node runs `CompressionStream` off the
|
|
250
250
|
main thread, so the entries compress on several cores while the JavaScript thread only feeds
|
|
251
|
-
them. fflate's async API, which runs its own worker pool, takes
|
|
251
|
+
them. fflate's async API, which runs its own worker pool, takes 764 ms on the same input; its
|
|
252
252
|
output is 2 % larger than the zlib rows, so it does slightly less work. The WebAssembly and
|
|
253
253
|
pure-JavaScript backends run on the main thread, and concurrent `add()` changes nothing for them.
|
|
254
254
|
|
|
@@ -269,22 +269,23 @@ JavaScript thread. Same 8 × 8 MB workload as the backends table, level 6. That
|
|
|
269
269
|
process per run and this one measures in-process after a warmup, which is why their Node numbers
|
|
270
270
|
differ:
|
|
271
271
|
|
|
272
|
-
| Runtime | sequential `add()` | concurrent `add()` | concurrent `add()`, `chunkSize`
|
|
272
|
+
| Runtime | sequential `add()` | concurrent `add()` | concurrent `add()`, `chunkSize` 64 KB | concurrent `add()` + `useWebWorkers` |
|
|
273
273
|
|---|--:|--:|--:|--:|
|
|
274
|
-
| Node.js v26.7.0 | 2.
|
|
275
|
-
| Bun 1.4.2 | 1.
|
|
276
|
-
| Deno 2.9.
|
|
274
|
+
| Node.js v26.7.0 | 2.23 s | **0.59 s** | 0.59 s | 0.61 s |
|
|
275
|
+
| Bun 1.4.2 | 1.20 s | **0.24 s** | 1.20 s | 0.25 s |
|
|
276
|
+
| Deno 2.9.7 | 1.39 s | 1.30 s | 1.31 s | **0.34 s** |
|
|
277
277
|
|
|
278
278
|
- **Node** runs `CompressionStream` off the JavaScript thread, on the libuv threadpool, four
|
|
279
279
|
threads by default (`UV_THREADPOOL_SIZE`): concurrent `add()` alone is 3.8× faster than
|
|
280
280
|
sequential, and the chunk size changes nothing. Node has no `Worker` global, so
|
|
281
281
|
`useWebWorkers` spawns nothing there and the entries run in-process, in the same time.
|
|
282
282
|
- **Bun** runs `CompressionStream` off the JavaScript thread only for writes larger than 128 KB
|
|
283
|
-
(its native implementation, since Bun 1.4 in August 2026).
|
|
284
|
-
default,
|
|
285
|
-
|
|
283
|
+
(its native implementation, since Bun 1.4 in August 2026). With the 256 KB chunks zip.js
|
|
284
|
+
writes by default, concurrent `add()` alone is 5.0× faster than sequential; with the 64 KB
|
|
285
|
+
chunks that were the default up to version 2.18.2 it gains nothing, and `useWebWorkers: true`
|
|
286
|
+
is 4.8× faster.
|
|
286
287
|
- **Deno** runs `CompressionStream` on the JavaScript thread whatever the write size: concurrent
|
|
287
|
-
`add()` alone gains
|
|
288
|
+
`add()` alone gains 7 %, `useWebWorkers: true` is 4.1× faster.
|
|
288
289
|
|
|
289
290
|
### Compression levels
|
|
290
291
|
|
|
@@ -294,13 +295,13 @@ column runs the same JavaScript on each engine. One 20 MB text entry, one thread
|
|
|
294
295
|
|
|
295
296
|
| Runtime | level 6, `CompressionStream` | level 5, WASM zlib | level 5, pure-JS zlib | level 1, WASM zlib |
|
|
296
297
|
|---|--:|--:|--:|--:|
|
|
297
|
-
| Node.js v26.7.0 |
|
|
298
|
-
| Bun 1.4.2 |
|
|
299
|
-
| Deno 2.9.
|
|
298
|
+
| Node.js v26.7.0 | 694 ms / 6.12 MB | 509 ms / 6.56 MB | 773 ms / 6.56 MB | 185 ms / 7.53 MB |
|
|
299
|
+
| Bun 1.4.2 | 368 ms / 6.20 MB | 459 ms / 6.56 MB | 661 ms / 6.56 MB | 177 ms / 7.53 MB |
|
|
300
|
+
| Deno 2.9.7 | 408 ms / 6.20 MB | 574 ms / 6.56 MB | 666 ms / 6.56 MB | 219 ms / 7.53 MB |
|
|
300
301
|
|
|
301
|
-
On Node, level 5 on the WebAssembly codec is
|
|
302
|
+
On Node, level 5 on the WebAssembly codec is 27 % faster than the default for 7 % more bytes.
|
|
302
303
|
On Bun and Deno the default is already faster than level 5, so level 5 there is slower and
|
|
303
|
-
larger; only level 1 buys speed on them,
|
|
304
|
+
larger; only level 1 buys speed on them, 52 % on Bun and 46 % on Deno, at 21 % more bytes. The
|
|
304
305
|
pure-JavaScript port at level 5 takes 1.5× the WebAssembly time on Node, 1.4× on Bun and 1.2×
|
|
305
306
|
on Deno, and is slower than the default on every runtime. The two bundled codecs produce the
|
|
306
307
|
same size on every runtime; the level-6 sizes differ because they come from three different
|
|
@@ -310,17 +311,17 @@ zlib builds.
|
|
|
310
311
|
|
|
311
312
|
On bulk data zip.js adds almost nothing over the host's `CompressionStream`, so its throughput is
|
|
312
313
|
that of the zlib the runtime ships, and they differ. One 256 MB text file, one thread, in
|
|
313
|
-
memory, fed in
|
|
314
|
+
memory, fed in 256 KB writes as zip.js does; gzip is Apple's, timed as a child process:
|
|
314
315
|
|
|
315
316
|
| Runtime | `CompressionStream` alone | zip.js, one entry | Output |
|
|
316
317
|
|---|--:|--:|--:|
|
|
317
|
-
| Node.js v26.7.0 | 8.
|
|
318
|
-
| Bun 1.4.2 | 4.
|
|
319
|
-
| Deno 2.9.
|
|
320
|
-
| Apple `gzip -6`, classic zlib | 12.
|
|
318
|
+
| Node.js v26.7.0 | 8.85 s | 8.91 s | 78.3 MB |
|
|
319
|
+
| Bun 1.4.2 | 4.72 s | 4.75 s | 79.4 MB |
|
|
320
|
+
| Deno 2.9.7 | 5.05 s | 5.20 s | 79.4 MB |
|
|
321
|
+
| Apple `gzip -6`, classic zlib | 12.2 to 12.6 s | | 78.9 MB |
|
|
321
322
|
|
|
322
|
-
zip.js is within
|
|
323
|
-
takes 53 % of Node's time and Deno's 57 %, for about 1.
|
|
323
|
+
zip.js is within 3 % of the raw stream on every runtime. On this file Bun's `CompressionStream`
|
|
324
|
+
takes 53 % of Node's time and Deno's 57 %, for about 1.4 % more bytes; Apple's gzip takes 1.4×
|
|
324
325
|
Node's time. Which zlib each runtime ships is not verified here; the table measures the result.
|
|
325
326
|
|
|
326
327
|
## Encryption: AES-256 on a stored entry
|
|
@@ -334,29 +335,29 @@ one archive written, then read back.
|
|
|
334
335
|
|
|
335
336
|
| Engine | Node.js | Bun | Deno |
|
|
336
337
|
|---|--:|--:|--:|
|
|
337
|
-
| WebAssembly, linked into the module of the WebAssembly builds | **
|
|
338
|
-
| JavaScript, the fallback of those builds and the engine of the native and core builds |
|
|
339
|
-
| 2.13.1, sjcl |
|
|
338
|
+
| WebAssembly, linked into the module of the WebAssembly builds | **138 / 139 MB/s** | **122 / 124** | 128 / 128 |
|
|
339
|
+
| JavaScript, the fallback of those builds and the engine of the native and core builds | 118 / 107 | 115 / 115 | **145 / 145** |
|
|
340
|
+
| 2.13.1, sjcl | 26 / 26 | 28 / 28 | 20 / 19 |
|
|
340
341
|
|
|
341
342
|
The same measurement in a browser, through the Web Worker pool, on 32 MB of bytes generated in
|
|
342
343
|
the page (the corpus is not served to the browser), median of 3 passes
|
|
343
344
|
([`benchmarks/bench-aes.html`](benchmarks/bench-aes.html), which prints its result and writes no
|
|
344
345
|
file):
|
|
345
346
|
|
|
346
|
-
| Engine | Firefox
|
|
347
|
+
| Engine | Firefox 156 | Chrome 153 |
|
|
347
348
|
|---|--:|--:|
|
|
348
|
-
| WebAssembly | **
|
|
349
|
-
| JavaScript |
|
|
349
|
+
| WebAssembly | **125 / 125 MB/s** | **136 / 138** |
|
|
350
|
+
| JavaScript | 79 / 79 | 104 / 104 |
|
|
350
351
|
| 2.13.1, sjcl | 17 / 17 | 22 / 22 |
|
|
351
352
|
|
|
352
|
-
- The JavaScript engine is 4.
|
|
353
|
-
7.
|
|
353
|
+
- The JavaScript engine is 4.1 to 4.7× sjcl on Node, Bun and in the two browsers, and 7.3 to
|
|
354
|
+
7.6× on Deno, where sjcl was slowest. sjcl ran the cipher 16 bytes
|
|
354
355
|
at a time through a bit-array layer; the new engine works on typed arrays and is fed whole
|
|
355
356
|
chunks, with the AES rounds and the SHA-1 steps written out. Every build has it.
|
|
356
357
|
- The WebAssembly kernel is the same C code on every host, with the same rounds written out:
|
|
357
|
-
|
|
358
|
-
engine is within
|
|
359
|
-
the browsers. The WebAssembly builds use
|
|
358
|
+
122 to 139 MB/s on Node, Bun and Deno, 125 to 138 MB/s in the two browsers. The JavaScript
|
|
359
|
+
engine is within 8 % of it on Bun and 23 % on Node, ahead of it on Deno, and 1.3 to 1.6×
|
|
360
|
+
slower in the browsers. The WebAssembly builds use
|
|
360
361
|
the kernel whenever their module loads and fall back to the JavaScript engine when it cannot,
|
|
361
362
|
for instance under a Content Security Policy that does not allow WebAssembly
|
|
362
363
|
(`'wasm-unsafe-eval'`).
|
|
@@ -369,8 +370,8 @@ file):
|
|
|
369
370
|
bytes in 2.13.1 to 67,820 in 2.14.0, `dist/zip-native.min.js` from 73,798 to 72,060 and
|
|
370
371
|
`dist/zip-core.min.js` from 36,405 to 35,493 (`git show <tag>:<file> | gzip -9 | wc -c`):
|
|
371
372
|
sjcl's removal outweighs the JavaScript engine and the kernel. Writing the rounds out after
|
|
372
|
-
2.14.0
|
|
373
|
-
|
|
373
|
+
2.14.0 gave some of it back, and the three files behind the encryption tables above gzip to
|
|
374
|
+
73,184, 75,050 and 37,264 bytes.
|
|
374
375
|
|
|
375
376
|
## Reproduce
|
|
376
377
|
|
|
@@ -390,7 +391,10 @@ node bench-runtimes.js # the runtime tables; also: bun bench-runtimes.js, deno
|
|
|
390
391
|
|
|
391
392
|
Each script writes its JSON to `benchmarks/results/`; the `*-run.log` files there are the
|
|
392
393
|
console output of the runs behind this page. `RUNS=<n>` sets the number of repetitions (default
|
|
393
|
-
3, 5 in `bench-aes.js`). `
|
|
394
|
+
3, 5 in `bench-aes.js`). `QUICK=1` divides every workload by 8, runs each measurement once and
|
|
395
|
+
writes under `benchmarks/results/quick/`; the scripts then finish in seconds instead of minutes,
|
|
396
|
+
which is enough to check that they run and not enough to read a number from.
|
|
397
|
+
`ZIPJS_BUNDLE=<path to a previous index.min.js> node bench-aes.js` adds
|
|
394
398
|
the row of a previous release; `git show v2.13.1:index.min.js` gave the sjcl row. The browser
|
|
395
399
|
encryption table comes from `benchmarks/bench-aes.html`, served from the repository root
|
|
396
400
|
(`npx http-server -p 8080`, then `http://localhost:8080/benchmarks/bench-aes.html`), with
|