B64tests
JavaScript performance comparison
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.
| Test | Ops/sec | |
|---|---|---|
|
github
|
|
pending… |
|
500px
|
|
pending… |
|
https://github.com/beatgammit/tar-js/blob/master/lib/utils.js
|
|
pending… |
|
Google Object
|
|
pending… |
|
500px mod x2
|
|
pending… |
|
500px modified
|
|
pending… |
|
500px mod x3
|
|
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.
- Revision 4: published Ian Clelland
- Revision 6: published
0 Comments