String builder benchmark test

From Trephine

Jump to: navigation, search

[subscribe] Recent blog entries

Live Demos

String builder benchmark test

A simple suite to benchmark the five implementations of buildList() in Efficient JavaScript string building.

Results are discussed in the article JavaScript string building benchmarks.

You must have JavaScript enabled to run this benchmark.

// List of the 5 implementations
var implementations = [
  function buildList( /* list1, list2, ... */ ) {
    var buf = "<ul>\n";                        // initialize buffer
    for (var i=0; i<arguments.length; i++) {   // iterate over arguments
      var list = arguments[i];                 // grab next list
      for (var j=0; j<list.length; j++) {      // iterate over list
        buf += "<li>";
        buf += list[j];                        // append item to buffer
        buf += "</li>\n";
      }
    }
    buf += "</ul>"                             // finalize buffer
    return buf;
  },
  function buildList( /* list1, list2, ... */ ) {
    var buf = [ "<ul>\n" ];                      // initialize array buffer
    for (var i=0; i<arguments.length; i++) {
      var list = arguments[i];
      for (var j=0; j<list.length; j++) {
        buf.push( "<li>", list[j], "</li>\n" );  // append item to buffer
      }
    }
    buf.push( "</ul>" );                         // finalize array buffer
    return buf.join('');                         // concatenate and return
  },
  function buildList( /* list1, list2, ... */ ) {
    var buf = [ "<ul>\n" ];
    for (var i=0; i<arguments.length; i++) {
      var list = arguments[i];
      for (var j=0; j<list.length; j++) {
        buf[buf.length] = "<li>";          // append <li> tags and
        buf[buf.length] = list[j];         // item to array buffer, utilizing
        buf[buf.length] = "</li>\n";       // primitive array assignment
      }
    }
    buf[buf.length] = "</ul>";
    return buf.join('');
  },
  function buildList( /* list1, list2, ... */ ) {
    var buf = [];
    for (var i=0; i<arguments.length; i++) {
      var list = arguments[i];
      buf[buf.length] = list.join( "</li>\n<li>" );  // join list, append to buf
    }
    if (!buf.length) return "<ul>\n</ul>";           // short-circuit if no items
    var last = buf.length-1;
    buf[0] = "<ul>\n<li>" + buf[0];                  // add opening <ul> prefix
    buf[last] = buf[last] + "</li>\n</ul>";          // add trailing </ul> suffix
    return buf.join( "</li>\n<li>" );                // concatenate and return
  },
  function buildList( /* list1, list2, ... */ ) {
    var size = 0;                                  // initalize size counter
    for (var i=0, l=arguments.length; i<l; i++) {  // iterate over arguments
      size += arguments[i].length;                 // add up all lists lengths
    }
    if (size==0) return "<ul></ul>";               // short-circuit if zero items
    var buf = new Array( size ), pos = 0;          // initialize buffer and pos
    for (var i=0, l=arguments.length; i<l; i++) {  // iterate over arguments
      var list = arguments[i];                     // grab the next list
      for (var j=0, k=list.length; j<k; j++) {     // iterate over list
        buf[pos++] = list[j];                      // set buffer position to item
      }
    }
    buf[0] = "<ul>\n<li>" + buf[0];                // add opening <ul> prefix
    buf[size-1] += "</li>\n</ul>";                 // add trailing </ul> suffix
    return buf.join("</li>\n<li>");                // concatenate and return
  }
];
 
// Short-hand names of implementations
var names = [
  'string buffer',
  'push array buffer',
  'native assignment',
  'direct join',
  'pre-count buffer'
];
 
// Build data sets
var data = {
  small: null,   // 128x128
  medium: null,  // 256x256
  large: null    // 512x512
}, p = 7;
for (var k in data) {
  var size = Math.pow( 2, p++ );
  var list = new Array( size ), args = new Array( size );
  for (var i=0; i<size; i++) list[i] = i;
  for (var i=0; i<size; i++) args[i] = list.slice(0);
  data[k] = args;
}
 
// Utility functions for setting status and adding data row
var status = function(s) {
  document.getElementById('status').innerHTML = s;
    "<div class=\"notice\">" + s + "</div>";
};
var addRow = function(row) {
  var results = document.getElementById('results');
  var tbody = results.getElementsByTagName('tbody')[0];
  var tr = document.createElement('tr');
  for (var i=0, l=row.length; i<l; i++) {
    var td = document.createElement('td');
    td.appendChild(document.createTextNode(row[i]));
    tr.appendChild(td);
  }
  tbody.appendChild(tr);
};
 
// Create task queue
var tasks = [], iterations = 5;
for (var i=0, l=implementations.length; i<l; i++) {
  (function( i, buildList, stat ){
    for (var k in data) (function(k){
      tasks.push( function(){
        status(
          "<div class=\"notice\"><span class=\"spinner\"></span> " +
          "Running implementation " + ( i + 1 ) + 
          " against " + k + " data set...</div>"
        );
        var start = new Date();
        for (var m=0; m<iterations; m++) buildList.apply( null, data[k] );
        var end = new Date();
        stat[k] = ( end.valueOf() - start.valueOf() ) / iterations;
      } );
    })(k);
    tasks.push( function(){
      var label = ( i + 1 ) + ". " + names[i];
      addRow( [ label, stat.small, stat.medium, stat.large ] );
    } );
  })( i, implementations[i], {} );
}
tasks.push( function(){
  status( "<div class=\"success\">Benchmarks completed successfully</div>" );
} );
 
// Execute tasks sequentially
var index = 0, chain = function(){
  tasks[index++]();
  if (index<tasks.length) setTimeout( chain, 100 );
};
chain();
<table class="wikitable" id="results">
  <thead>
    <th>implementation</th>
    <th>small (ms)</th>
    <th>medium (ms)</th>
    <th>large (ms)</th>
  </thead>
  <tbody></tbody>
</table>
<div id="status"></div>
.demotab .spinner {
  padding: 10px 10px;
  background: url("skins/common/images/spinner.gif");
  background-position: center center;
  background-repeat: no-repeat;
}
.demotab td {
  border-bottom: 1px solid #ccc;
}

Public domain declaration

Just so there's no confusion: all of the code snippets on this page are provided "AS IS", without warranty of any kind, express or implied.

All of the code snippets on this page are hereby released into the public domain by the me, the copyright holder. This applies worldwide. Or in case this is not legally possible: The copyright holder grants any entity the right to use this work for any purpose, without any conditions, unless such conditions are required by law.

If you'd feel better with a "real" license, you're free to use code snippets on this page under the MIT license as described on the about page.

Any links back to this site are always appreciated, but not required. Enjoy!