JSON Parse vs Base64 to ArrayBuffer

JavaScript performance comparison

Revision 3 of this test case created

Info

A comparison of JSON.Parse decoding into an array of floats versus JavaScript-based Base64 decoding directly into an array buffer.

Preparation code

 
<script>
Benchmark.prototype.setup = function() {
    THREE = function() {};
   
    /**
     * @author bhouston / http://exocortex.com/
     * Original source from: 2013, April 22: https://github.com/niklasvh/base64-arraybuffer (MIT-LICENSED)
     */

   
    THREE.Base64 = function () {
    };
   
    THREE.Base64.base64String = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
   
    THREE.Base64.base64DecodeLookup = function() {
   
      var coreArrayBuffer = new ArrayBuffer( 256 );
      var base64DecodeLookupTable = new Uint8Array( coreArrayBuffer );
      for( var i = 0; i < THREE.Base64.base64String.length; i ++ ) {
        base64DecodeLookupTable[ THREE.Base64.base64String[ i ].charCodeAt( 0 ) ] = i;
      }
   
      return base64DecodeLookupTable;
   
    }();
   
    THREE.Base64.fromArrayBuffer = function (arraybuffer) {
      var bytes = new Uint8Array(arraybuffer),
        i, len = bytes.buffer.byteLength, base64 = "";
   
      for (i = 0; i < len; i+=3) {
        base64 += THREE.Base64.base64String[bytes[i] >> 2];
        base64 += THREE.Base64.base64String[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)];
        base64 += THREE.Base64.base64String[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)];
        base64 += THREE.Base64.base64String[bytes[i + 2] & 63];
      }
   
      if ((len % 3) === 2) {
        base64 = base64.substring(0, base64.length - 1) + "=";
      } else if (len % 3 === 1) {
        base64 = base64.substring(0, base64.length - 2) + "==";
      }
   
      return base64;
    };
   
    THREE.Base64.toArrayBuffer = function() {
   
      var base64DecodeLookup = THREE.Base64.base64DecodeLookup;
   
      return function(base64) {
   
        var bufferLength = base64.length * 0.75,
          len = base64.length, i, p = 0,
          encoded1, encoded2, encoded3, encoded4;
   
        if (base64[base64.length - 1] === "=") {
          bufferLength--;
          if (base64[base64.length - 2] === "=") {
            bufferLength--;
          }
        }
   
        var arraybuffer = new ArrayBuffer(bufferLength),
        bytes = new Uint8Array(arraybuffer);
   
        for (i = 0; i < len; i+=4) {
          encoded1 = base64DecodeLookup[base64.charCodeAt(i)];
          encoded2 = base64DecodeLookup[base64.charCodeAt(i+1)];
          encoded3 = base64DecodeLookup[base64.charCodeAt(i+2)];
          encoded4 = base64DecodeLookup[base64.charCodeAt(i+3)];
   
          bytes[p++] = (encoded1 << 2) | (encoded2 >> 4);
          bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2);
          bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63);
        }
   
        return arraybuffer;
      };
   
    }();
   
    THREE.Base64.toArrayOfFloats = function( base64 ) {
   
      var arrayBuffer = THREE.Base64.toArrayBuffer( base64 );
      var floatArray = new Float32Array( arrayBuffer );
   
      var arrayOfFloats = [];
      for( var i = 0, il = floatArray.length; i < il; i ++ ) {
        arrayOfFloats.push( floatArray[i] );
      }  
   
      return arrayOfFloats;
   
    };
   
   
   
   
    /*
    Copyright Vassilis Petroulias [DRDigit]
   
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at
   
           http://www.apache.org/licenses/LICENSE-2.0
   
    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
    */

    var B64 = {
        alphabet: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=',
        lookup: null,
        ie: /MSIE /.test(navigator.userAgent),
        ieo: /MSIE [67]/.test(navigator.userAgent),
        encode: function (s) {
            var buffer = B64.toUtf8(s),
                position = -1,
                len = buffer.length,
                nan1, nan2, enc = [, , , ];
            if (B64.ie) {
                var result = [];
                while (++position < len) {
                    nan1 = buffer[position + 1], nan2 = buffer[position + 2];
                    enc[0] = buffer[position] >> 2;
                    enc[1] = ((buffer[position] & 3) << 4) | (buffer[++position] >> 4);
                    if (isNaN(nan1)) enc[2] = enc[3] = 64;
                    else {
                        enc[2] = ((buffer[position] & 15) << 2) | (buffer[++position] >> 6);
                        enc[3] = (isNaN(nan2)) ? 64 : buffer[position] & 63;
                    }
                    result.push(B64.alphabet[enc[0]], B64.alphabet[enc[1]], B64.alphabet[enc[2]], B64.alphabet[enc[3]]);
                }
                return result.join('');
            } else {
                result = '';
                while (++position < len) {
                    nan1 = buffer[position + 1], nan2 = buffer[position + 2];
                    enc[0] = buffer[position] >> 2;
                    enc[1] = ((buffer[position] & 3) << 4) | (buffer[++position] >> 4);
                    if (isNaN(nan1)) enc[2] = enc[3] = 64;
                    else {
                        enc[2] = ((buffer[position] & 15) << 2) | (buffer[++position] >> 6);
                        enc[3] = (isNaN(nan2)) ? 64 : buffer[position] & 63;
                    }
                    result += B64.alphabet[enc[0]] + B64.alphabet[enc[1]] + B64.alphabet[enc[2]] + B64.alphabet[enc[3]];
                }
                return result;
            }
        },
        decode: function (s) {
            var buffer = B64.fromUtf8(s),
                position = 0,
                len = buffer.length;
            if (B64.ieo) {
                result = [];
                while (position < len) {
                    if (buffer[position] < 128) result.push(String.fromCharCode(buffer[position++]));
                    else if (buffer[position] > 191 && buffer[position] < 224) result.push(String.fromCharCode(((buffer[position++] & 31) << 6) | (buffer[position++] & 63)));
                    else result.push(String.fromCharCode(((buffer[position++] & 15) << 12) | ((buffer[position++] & 63) << 6) | (buffer[position++] & 63)));
                }
                return result.join('');
            } else {
                result = '';
                while (position < len) {
                    if (buffer[position] < 128) result += String.fromCharCode(buffer[position++]);
                    else if (buffer[position] > 191 && buffer[position] < 224) result += String.fromCharCode(((buffer[position++] & 31) << 6) | (buffer[position++] & 63));
                    else result += String.fromCharCode(((buffer[position++] & 15) << 12) | ((buffer[position++] & 63) << 6) | (buffer[position++] & 63));
                }
                return result;
            }
        },
        toUtf8: function (s) {
            var position = -1,
                len = s.length,
                chr, buffer = [];
            if (/^[\x00-\x7f]*$/.test(s)) while (++position < len)
            buffer.push(s.charCodeAt(position));
            else while (++position < len) {
                chr = s.charCodeAt(position);
                if (chr < 128) buffer.push(chr);
                else if (chr < 2048) buffer.push((chr >> 6) | 192, (chr & 63) | 128);
                else buffer.push((chr >> 12) | 224, ((chr >> 6) & 63) | 128, (chr & 63) | 128);
            }
            return buffer;
        },
        fromUtf8: function (s) {
            var position = -1,
                len, buffer = [],
                enc = [, , , ];
            if (!B64.lookup) {
                len = B64.alphabet.length;
                B64.lookup = {};
                while (++position < len)
                B64.lookup[B64.alphabet[position]] = position;
                position = -1;
            }
            len = s.length;
            while (position < len) {
                enc[0] = B64.lookup[s.charAt(++position)];
                enc[1] = B64.lookup[s.charAt(++position)];
                buffer.push((enc[0] << 2) | (enc[1] >> 4));
                enc[2] = B64.lookup[s.charAt(++position)];
                if (enc[2] == 64) break;
                buffer.push(((enc[1] & 15) << 4) | (enc[2] >> 2));
                enc[3] = B64.lookup[s.charAt(++position)];
                if (enc[3] == 64) break;
                buffer.push(((enc[2] & 3) << 6) | enc[3]);
            }
            return buffer;
        }
    };
   
   
   
   
    var array = []
    for (var i = 0; i < 10000; i++) {
      array.push(i * 0.8734 - 5000);
    }
   
    var arrayBuffer = new ArrayBuffer(array.length * 4);
    var floatBuffer = new Float32Array(arrayBuffer);
    for (var i = 0; i < array.length; i++) {
      floatBuffer[i] = array[i];
    }
   
    var bufferInBase64 = THREE.Base64.fromArrayBuffer(arrayBuffer);
    var bufferInJSON = JSON.stringify(array);
};
</script>

Test runner

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

Java applet disabled.

Testing in unknown unknown
Test Ops/sec
JSON to array of floats
var fromJson = JSON.parse(bufferInJSON);
pending…
Base64 to arraybuffer
var fromBase64 = THREE.Base64.toArrayBuffer(bufferInBase64);
pending…
Base64 to array of floats
var fromBase64 = THREE.Base64.toArrayOfFloats(bufferInBase64);
pending…
var fromBase64 = B64.decode(bufferInBase64);
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. Here’s a list of current revisions for this page:

0 comments

Add a comment