B64tests

JavaScript performance comparison

Test case created by and last updated

Preparation code


      
      <script>
Benchmark.prototype.setup = function() {
  var buf = new ArrayBuffer(500);
  var data = new Uint8Array(buf);
  for (var i=0; i<500; ++i) data[i] = i % 256;
  
  
  // Method 1: Submitted with pull request
  // Apparent original source: https://gist.github.com/jonleighton/958841
  function base64_1(bytes) {
    var base64    = '';
    var encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
  
    var byteLength    = bytes.byteLength;
    var byteRemainder = byteLength % 3;
    var mainLength    = byteLength - byteRemainder;
  
    var a, b, c, d;
    var chunk;
  
    // Main loop deals with bytes in chunks of 3
    for (var i = 0; i < mainLength; i = i + 3) {
      // Combine the three bytes into a single integer
      chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];
  
      // Use bitmasks to extract 6-bit segments from the triplet
      a = (chunk & 16515072) >> 18; // 16515072 = (2^6 - 1) << 18
      b = (chunk & 258048)   >> 12; // 258048   = (2^6 - 1) << 12
      c = (chunk & 4032)     >>  6; // 4032     = (2^6 - 1) << 6
      d = chunk & 63;               // 63       = 2^6 - 1
  
      // Convert the raw binary segments to the appropriate ASCII encoding
      base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d];
    }
  
    // Deal with the remaining bytes and padding
    if (byteRemainder == 1) {
      chunk = bytes[mainLength];
  
      a = (chunk & 252) >> 2; // 252 = (2^6 - 1) << 2
  
      // Set the 4 least significant bits to zero
      b = (chunk & 3)   << 4; // 3   = 2^2 - 1
  
      base64 += encodings[a] + encodings[b] + '==';
    } else if (byteRemainder == 2) {
      chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1];
  
      a = (chunk & 64512) >> 10; // 64512 = (2^6 - 1) << 10
      b = (chunk & 1008)  >>  4; // 1008  = (2^6 - 1) << 4
  
      // Set the 2 least significant bits to zero
      c = (chunk & 15)    <<  2; // 15    = 2^4 - 1
  
      base64 += encodings[a] + encodings[b] + encodings[c] + '=';
    }
    return base64;
  }
  
  // Method 2: Written for 500px demo
  function base64_2(rawData) {
      var numBytes = rawData.byteLength;
      var b64table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
      var url='';
      var segment;
      for (var i=0;i<numBytes-2;i+=3) {
          segment = (rawData[i] << 16) + (rawData[i+1] << 8) + rawData[i+2];
          url += b64table[segment >> 18] + b64table[(segment & 0x3ffff) >> 12] + b64table[(segment & 0xfff) >> 6] + b64table[segment & 0x3f];
      }
      if (numBytes - i == 2) {
          segment = (rawData[i] << 16) + (rawData[i+1] << 8);
          url += b64table[segment >> 18] + b64table[(segment & 0x3ffff) >> 12] + b64table[(segment & 0xfff) >> 6] + '=';
      } else if (numBytes - i == 1) {
          segment = (rawData[i] << 16);
          url += b64table[segment >> 18] + b64table[(segment & 0x3ffff) >> 12] + '==';
      }
  }
  
  // Method 2: Written for 500px demo
  function base64_2a(rawData) {
      var numBytes = rawData.byteLength;
      var b64table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
      var url=[];
      var segment;
      for (var i=0;i<numBytes-2;i+=3) {
          segment = (rawData[i] << 16) + (rawData[i+1] << 8) + rawData[i+2];
          url.push(b64table[segment >> 18] + b64table[(segment & 0x3ffff) >> 12] + b64table[(segment & 0xfff) >> 6] + b64table[segment & 0x3f]);
      }
      if (numBytes - i == 2) {
          segment = (rawData[i] << 16) + (rawData[i+1] << 8);
          url.push(b64table[segment >> 18] + b64table[(segment & 0x3ffff) >> 12] + b64table[(segment & 0xfff) >> 6] + '=');
      } else if (numBytes - i == 1) {
          segment = (rawData[i] << 16);
          url.push(b64table[segment >> 18] + b64table[(segment & 0x3ffff) >> 12] + '==');
      }
      return url.join("");
  
  }
  
  // Method 2: Written for 500px demo
  function base64_2b(rawData) {
      var numBytes = rawData.byteLength;
      var b64table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
      var url='';
      var segment;
      for (var i=0;i<numBytes-2;i+=3) {
          segment = (rawData[i] << 16) + (rawData[i+1] << 8) + rawData[i+2];
          url += b64table[segment >> 18] + b64table[segment >> 12 & 0x3f] + b64table[segment >> 6 & 0x3f] + b64table[segment & 0x3f];
      }
      if (numBytes - i == 2) {
          segment = (rawData[i] << 16) + (rawData[i+1] << 8);
          url += b64table[segment >> 18] + b64table[segment >> 12 & 0x3f] + b64table[segment >> 6 & 0x3f] + '=';
      } else if (numBytes - i == 1) {
          segment = (rawData[i] << 16);
          url += b64table[segment >> 18] + b64table[segment >> 12 & 0x3f] + '==';
      }
  }
  
  // Method 3: Found through Google
  // Source: https://github.com/beatgammit/tar-js/blob/master/lib/utils.js
  var lookup = [
      'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
      'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
      'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
      'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
      'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
      'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
      'w', 'x', 'y', 'z', '0', '1', '2', '3',
      '4', '5', '6', '7', '8', '9', '+', '/'
  ];
  
  function clean(length) {
      var i, buffer = new Uint8Array(length);
      for (i = 0; i < length; i += 1) {
          buffer[i] = 0;
      }
      return buffer;
  }
  
  function extend(orig, length, addLength, multipleOf) {
      var newSize = length + addLength,
          buffer = clean((parseInt(newSize / multipleOf) + 1) * multipleOf);
  
      buffer.set(orig);
  
      return buffer;
  }
  
  function pad(num, bytes, base) {
      num = num.toString(base || 8);
      return "000000000000".substr(num.length + 12 - bytes) + num;
  }	
  
  function stringToUint8 (input, out, offset) {
      var i, length;
  
      out = out || clean(input.length);
  
      offset = offset || 0;
      for (i = 0, length = input.length; i < length; i += 1) {
          out[offset] = input.charCodeAt(i);
          offset += 1;
      }
  
      return out;
  }
  
  function base64_3(uint8) {
  
      var i,
          extraBytes = uint8.length % 3, // if we have 1 byte left, pad 2 bytes
          output = "",
          temp, length;
  
      function tripletToBase64 (num) {
          return lookup[num >> 18 & 0x3F] + lookup[num >> 12 & 0x3F] + lookup[num >> 6 & 0x3F] + lookup[num & 0x3F];
      };
  
      // go through the array every three bytes, we'll deal with trailing stuff later
      for (i = 0, length = uint8.length - extraBytes; i < length; i += 3) {
          temp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2]);
          output += tripletToBase64(temp);
      }
  
      // this prevents an ERR_INVALID_URL in Chrome (Firefox okay)
      switch (output.length % 4) {
          case 1:
              output += '=';
              break;
          case 2:
              output += '==';
              break;
          default:
              break;
      }
      return output;
  }
  
  
  // Method 4: Google closure library version
  // Source: http://docs.closure-library.googlecode.com/git/closure_goog_crypt_base64.js.html
  // Simplified for runtime performance and flat namespace
  var ENCODED_VALS = 
      'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
      'abcdefghijklmnopqrstuvwxyz' +
      '0123456789=';
  var byteToCharMap_ = {};
  for (var i = 0; i < ENCODED_VALS.length; i++) {
        byteToCharMap_[i] = ENCODED_VALS.charAt(i);
  }
  
  function base64_4(input) {
    var byteToCharMap = byteToCharMap_;
  
    var output = [];
  
    for (var i = 0; i < input.length; i += 3) {
      var byte1 = input[i];
      var haveByte2 = i + 1 < input.length;
      var byte2 = haveByte2 ? input[i + 1] : 0;
      var haveByte3 = i + 2 < input.length;
      var byte3 = haveByte3 ? input[i + 2] : 0;
  
      var outByte1 = byte1 >> 2;
      var outByte2 = ((byte1 & 0x03) << 4) | (byte2 >> 4);
      var outByte3 = ((byte2 & 0x0F) << 2) | (byte3 >> 6);
      var outByte4 = byte3 & 0x3F;
  
      if (!haveByte3) {
        outByte4 = 64;
  
        if (!haveByte2) {
          outByte3 = 64;
        }
      }
  
      output.push(byteToCharMap[outByte1],
                  byteToCharMap[outByte2],
                  byteToCharMap[outByte3],
                  byteToCharMap[outByte4]);
    }
  
    return output.join('');
  };
  
  var byteToCharArray_ = lookup;
  byteToCharArray_.push('=');
  
  // Google closure library version, modified to use array rather than object
  function base64_5(input) {
    var byteToCharMap = byteToCharArray_;
  var byte1, byte2, byte3;
  var outByte1, outByte2, outByte3, outByte4;
  
    var output = [];
  
    var len = input.length - (input.length % 3)
    for (var i = 0; i < len; i += 3) {
      byte1 = input[i];
      byte2 = input[i + 1];
      byte3 = input[i + 2];
  
      outByte1 = byte1 >> 2;
      outByte2 = ((byte1 & 0x03) << 4) | (byte2 >> 4);
      outByte3 = ((byte2 & 0x0F) << 2) | (byte3 >> 6);
      outByte4 = byte3 & 0x3F;
  
      output.push(byteToCharMap[outByte1],
                  byteToCharMap[outByte2],
                  byteToCharMap[outByte3],
                  byteToCharMap[outByte4]);
    }
    if (i < input.length) {
      byte1 = input[i];
      var haveByte2 = i + 1 < input.length;
      byte2 = haveByte2 ? input[i + 1] : 0;
      var haveByte3 = i + 2 < input.length;
      byte3 = haveByte3 ? input[i + 2] : 0;
  
      outByte1 = byte1 >> 2;
      outByte2 = ((byte1 & 0x03) << 4) | (byte2 >> 4);
      outByte3 = ((byte2 & 0x0F) << 2) | (byte3 >> 6);
      outByte4 = byte3 & 0x3F;
  
      if (!haveByte3) {
        outByte4 = 64;
  
        if (!haveByte2) {
          outByte3 = 64;
        }
      }
  
      output.push(byteToCharMap[outByte1],
                  byteToCharMap[outByte2],
                  byteToCharMap[outByte3],
                  byteToCharMap[outByte4]);
    }
  
    return output.join('');
  };
  
  // Method 6: Based on 500px demo; using 12-bit array indices for speed
  var b64table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  var b64product = {};
  var b64productstr = '';
  for (var i=0; i<64; i++) {
    for (var j=0; j<64; j++) {
      b64product[i*64+j] = b64table[i] + b64table[j];
      b64productstr += b64table[i] + b64table[j];
    }
  }
  
  function base64_6(rawData) {
      var numBytes = rawData.byteLength;
      var url="";
      var segment;
      for (var i=0;i<numBytes-2;i+=3) {
          segment = (rawData[i] << 16) + (rawData[i+1] << 8) + rawData[i+2];
          var i1 = segment >> 12, i2 = segment & 0xfff;
          url += b64product[i1] + b64product[i2];
      }
      if (numBytes - i == 2) {
          segment = (rawData[i] << 16) + (rawData[i+1] << 8);
          url += b64table[segment >> 18] + b64table[(segment & 0x3ffff) >> 12] + b64table[(segment & 0xfff) >> 6] + '=';
      } else if (numBytes - i == 1) {
          segment = (rawData[i] << 16);
          url += b64table[segment >> 18] + b64table[(segment & 0x3ffff) >> 12] + '==';
      }
  }
  
  function base64_7(rawData) {
      var numBytes = rawData.byteLength;
      var url="";
      var segment;
      var tbl = b64product;
      for (var i=0;i<numBytes-2;i+=3) {
          segment = (rawData[i] << 16) + (rawData[i+1] << 8) + rawData[i+2];
          url += tbl[segment >> 12] + tbl[segment & 0xfff];
      }
      if (numBytes - i == 2) {
          segment = (rawData[i] << 16) + (rawData[i+1] << 8);
          url += b64table[segment >> 18] + b64table[(segment & 0x3ffff) >> 12] + b64table[(segment & 0xfff) >> 6] + '=';
      } else if (numBytes - i == 1) {
          segment = (rawData[i] << 16);
          url += b64table[segment >> 18] + b64table[(segment & 0x3ffff) >> 12] + '==';
      }
  }
  
  function base64_8(rawData) {
      var numBytes = rawData.byteLength;
      var url="";
      var segment;
      for (var i=0;i<numBytes-2;i+=3) {
          segment = (rawData[i] << 16) + (rawData[i+1] << 8) + rawData[i+2];
          var i1 = segment >> 12 * 2, i2 = segment & 0xfff * 2;
          url += b64productstr[i1] + b64productstr[i1+1] + b64productstr[i2] + b64productstr[i2+1];
      }
      if (numBytes - i == 2) {
          segment = (rawData[i] << 16) + (rawData[i+1] << 8);
          url += b64table[segment >> 18] + b64table[(segment & 0x3ffff) >> 12] + b64table[(segment & 0xfff) >> 6] + '=';
      } else if (numBytes - i == 1) {
          segment = (rawData[i] << 16);
          url += b64table[segment >> 18] + b64table[(segment & 0x3ffff) >> 12] + '==';
      }
  }
  

};
</script>

Test runner

Warning! For accurate results, please disable Firebug before running the tests. (Why?)

Java applet disabled.

Testing in CCBot 2.0.0 / Other 0.0.0
Test Ops/sec
github
base64_1(data);
pending…
500px
base64_2(data);
pending…
https://github.com/beatgammit/tar-js/blob/master/lib/utils.js
base64_3(data);
pending…
Google Object
base64_2a(data)
pending…
500px mod x2
base64_7(data);
pending…
500px modified
base64_6(data);
pending…
500px mod x3
base64_8(data)
pending…

Compare results of other browsers

Revisions

You can edit these tests or add even more tests to this page by appending /edit to the URL.

0 Comments