Marko Taht
Marko Taht

Reputation: 1538

Fastest way to convert BigInt to Uint8Array?

Situation:

A JSON object that has an array of ~3000 elements. Each element is in format

base64String.base64String.base64String.base64String....

Somewhere around 20-30 base64String separated with ..

These base64String contain lot of information. And values. values are summed together into bigInt. And then the bigInt is converted to byte to be used in hashing and what not.

This entire process needs to be as fast as possible, since this is a small fraction of the complete chain of processing, and everything needs to be fast. The bigInts values are limited to 64bits (Java Long).

I have tested 3 options:

function bigIntToBigEndianBytesLoop(value) {
    const hex = value.toString(16).padStart(8 * 2, '0');
    const bytes = new Uint8Array(8);
    for (let i = 0; i < 8; i++) {
        bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
    }
    return bytes;
}

function bigIntToBigEndianBytesDataView(value) {
    const buf = new ArrayBuffer(8);
    const view = new DataView(buf);

    view.setBigUint64(0, value);
    return new Uint8Array(buf);
}

function bigIntToUint8ArrayDirect(num) {
    const result = new Uint8Array(8);
    result[7] = Number(num & 0xffn);
    result[6] = Number((num >> 8n) & 0xffn);
    result[5] = Number((num >> 16n) & 0xffn);
    result[4] = Number((num >> 24n) & 0xffn);
    result[3] = Number((num >> 32n) & 0xffn);
    result[2] = Number((num >> 40n) & 0xffn);
    result[1] = Number((num >> 48n) & 0xffn);
    result[0] = Number((num >> 56n) & 0xffn);
    return result;
}

So far it seems tht direct approach is the fastest. https://www.measurethat.net/Benchmarks/Show/33000/3/bigint-to-bigendian-bytes Is there any other methods to use?

Upvotes: 0

Views: 69

Answers (1)

trincot
trincot

Reputation: 351019

You could first split the BigInt into two numbers (32 bits), and then continue with those two numbers. This reduces the number of operations on bigints and calls of Number. If the bigint is guaranteed to be in the range of an unsigned 64 bit integer, you can also save on the second & operation. Using >>> you can similarly skip one & operation for each of these two numbers:

// Direct Constructor with Split
function bigIntToUint8ArrayDirectConstructor2(value) {
    const result = new Uint8Array(8);
    const low = Number(value & 0xffffffffn);
    const high = Number(value >> 32n); 
    result[7] = low & 0xff;
    result[6] = (low >>> 8) & 0xff;
    result[5] = (low >>> 16) & 0xff;
    result[4] = low >>> 24;
    result[3] = high & 0xff;
    result[2] = (high >>> 8) & 0xff;
    result[1] = (high >>> 16) & 0xff;
    result[0] = high >>> 24;
    return result;
}

This is what I got:

results of benchmark run

Benchmark with this variant added as "Direct Constructor with split"

Upvotes: 1

Related Questions