Browse Source

add benchmarks

Evan You 10 years ago
parent
commit
71ca7b7829

+ 15 - 0
benchmarks/big-table/demo.css

@@ -0,0 +1,15 @@
+form {
+  margin-bottom: 15px ;
+}
+
+td.hidden {
+  color: #ccc;
+}
+
+table.filtered td.item {
+  background-color: #FFFFBF;
+}
+
+table.filtered td.item.hidden {
+  background-color: transparent;
+}

+ 125 - 0
benchmarks/big-table/index.html

@@ -0,0 +1,125 @@
+<script src="../../dist/vue.js"></script>
+<link rel="stylesheet" href="style.css">
+<link rel="stylesheet" href="demo.css">
+
+<div id="el">
+  <h1>
+    Rendering Large Datasets With Vue {{renderTime}}
+  </h1>
+
+  <form>
+    <strong>Filter Data</strong>:
+    <input type="text" v-model="filter">
+
+    <!--
+      If the user is filtering the data, we want to offer some insight into
+      the breadth of the filtering.
+    -->
+    <span v-if="filter">
+      &mdash;
+      Filtering <strong>{{ filter }}</strong>
+      over {{ dataPoints }} data points,
+      {{ visibleCount() }} found.
+    </span>
+
+    <!-- Provide tooling to unmount and remount the grid. -->
+    <!-- <a v-if="grid.length" v-on="click:unmountGrid">Unmount Grid</a> -->
+    <a @click="remountGrid">Remount Grid</a>
+  </form>
+
+  <table width="100%" cellspacing="2" :class="{ filtered: filter }">
+    <tr v-for="row in grid">
+      <th>{{ row.id }}</th>
+      <td v-for="item in row.items"
+        class="item"
+        :class="{ hidden: !matches(item) }">
+        {{ item.value }}
+      </td>
+    </tr>
+  </table>
+</div>
+
+<script>
+function generateGrid( rowCount, columnCount ) {
+  var valuePoints = [
+    "Daenerys", "Jon", "Sansa", "Arya", "Stannis", "Gregor", "Tyrion",
+    "Theon", "Joffrey", "Ramsay", "Cersei", "Bran", "Margaery",
+    "Melisandre", "Daario", "Jamie", "Eddard", "Myrcella", "Robb",
+    "Jorah", "Petyr", "Tommen", "Sandor", "Oberyn", "Drogo", "Ygritte"
+  ];
+  var valueIndex = 0;
+  var grid = [];
+
+  for ( var r = 0 ; r < rowCount ; r++ ) {
+    var row = {
+      id: r,
+      items: []
+    };
+    for ( var c = 0 ; c < columnCount ; c++ ) {
+      row.items.push({
+        id: ( r + "-" + c ),
+        value: valuePoints[ valueIndex ]
+      });
+      if ( ++valueIndex >= valuePoints.length ) {
+        valueIndex = 0;
+      }
+    }
+    grid.push( row );
+  }
+
+  return( grid );
+}
+
+var grid = generateGrid(1000, 10)
+var s = window.performance.now()
+console.profile('a')
+var vm = new Vue({
+
+  el: '#el',
+
+  data: {
+    renderTime: 0,
+    grid: Object.freeze(grid),
+    dataPoints: grid.length * grid[0].items.length,
+    filter: ''
+  },
+
+  methods: {
+    updateFilter (e) {
+      this.filter = e.target.value.toLowerCase()
+    },
+    matches (item) {
+      return item.value.toLowerCase().indexOf(this.filter) > -1
+    },
+    visibleCount () {
+      var count = 0
+      var grid = this.grid
+      for (var i = 0, l = grid.length; i < l; i++) {
+        var row = grid[i].items
+        for (var j = 0, k = row.length; j < k; j++) {
+          var item = row[j]
+          var matched = !this.filter || this.matches(item)
+          if (matched) {
+            count++
+          }
+        }
+      }
+      return count
+    },
+    unmountGrid: function () {
+      this.grid = []
+    },
+    remountGrid: function () {
+      this.grid = generateGrid(1000, 10)
+      console.profile('unmount')
+      Vue.nextTick(function () {
+        console.profileEnd('unmount')
+      })
+    }
+  }
+})
+console.profileEnd('a')
+setTimeout(function () {
+  vm.renderTime = window.performance.now() - s
+}, 0)
+</script>

File diff suppressed because it is too large
+ 2 - 0
benchmarks/big-table/style.css


+ 211 - 0
benchmarks/dbmon/ENV.js

@@ -0,0 +1,211 @@
+var ENV = ENV || (function() {
+
+  var first = true;
+  var counter = 0;
+  var data;
+  var _base;
+  (_base = String.prototype).lpad || (_base.lpad = function(padding, toLength) {
+    return padding.repeat((toLength - this.length) / padding.length).concat(this);
+  });
+
+  function formatElapsed(value) {
+    var str = parseFloat(value).toFixed(2);
+    if (value > 60) {
+      minutes = Math.floor(value / 60);
+      comps = (value % 60).toFixed(2).split('.');
+      seconds = comps[0].lpad('0', 2);
+      ms = comps[1];
+      str = minutes + ":" + seconds + "." + ms;
+    }
+    return str;
+  }
+
+  function getElapsedClassName(elapsed) {
+    var className = 'Query elapsed';
+    if (elapsed >= 10.0) {
+      className += ' warn_long';
+    }
+    else if (elapsed >= 1.0) {
+      className += ' warn';
+    }
+    else {
+      className += ' short';
+    }
+    return className;
+  }
+
+  function countClassName(queries) {
+    var countClassName = "label";
+    if (queries >= 20) {
+      countClassName += " label-important";
+    }
+    else if (queries >= 10) {
+      countClassName += " label-warning";
+    }
+    else {
+      countClassName += " label-success";
+    }
+    return countClassName;
+  }
+
+  function updateQuery(object) {
+    if (!object) {
+      object = {};
+    }
+    var elapsed = Math.random() * 15;
+    object.elapsed = elapsed;
+    object.formatElapsed = formatElapsed(elapsed);
+    object.elapsedClassName = getElapsedClassName(elapsed);
+    object.query = "SELECT blah FROM something";
+    object.waiting = Math.random() < 0.5;
+    if (Math.random() < 0.2) {
+      object.query = "<IDLE> in transaction";
+    }
+    if (Math.random() < 0.1) {
+      object.query = "vacuum";
+    }
+    return object;
+  }
+
+  function cleanQuery(value) {
+    if (value) {
+      value.formatElapsed = "";
+      value.elapsedClassName = "";
+      value.query = "";
+      value.elapsed = null;
+      value.waiting = null;
+    } else {
+      return {
+        query: "***",
+        formatElapsed: "",
+        elapsedClassName: ""
+      };
+    }
+  }
+
+  function generateRow(object, keepIdentity, counter) {
+    var nbQueries = Math.floor((Math.random() * 10) + 1);
+    if (!object) {
+      object = {};
+    }
+    object.lastMutationId = counter;
+    object.nbQueries = nbQueries;
+    if (!object.lastSample) {
+      object.lastSample = {};
+    }
+    if (!object.lastSample.topFiveQueries) {
+      object.lastSample.topFiveQueries = [];
+    }
+    if (keepIdentity) {
+      // for Angular optimization
+      if (!object.lastSample.queries) {
+        object.lastSample.queries = [];
+        for (var l = 0; l < 12; l++) {
+          object.lastSample.queries[l] = cleanQuery();
+        }
+      }
+      for (var j in object.lastSample.queries) {
+        var value = object.lastSample.queries[j];
+        if (j <= nbQueries) {
+          updateQuery(value);
+        } else {
+          cleanQuery(value);
+        }
+      }
+    } else {
+      object.lastSample.queries = [];
+      for (var j = 0; j < 12; j++) {
+        if (j < nbQueries) {
+          var value = updateQuery(cleanQuery());
+          object.lastSample.queries.push(value);
+        } else {
+          object.lastSample.queries.push(cleanQuery());
+        }
+      }
+    }
+    for (var i = 0; i < 5; i++) {
+      var source = object.lastSample.queries[i];
+      object.lastSample.topFiveQueries[i] = source;
+    }
+    object.lastSample.nbQueries = nbQueries;
+    object.lastSample.countClassName = countClassName(nbQueries);
+    return object;
+  }
+
+  function getData(keepIdentity) {
+    var oldData = data;
+    if (!keepIdentity) { // reset for each tick when !keepIdentity
+      data = [];
+      for (var i = 1; i <= ENV.rows; i++) {
+        data.push({ dbname: 'cluster' + i, query: "", formatElapsed: "", elapsedClassName: "" });
+        data.push({ dbname: 'cluster' + i + ' slave', query: "", formatElapsed: "", elapsedClassName: "" });
+      }
+    }
+    if (!data) { // first init when keepIdentity
+      data = [];
+      for (var i = 1; i <= ENV.rows; i++) {
+        data.push({ dbname: 'cluster' + i });
+        data.push({ dbname: 'cluster' + i + ' slave' });
+      }
+      oldData = data;
+    }
+    for (var i in data) {
+      var row = data[i];
+      if (!keepIdentity && oldData && oldData[i]) {
+        row.lastSample = oldData[i].lastSample;
+      }
+      if (!row.lastSample || Math.random() < ENV.mutations()) {
+        counter = counter + 1;
+        if (!keepIdentity) {
+          row.lastSample = null;
+        }
+        generateRow(row, keepIdentity, counter);
+      } else {
+        data[i] = oldData[i];
+      }
+    }
+    first = false;
+    return {
+      toArray: function() {
+        return data;
+      }
+    };
+  }
+
+  var mutationsValue = 0.5;
+
+  function mutations(value) {
+    if (value) {
+      mutationsValue = value;
+      return mutationsValue;
+    } else {
+      return mutationsValue;
+    }
+  }
+
+  var body = document.querySelector('body');
+  var theFirstChild = body.firstChild;
+
+  var sliderContainer = document.createElement( 'div' );
+  sliderContainer.style.cssText = "display: flex";
+  var slider = document.createElement('input');
+  var text = document.createElement('label');
+  text.innerHTML = 'mutations : ' + (mutationsValue * 100).toFixed(0) + '%';
+  text.id = "ratioval";
+  slider.setAttribute("type", "range");
+  slider.style.cssText = 'margin-bottom: 10px; margin-top: 5px';
+  slider.addEventListener('change', function(e) {
+    ENV.mutations(e.target.value / 100);
+    document.querySelector('#ratioval').innerHTML = 'mutations : ' + (ENV.mutations() * 100).toFixed(0) + '%';
+  });
+  sliderContainer.appendChild( text );
+  sliderContainer.appendChild( slider );
+  body.insertBefore( sliderContainer, theFirstChild );
+
+  return  {
+    generateData: getData,
+    rows: 50,
+    timeout: 0,
+    mutations: mutations
+  };
+})();

+ 16 - 0
benchmarks/dbmon/app.js

@@ -0,0 +1,16 @@
+Vue.config.preserveWhitespace = false
+
+var app = new Vue({
+  el: '#app',
+  data: {
+    databases: []
+  }
+})
+
+function loadSamples() {
+  app.databases = Object.freeze(ENV.generateData().toArray());
+  Monitoring.renderRate.ping();
+  setTimeout(loadSamples, ENV.timeout);
+}
+
+loadSamples()

+ 36 - 0
benchmarks/dbmon/index.html

@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta name="description" content="dbmon vue" />
+<link href="./lib/bootstrap.min.css" rel="stylesheet" type="text/css" />
+<link href="./lib/styles.css" rel="stylesheet" type="text/css" />
+<title>dbmon (Vue)</title>
+</head>
+<body>
+  <div id="app">
+    <table class="table table-striped lastest-data">
+      <tbody>
+        <tr v-for="db in databases" track-by="$index">
+          <td class="dbname">{{db.dbname}}</td>
+          <td class="query-count">
+            <span :class="db.lastSample.countClassName">{{db.lastSample.nbQueries}}</span>
+          </td>
+          <td v-for="q in db.lastSample.topFiveQueries" track-by="$index" :class="'Query ' + q.elapsedClassName">
+            {{q.formatElapsed}}
+            <div class="popover left">
+              <div class="popover-content">{{q.query}}</div>
+              <div class="arrow"></div>
+            </div>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+  </div>
+
+  <script src="./ENV.js"></script>
+  <script src="./lib/memory-stats.js"></script>
+  <script src="./lib/monitor.js"></script>
+  <script src="../../dist/vue.js"></script>
+  <script src="./app.js"></script>
+</body>
+</html>

File diff suppressed because it is too large
+ 6 - 0
benchmarks/dbmon/lib/bootstrap.min.css


+ 101 - 0
benchmarks/dbmon/lib/memory-stats.js

@@ -0,0 +1,101 @@
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author jetienne / http://jetienne.com/
+ * @author paulirish / http://paulirish.com/
+ */
+var MemoryStats = function (){
+
+	var msMin	= 100;
+	var msMax	= 0;
+
+	var container	= document.createElement( 'div' );
+	container.id	= 'stats';
+	container.style.cssText = 'width:80px;opacity:0.9;cursor:pointer';
+
+	var msDiv	= document.createElement( 'div' );
+	msDiv.id	= 'ms';
+	msDiv.style.cssText = 'padding:0 0 3px 3px;text-align:left;background-color:#020;';
+	container.appendChild( msDiv );
+
+	var msText	= document.createElement( 'div' );
+	msText.id	= 'msText';
+	msText.style.cssText = 'color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px';
+	msText.innerHTML= 'Memory';
+	msDiv.appendChild( msText );
+
+	var msGraph	= document.createElement( 'div' );
+	msGraph.id	= 'msGraph';
+	msGraph.style.cssText = 'position:relative;width:74px;height:30px;background-color:#0f0';
+	msDiv.appendChild( msGraph );
+
+	while ( msGraph.children.length < 74 ) {
+
+		var bar = document.createElement( 'span' );
+		bar.style.cssText = 'width:1px;height:30px;float:left;background-color:#131';
+		msGraph.appendChild( bar );
+
+	}
+
+	var updateGraph = function ( dom, height, color ) {
+
+		var child = dom.appendChild( dom.firstChild );
+		child.style.height = height + 'px';
+		if( color ) child.style.backgroundColor = color;
+
+	}
+
+	var perf = window.performance || {};
+	// polyfill usedJSHeapSize
+	if (!perf && !perf.memory){
+		perf.memory = { usedJSHeapSize : 0 };
+	}
+	if (perf && !perf.memory){
+		perf.memory = { usedJSHeapSize : 0 };
+	}
+
+	// support of the API?
+	if( perf.memory.totalJSHeapSize === 0 ){
+		console.warn('totalJSHeapSize === 0... performance.memory is only available in Chrome .')
+	}
+	
+	// TODO, add a sanity check to see if values are bucketed.
+	// If so, reminde user to adopt the --enable-precise-memory-info flag.
+	// open -a "/Applications/Google Chrome.app" --args --enable-precise-memory-info
+
+	var lastTime	= Date.now();
+	var lastUsedHeap= perf.memory.usedJSHeapSize;
+	return {
+		domElement: container,
+
+		update: function () {
+
+			// refresh only 30time per second
+			if( Date.now() - lastTime < 1000/30 )	return;
+			lastTime	= Date.now()
+
+			var delta	= perf.memory.usedJSHeapSize - lastUsedHeap;
+			lastUsedHeap	= perf.memory.usedJSHeapSize;
+			var color	= delta < 0 ? '#830' : '#131';
+			
+			var ms	= perf.memory.usedJSHeapSize;
+			msMin	= Math.min( msMin, ms );
+			msMax	= Math.max( msMax, ms );
+			msText.textContent = "Mem: " + bytesToSize(ms, 2);
+			
+			var normValue	= ms / (30*1024*1024);
+			var height	= Math.min( 30, 30 - normValue * 30 );
+			updateGraph( msGraph, height, color);
+			
+			function bytesToSize( bytes, nFractDigit ){
+				var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
+				if (bytes == 0) return 'n/a';
+				nFractDigit	= nFractDigit !== undefined ? nFractDigit : 0;
+				var precision	= Math.pow(10, nFractDigit);
+				var i 		= Math.floor(Math.log(bytes) / Math.log(1024));
+				return Math.round(bytes*precision / Math.pow(1024, i))/precision + ' ' + sizes[i];
+			};
+		}
+
+	}
+	
+};

+ 60 - 0
benchmarks/dbmon/lib/monitor.js

@@ -0,0 +1,60 @@
+var Monitoring = Monitoring || (function() {
+
+  var stats = new MemoryStats();
+  stats.domElement.style.position = 'fixed';
+  stats.domElement.style.right        = '0px';
+  stats.domElement.style.bottom       = '0px';
+  document.body.appendChild( stats.domElement );
+  requestAnimationFrame(function rAFloop(){
+      stats.update();
+      requestAnimationFrame(rAFloop);
+  });
+
+  var RenderRate = function () {
+    var container = document.createElement( 'div' );
+    container.id  = 'stats';
+    container.style.cssText = 'width:150px;opacity:0.9;cursor:pointer;position:fixed;right:80px;bottom:0px;';
+
+    var msDiv = document.createElement( 'div' );
+    msDiv.id  = 'ms';
+    msDiv.style.cssText = 'padding:0 0 3px 3px;text-align:left;background-color:#020;';
+    container.appendChild( msDiv );
+
+    var msText  = document.createElement( 'div' );
+    msText.id = 'msText';
+    msText.style.cssText = 'color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px';
+    msText.innerHTML= 'Repaint rate: 0/sec';
+    msDiv.appendChild( msText );
+
+    var bucketSize = 20;
+    var bucket = [];
+    var lastTime  = Date.now();
+    return {
+      domElement: container,
+      ping: function () {
+        var start = lastTime;
+        var stop = Date.now();
+        var rate = 1000 / (stop - start);
+        bucket.push(rate);
+        if (bucket.length > bucketSize) {
+          bucket.shift();
+        }
+        var sum = 0;
+        for (var i = 0; i < bucket.length; i++) {
+          sum = sum + bucket[i];
+        }
+        msText.textContent = "Repaint rate: " + (sum / bucket.length).toFixed(2) + "/sec";
+        lastTime = stop;
+      }
+    }
+  };
+
+  var renderRate = new RenderRate();
+  document.body.appendChild( renderRate.domElement );
+
+  return {
+    memoryStats: stats,
+    renderRate: renderRate
+  };
+  
+})();

+ 10 - 0
benchmarks/dbmon/lib/styles.css

@@ -0,0 +1,10 @@
+.Query {
+  position: relative;
+}
+
+.Query:hover .popover {
+  left: -100%;
+  width: 100%;
+  display: block;
+}
+

+ 96 - 0
benchmarks/reorder-list/index.html

@@ -0,0 +1,96 @@
+<script src="https://cdn.jsdelivr.net/lodash/4.10.0/lodash.min.js"></script>
+<script src="../../dist/vue.js"></script>
+<style>
+.danger {
+  background-color: red;
+}
+</style>
+
+<script type="x/template" id="t">
+<div>
+  <p>Used {{time}}ms.</p>
+  <button @click="shuffle">shuffle</button>
+  <button @click="add">add</button>
+  <table class="table table-hover table-striped test-data">
+    <row v-for="item in items" track-by="id"
+      :class="{ danger: item.id === selected }"
+      :item="item"
+      @select="select(item)"
+      @remove="remove(item)">
+    </row>
+  </table>
+</div>
+</script>
+
+<script type="x/template" id="row">
+<tr>
+  <td class="col-md-1">{{item.id}}</td>
+  <td class="col-md-4">
+      <a @click="$emit('select')">{{item.label}}</a>
+  </td>
+  <td class="col-md-1">
+    <button @click="$emit('remove')">remove</button>
+  </td>
+</tr>
+</script>
+
+<h1>1000 Components</h1>
+<div id="el">
+</div>
+
+<script>
+var items = []
+for (var i = 0; i < 1000; i++) {
+  items.push({
+    id: i,
+    label: String(Math.random()).slice(0, 5)
+  })
+}
+
+var s = window.performance.now()
+var vm = new Vue({
+  el: '#el',
+  template: '#t',
+  data: {
+    time: 0,
+    items: items,
+    selected: null
+  },
+  methods: {
+    shuffle: wrap(function () {
+      this.items = _.shuffle(this.items)
+    }),
+    add: wrap(function () {
+      this.items.push({
+        id: this.items.length,
+        label: String(Math.random()).slice(0, 5)
+      })
+    }),
+    select: wrap(function (item) {
+      this.selected = item.id
+    }),
+    remove: wrap(function (item) {
+      this.items.$remove(item)
+    })
+  },
+  components: {
+    row: {
+      props: ['item'],
+      template: '#row'
+    }
+  }
+})
+setTimeout(function () {
+  vm.time = window.performance.now() - s
+}, 0)
+
+function wrap (fn) {
+  return function () {
+    var s = window.performance.now()
+    fn.apply(this, arguments)
+    Vue.nextTick(function () {
+      vm.time = window.performance.now() - s
+    })
+  }
+}
+</script>

+ 87 - 0
benchmarks/svg/index.html

@@ -0,0 +1,87 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <title>vue.js version</title>
+    <script src="https://cdn.jsdelivr.net/stats.js/r11/stats.min.js"></script>
+    <script src="../../dist/vue.js"></script>
+  <style>
+html, body {
+  height: 100%;
+  width: 100%;
+  padding: 0;
+  margin: 0;
+}
+svg {
+  width: 800px;
+  height: 600px;
+}
+  </style>
+</head>
+<body>
+ <div id="app">
+ <svg>
+    <circle  v-for='point in points' :cx='point.x' :cy='point.y' r='2px' fill='#FC309D'></circle>
+ </svg>
+</div>
+<script type="text/javascript" charset="utf-8">
+var stats = new Stats();
+stats.setMode(0); // 0: fps, 1: ms, 2: mb
+
+// align top-left
+stats.domElement.style.position = 'absolute';
+stats.domElement.style.left = '0px';
+stats.domElement.style.top = '0px';
+
+document.body.appendChild( stats.domElement );
+
+var WIDTH = 800
+var HEIGHT = 600
+var model = createModel(1000);
+
+new Vue({
+  el: '#app',
+  data: model,
+  created: function () {
+    var self = this
+    self.raf = window.requestAnimationFrame(render)
+    stats.begin()
+    function render () {
+      stats.end()
+      stats.begin()
+      self.raf = window.requestAnimationFrame(render)
+      self.step()
+    }
+  }
+});
+
+function createModel (count) {
+  var points = []
+  for (var i = 0; i < count; ++i) {
+    points.push({
+      x: Math.random() * WIDTH,
+      y: Math.random() * HEIGHT,
+      vx: Math.random() * 4 - 2,
+      vy: Math.random() * 4 - 2
+    })
+  }
+
+  return {
+    points: points,
+    step: step
+  }
+
+  function step () {
+    points.forEach(move)
+    // move(points[0])
+  }
+
+  function move (p) {
+    if (p.x > WIDTH || p.x < 0) p.vx *= -1
+    if (p.y > HEIGHT || p.y < 0) p.vy *= -1
+    p.y += p.vy
+    p.x += p.vx
+  }
+}
+</script>
+</body>
+</html>

Some files were not shown because too many files changed in this diff