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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions