Skip to content

Potential performance benefits of using BigInt? #90

@Juraj-Masiar

Description

@Juraj-Masiar

Hello,
I've been using this library for creating custom base93 string, but the performance was pretty bad for my 200KB inputs.
So I've tried to optimize it a bit manually, and then used modern AI to further improve it (actually, completely rewrite it for my usecase).

For inspiration, this is what I'm using now, including performance comparison:

/**
 * This is a replacement for the 'base-x' library.
 * It's been almost fully generated by AI (Sonnet Thinking 3.7).
 * Using BigInt and bitwise operations, we achieved:
 * Firefox encode: 12530ms vs 4278ms =  2.93x
 * Chrome encode:   6649ms vs 2377ms =  2.80x
 * Firefox decode:  8364ms vs 5408ms =  1.55x
 */

export class BaseX {
  readonly #base = BigInt(this.alphabet.length);

  constructor(readonly alphabet: string) {

  }

  encode(buffer: Uint8Array): string {
    if (buffer.length === 0) return '';
    // Convert buffer to BigInt efficiently using bitwise operations
    let value = 0n;
    for (let i = 0; i < buffer.length; i++) {
      value = (value << 8n) | BigInt(buffer[i]);
    }
    // Convert to baseX string efficiently using array
    const digits: string[] = [];
    if (value === 0n) return this.alphabet[0];
    while (value > 0n) {
      const remainder = Number(value % this.#base);
      digits.push(this.alphabet[remainder]);
      value /= this.#base;
    }
    return digits.reverse().join('');
  }

  decode(data: string): Uint8Array {
    if (data.length === 0) return new Uint8Array(0);
    // Convert the baseX string to a BigInt value
    let value = 0n;
    for (let i = 0; i < data.length; i++) {
      const digit = this.alphabet.indexOf(data[i]);
      if (digit === -1) {
        throw new Error(`Invalid character at position ${i}: ${data[i]}`);
      }
      value = value * this.#base + BigInt(digit);
    }
    // If value is 0, return a single-byte buffer with value 0
    if (value === 0n) {
      return new Uint8Array([0]);
    }
    // Determine buffer size efficiently by counting bytes
    let temp = value;
    let byteLength = 0;
    while (temp > 0n) {
      temp >>= 8n;
      byteLength++;
    }
    // Create buffer and fill it from right to left
    const buffer = new Uint8Array(byteLength);
    for (let i = byteLength - 1; i >= 0; i--) {
      buffer[i] = Number(value & 0xffn);
      value >>= 8n;
    }
    return buffer;
  }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions