base64-js icon indicating copy to clipboard operation
base64-js copied to clipboard

Performance improvements and fixed a bug in fromByteArray() when extra bytes are negative

Open calebsander opened this issue 6 years ago • 4 comments

Performance improvements:

  • Pre-allocating arrays in encodeChunk() and fromByteArray()
  • Removing unnecessary operations, e.g. increments of a variable that is never used again
  • Using bitwise operators (e.g. & 3 instead of % 4 and | instead of +) where possible
  • Removing an unnecessary argument being passed to _byteLength()
  • Removing unnecessary branching

I see a speed increase of about 10% in the benchmark for fromByteArray().

Also fixed a bug with fromByteArray() when it is passed negative "extra bytes," i.e. bytes at or after the last index that is a multiple of 3. encodeChunk() masks each byte with 0xFF, but this was missing in the logic for when extraBytes is 1 or 2. It's not clear to me why it's necessary to support passing in negative bytes (especially as this is impossible with a Uint8Array), but there are test cases that do so, so I wanted to make the code consistent.

calebsander avatar Jul 17 '18 07:07 calebsander

More efficient is to use string concat then parts array, e.g. see https://jsperf.com/javascript-concat-vs-join/2

xmedeko avatar Sep 12 '19 12:09 xmedeko

I found it was possible to slightly improve performance by using string concatenation of the encoded chunks, but using string concatenation in encodeChunk() led to significant performance decreases:

join() for all
base64.fromByteArray() (encode) x 58.66 ops/sec ±1.24% (61 runs sampled)

+= between chunks, join() within chunk
base64.fromByteArray() (encode) x 62.62 ops/sec ±1.14% (65 runs sampled)

+= for all
base64.fromByteArray() (encode) x 20.20 ops/sec ±1.43% (38 runs sampled)

calebsander avatar Sep 12 '19 15:09 calebsander

@calebsander Can you share output from running the performance suite before and after your changes? Thanks.

feross avatar Sep 12 '19 18:09 feross

Sure. Here is the output from running node bench/bench.js on the upstream master (08a344d):

base64.toByteArray() (decode) x 320 ops/sec ±0.77% (88 runs sampled)
base64.fromByteArray() (encode) x 60.11 ops/sec ±0.90% (62 runs sampled)
base64.byteLength() (encode) x 42,536 ops/sec ±0.29% (94 runs sampled)

and with my changes (804d47e):

base64.toByteArray() (decode) x 342 ops/sec ±0.77% (90 runs sampled)
base64.fromByteArray() (encode) x 64.79 ops/sec ±0.57% (67 runs sampled)
base64.byteLength() (encode) x 42,628 ops/sec ±0.27% (94 runs sampled)

calebsander avatar Sep 12 '19 19:09 calebsander