Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

base64 encode/decode is really slow #1187

Open
yasushi-saito opened this issue Jan 18, 2022 · 4 comments
Open

base64 encode/decode is really slow #1187

yasushi-saito opened this issue Jan 18, 2022 · 4 comments

Comments

@yasushi-saito
Copy link

yasushi-saito commented Jan 18, 2022

Background: we have a grpc-web-based app that performs bulk data upload, and it can sustain only about ~15MB/s regardless of the underlying network speed. The reason is that the base64 encoder used by grpc-web (goog.crypt.base64) is quite slow. I did a quick benchmark between goog.crypt.base64.encodeByteArray vs Base64.fromUint8Array (from https://www.npmjs.com/package/js-base64), and the former is about 50x slower than js-base64. E.g., encoding a random 16MB Uint8Array payload takes about ~1000ms with goog.crypt.base64, 19ms with js-base64.

@yasushi-saito
Copy link
Author

FYI, here's a node script that I used.

require('google-closure-library');
goog.require('goog.crypt.base64');
const {Base64} = require('js-base64');

// Create a Uint8Array with random contents.
function randomArray(len) {
    const a = new Uint8Array(len);
    for (let i = 0; i < len; i += 1) {
        a[i] = Math.ceil((Math.random()) * 256);
    }
    return a;
}

function runBench(label, len, encode, decode) {
    // Run a encode/decode roundtrip once and verify their behavior.
    const bin = randomArray(len);
    const str = encode(bin) // base64 string
    const bin1 = decode(str); // should be the same as bin
    if (bin1.length != bin.length) {
        throw Error(`wrong length: got ${bin1.length} want ${bin.length}`);
    }
    for (let i = 0; i < bin.length; i += 1) {
        if (bin[i] != bin1[i]) {
            throw Error(`wrong data at ${i}: got ${bin1[i]} want ${bin[i]}`);
        }
    }

    // Run a given function in a loop and measure a mean runtime.
    const run = (cb) => {
        const startTime = new Date();
        let rep = 0;
        for (; ;) {
            for (let i = 0; i < 10; i += 1) {
                cb();
                rep += 1;
            }
            const elapsed = new Date() - startTime;
            if (elapsed > 1000) {
                return elapsed / rep;
            }
        }
    };

    const encodeTime = run(() => encode(bin));
    console.log(`${label} encode: len=${len}: ${encodeTime}ms`);
    const decodeTime = run(() => decode(str));
    console.log(`${label} decode: len=${len}: ${decodeTime}ms`);
}

const lens = [8 << 10, 1 << 20, 16 << 20];
for (i in lens) {
    runBench("jsbase64", lens[i],
             Base64.fromUint8Array,
             Base64.toUint8Array);
    runBench("googcrypt", lens[i],
             goog.crypt.base64.encodeByteArray,
             goog.crypt.base64.decodeStringToUint8Array);
}

result:

jsbase64 encode: len=8192: 0.008025978191148172ms
jsbase64 decode: len=8192: 0.08218390804597701ms
googcrypt encode: len=8192: 0.15714285714285714ms
googcrypt decode: len=8192: 0.27424657534246577ms
jsbase64 encode: len=1048576: 0.997029702970297ms
jsbase64 decode: len=1048576: 10.55ms
googcrypt encode: len=1048576: 57.2ms
googcrypt decode: len=1048576: 35.46666666666667ms
jsbase64 encode: len=16777216: 18.333333333333332ms
jsbase64 decode: len=16777216: 178.3ms
googcrypt encode: len=16777216: 966.8ms
googcrypt decode: len=16777216: 581.6ms

@sampajano
Copy link
Collaborator

@yasushi-saito Thanks a lot for discovering the performance bottleneck and doing the benchmark! :)

Although, it's unlikely that we'll adopt a third-party library (i.e. js-base64) for doing encoding/decoding (especially internally).

Maybe you could also consider filing this bug to the closure library, which we (and other dependencies) would benefit from if they can make a performance improvement?

Thanks! :)

@yasushi-saito
Copy link
Author

Makes sense. I created an issue google/closure-library#1161

@sampajano
Copy link
Collaborator

Thanks for filing the bug! I've subscribed also :)

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

No branches or pull requests

2 participants