Backbone associations speed suite

JavaScript performance comparison

Revision 14 of this test case created

Info

Backbone-associations

This test ups the complexity of the object graph. Imagine a matrix of "experiments", testing 25 "chemicals" against 5 "substrates"; each experiment has many "trials". The company model has a list of chemicals, substrates, and experiments that need to be run. Each employee in the company runs some number (4) of the experiments, doing some number (3) of trials, and updating each trial some number (3) times.

Preparation code

<script src="//ajax.googleapis.com/ajax/libs/prototype/1/prototype.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js">
</script>
<script src="//underscorejs.org/underscore.js">
</script>
<script src="//backbonejs.org/backbone.js">
</script>
<script src="//dhruvaray.github.com/backbone-associations/backbone-associations.js">
</script>
<script src="//pauluithol.github.io/Backbone-relational/backbone-relational.js">
</script>
<script>
Benchmark.prototype.setup = function() {
    var _getDepartments = function() {
      var loc1 = {
        add1: "P.O Box 3899",
        zip: "94404",
        state: "CA"
      };
      var loc2 = {
        add1: "P.O Box 4899",
        zip: "95502",
        state: "CA"
      };
      return [{
        name: 'R&D',
        number: '23',
        locations: [
          loc1,
          loc2
        ]
      }, {
        name: 'Marketing',
        number: '24',
        locations: [
          loc1,
          loc2
        ]
      }];
    };
   
   
    var getEmployees = function(count) {
      !count && (count = 10);
      var result = {
        name: 'comp' + count,
        employees: []
      };
      var departmentData = _getDepartments();
      for (var i = 0; i < count; i++) {
        var emp = {
          fname: 'fname' + i,
          lname: 'lname' + i,
          age: (function() {
            var age = parseInt(Math.random() * 100);
            return age < 20 ? age + 20 : age > 65 ? 65 : age;
          })(),
          sex: (function() {
            return (parseInt(Math.random() * 100) % 2) ? 'M' : 'F';
          })(),
          works_for: (function() {
            return departmentData[(parseInt(Math.random() * 100) % 2)];
          })()
        };
        result.employees.push(emp);
      }
      return result;
    };
   
    var getExperimentalSetup = function(substrateCount, chemicalCount) {
      var substrates = [],
        chemicals = [];
      for (var i = 0; i < substrateCount; i++) {
        substrates.push({
          id: i,
          name: "substrate" + i
        });
      }
      for (var i = 0; i < chemicalCount; i++) {
        chemicals.push({
          id: i,
          name: "chemical" + i
        });
      }
      return {
        substrates: substrates,
        chemicals: chemicals
      }
    };
   
    // Associated Model  
    var associatedModel = {};
   
   
    associatedModel.Location = Backbone.AssociatedModel.extend({
      defaults: {
        add1: "",
        add2: null,
        zip: "",
        state: ""
      }
    });
   
    associatedModel.Department = Backbone.AssociatedModel.extend({
      relations: [{
        type: Backbone.Many,
        key: 'locations',
        relatedModel: associatedModel.Location
      }],
      defaults: {
        name: '',
        locations: [],
        number: -1
      }
    });
   
    associatedModel.Employee = Backbone.AssociatedModel.extend({
      relations: [{
        type: Backbone.One,
        key: 'works_for',
        relatedModel: associatedModel.Department
      }],
      validate: function(attr) {
        return (attr.sex && attr.sex != "M" && attr.sex != "F") ? "invalid sex value" : undefined;
      },
      defaults: {
        sex: 'M',
        //{F,M}
        age: 0,
        fname: "",
        lname: "",
        works_for: {}
      }
    });
   
    associatedModel.Trial = Backbone.AssociatedModel.extend({
      relations: [{
        type: Backbone.One,
        key: 'employee',
        relatedModel: associatedModel.Employee
      }],
      defaults: {
        name: '',
        employee: {}
      }
    });
   
    associatedModel.Chemical = Backbone.AssociatedModel.extend({
      defaults: {
        name: '',
        experiments: []
      }
    });
   
    associatedModel.Substrate = Backbone.AssociatedModel.extend({
      defaults: {
        name: '',
        experiments: []
      }
    });
   
    associatedModel.Experiment = Backbone.AssociatedModel.extend({
      relations: [{
        type: Backbone.Many,
        key: 'trials',
        relatedModel: associatedModel.Trial
      }, {
        type: Backbone.One,
        key: 'chemical',
        relatedModel: associatedModel.Chemical
      }, {
        type: Backbone.One,
        key: 'substrate',
        relatedModel: associatedModel.Substrate
      }],
      defaults: {
        trials: [],
        chemical: {},
        substrate: {}
      }
    });
   
    associatedModel.Company = Backbone.AssociatedModel.extend({
      relations: [{
        type: Backbone.Many,
        key: 'employees',
        relatedModel: associatedModel.Employee
      }, {
        type: Backbone.Many,
        key: 'chemicals',
        relatedModel: associatedModel.Chemical
      }, {
        type: Backbone.Many,
        key: 'substrates',
        relatedModel: associatedModel.Substrate
      }, {
        type: Backbone.Many,
        key: 'experiments',
        relatedModel: associatedModel.Experiment
      }],
      defaults: {
        name: '',
        employees: [],
        chemicals: [],
        substrates: [],
        experiments: []
      }
    });
   
    // Relational Model    
    var relationalModel = {};
    relationalModel.Location = Backbone.RelationalModel.extend({
      defaults: {
        add1: "",
        add2: null,
        zip: "",
        state: ""
      }
    });
   
    relationalModel.Department = Backbone.RelationalModel.extend({
      relations: [{
        type: Backbone.HasMany,
        key: 'locations',
        relatedModel: relationalModel.Location
      }],
      defaults: {
        name: '',
        locations: [],
        number: -1
      }
    });
   
    relationalModel.Employee = Backbone.RelationalModel.extend({
      relations: [{
        type: Backbone.HasOne,
        key: 'works_for',
        relatedModel: relationalModel.Department
      }],
      validate: function(attr) {
        return (attr.sex && attr.sex != "M" && attr.sex != "F") ? "invalid sex value" : undefined;
      },
      defaults: {
        sex: 'M',
        //{F,M}
        age: 0,
        fname: "",
        lname: "",
        works_for: {}
      }
    });
   
    relationalModel.Trial = Backbone.RelationalModel.extend({
      relations: [{
        type: Backbone.HasOne,
        key: 'employee',
        relatedModel: relationalModel.Employee
      }],
      defaults: {
        name: '',
        employee: {}
      }
    });
   
    relationalModel.Chemical = Backbone.RelationalModel.extend({
      defaults: {
        name: '',
        experiments: []
      }
    });
   
    relationalModel.Substrate = Backbone.RelationalModel.extend({
      defaults: {
        name: '',
        experiments: []
      }
    });
   
    relationalModel.Experiment = Backbone.RelationalModel.extend({
      relations: [{
        type: Backbone.HasMany,
        key: 'trials',
        relatedModel: relationalModel.Trial
      }, {
        type: Backbone.HasOne,
        key: 'chemical',
        relatedModel: relationalModel.Chemical
      }, {
        type: Backbone.HasOne,
        key: 'substrate',
        relatedModel: relationalModel.Substrate
      }],
      defaults: {
        name: '',
        trials: [],
        chemical: {},
        substrate: {}
      }
    });
   
    relationalModel.Company = Backbone.RelationalModel.extend({
      relations: [{
        type: Backbone.HasMany,
        key: 'employees',
        relatedModel: relationalModel.Employee
      }, {
        type: Backbone.HasMany,
        key: 'chemicals',
        relatedModel: relationalModel.Chemical
      }, {
        type: Backbone.HasMany,
        key: 'substrates',
        relatedModel: relationalModel.Substrate
      }, {
        type: Backbone.HasMany,
        key: 'experiments',
        relatedModel: relationalModel.Experiment
      }],
      defaults: {
        name: '',
        employees: [],
        chemicals: [],
        substrates: [],
        experiments: []
      }
    });
   
    var employeeCollection = getEmployees(100);
    var experimentalSetup = getExperimentalSetup(5, 25);
   
    // construstruction
   
    var buildExperiments = function(company) {
      n = 0
      company.get('chemicals').each(function(chemical) {
        company.get('substrates').each(function(substrate) {
          company.get('experiments').add({
            number: n++,
            chemical: chemical,
            substrate: substrate,
          });
        });
      });
    };
   
    var runExperiments = function(company, nExperiments, nTrials, nData) {
      company.get('employees').each(function(employee) {
        experiment = company.get('experiments').at(parseInt(Math.random() * company.get('experiments').length));
        for (var i = 0; i < nTrials; i++) {
          experiment.get('trials').add({
            name: employee.get('name') + i,
            employee: employee,
            tick: 0
          });
          var trial = experiment.get('trials').last()
          for (var d = 0; d < nData; d++) {
            trial.set('tick', trial.get('tick') + 1);
          }
        }
      });
    }
};
</script>

Preparation code output

Test runner

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

Java applet disabled.

Testing in unknown unknown
Test Ops/sec
backbone-associations
// async test
var company = new associatedModel.Company();
company.set(employeeCollection);
company.set(experimentalSetup);
buildExperiments(company);
runExperiments(company, 4, 3, 3);
pending…
backbone-relational
var company = new relationalModel.Company();
company.set(employeeCollection);
company.set(experimentalSetup);
buildExperiments(company);
runExperiments(company, 4, 3, 3);
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