Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save sounisi5011/a0d497fea1b2aea945e5 to your computer and use it in GitHub Desktop.
Save sounisi5011/a0d497fea1b2aea945e5 to your computer and use it in GitHub Desktop.
Uint8ClampedArray/CanvasPixelArrayの比較処理 - パフォーマンス計測
Uint8ClampedArray/CanvasPixelArrayの比較処理 - パフォーマンス計測
(function (window) {
'use strict';
try {
var pixelRatio = window.devicePixelRatio || 1;
var offset = 12 * pixelRatio;
var node = document.createElement('canvas');
var ctx1;
var ctx2;
var imageData;
var data1;
var isEqual;
var getImageData = function(ctx){
return ctx.getImageData(0, 0, node.width, node.height);
};
node.width = 100;
node.height = 100;
/**
* @see https://github.com/Modernizr/Modernizr/blob/master/feature-detects/canvas.js
*/
if (!(node.getContext && (ctx1 = node.getContext('2d')))) {
throw new Error('canvas要素に対応していません');
}
/**
* @link https://github.com/Modernizr/Modernizr/blob/master/feature-detects/canvastext.js
*/
if (typeof ctx1.fillText !== 'function') {
throw new Error('canvas fillTextに対応していません');
}
ctx1.fillStyle = '#f00';
ctx1.textBaseline = 'top';
ctx1.font = '32px Arial';
ctx1.fillText('\ud83d\udc28', 0, 0);
if (typeof ctx1.getImageData !== 'function') {
throw new Error('canvas getImageDataに対応していません');
}
if (!(
(imageData = getImageData(ctx1)) &&
(data1 = imageData.data)
)) {
throw new Error('canvas getImageDataから正しい結果を取得できませんでした');
}
ctx2 = node.cloneNode(false).getContext('2d');
ctx2.drawImage(node, 0, 0);
ctx2.fillStyle = '#00f';
ctx2.fillRect(70, 70, 2, 2);
document.body.appendChild(ctx1.canvas);
document.body.appendChild(ctx2.canvas);
/**
* TypedArray.prototype.toStringで文字列変換し、
* 比較する
*/
if (data1.toString) {
/* 同一のデータ */
JSLitmus.test('toString [TRUE]', function(count) {
var data;
while (count--) {
data = getImageData(ctx1).data;
isEqual = data1.toString() === data.toString();
}
});
/* 異なるデータ */
JSLitmus.test('toString [FALSE]', function(count) {
var data;
while (count--) {
data = getImageData(ctx2).data;
isEqual = data1.toString() === data.toString();
}
});
}
/**
* for文で地道に全ての要素が同じ値か比較する
*/
/* 同一のデータ */
JSLitmus.test('for [TRUE]', function(count) {
var data;
while (count--) {
data = getImageData(ctx1).data;
isEqual = true;
for (var i = data1.length; i--; ) {
if (data1[i] !== data[i]) {
isEqual = false;
break;
}
}
}
});
/* 異なるデータ */
JSLitmus.test('for [FALSE]', function(count) {
var data;
while (count--) {
data = getImageData(ctx2).data;
isEqual = true;
for (var i = data1.length; i--; ) {
if (data1[i] !== data[i]) {
isEqual = false;
break;
}
}
}
});
/**
* TypedArray.prototype.everyで
* 全ての要素が同じ値か比較する
*/
if (data1.every) {
/* 同一のデータ、コールバック関数を事前に定義、第一引数を使用 */
JSLitmus.test('every1 [TRUE]', function(count) {
var data = getImageData(ctx1).data;
var every_callback1_t = function(elem, index) {
return elem === data[index];
};
while (count--) {
isEqual = data1.every(every_callback1_t);
}
});
/* 異なるデータ、コールバック関数を事前に定義、第一引数を使用 */
JSLitmus.test('every1 [FALSE]', function(count) {
var data = getImageData(ctx2).data;
var every_callback1_f = function(elem, index) {
return elem === data[index];
};
while (count--) {
isEqual = data1.every(every_callback1_f);
}
});
/* 同一のデータ、コールバック関数を事前に定義、外部変数を使用 */
JSLitmus.test('every2 [TRUE]', function(count) {
var data = getImageData(ctx1).data;
var every_callback2_t = function(_, index) {
return data1[index] === data[index];
};
while (count--) {
isEqual = data1.every(every_callback2_t);
}
});
/* 異なるデータ、コールバック関数を事前に定義、外部変数を使用 */
JSLitmus.test('every2 [FALSE]', function(count) {
var data = getImageData(ctx2).data;
var every_callback2_f = function(_, index) {
return data1[index] === data[index];
};
while (count--) {
isEqual = data1.every(every_callback2_f);
}
});
/* 同一のデータ、コールバック関数をインラインで定義、第一引数を使用 */
JSLitmus.test('every3 [TRUE]', function(count) {
var data;
while (count--) {
data = getImageData(ctx1).data;
isEqual = data1.every(function(elem, index) {
return elem === data[index];
});
}
});
/* 異なるデータ、コールバック関数をインラインで定義、第一引数を使用 */
JSLitmus.test('every3 [FALSE]', function(count) {
var data;
while (count--) {
data = getImageData(ctx2).data;
isEqual = data1.every(function(elem, index) {
return elem === data[index];
});
}
});
/* 同一のデータ、コールバック関数をインラインで定義、外部変数を使用 */
JSLitmus.test('every4 [TRUE]', function(count) {
var data;
while (count--) {
data = getImageData(ctx1).data;
isEqual = data1.every(function(_, index) {
return data1[index] === data[index];
});
}
});
/* 異なるデータ、コールバック関数をインラインで定義、外部変数を使用 */
JSLitmus.test('every4 [FALSE]', function(count) {
var data;
while (count--) {
data = getImageData(ctx2).data;
isEqual = data1.every(function(_, index) {
return data1[index] === data[index];
});
}
});
}
} catch (e) {
console.error(e);
alert(e.message);
}
}(window));
<!DOCTYPE html>
<meta charset="utf-8">
<title>Uint8ClampedArray/CanvasPixelArrayの比較処理 - パフォーマンス計測</title>
<script src="JSLitmus.js"></script>
<body>
<script src="app.js"></script>
// JSLitmus.js
//
// Copyright (c) 2010, Robert Kieffer, http://broofa.com
// Available under MIT license (http://en.wikipedia.org/wiki/MIT_License)
// @link http://www.broofa.com/Tools/JSLitmus/
(function(){var platform="unknown platform",ua=navigator.userAgent;var oses=["Windows","iPhone OS","(Intel |PPC )?Mac OS X","Linux"].join("|");var pOS=(new RegExp("(("+oses+") [^ );]*)")).test(ua)?RegExp.$1:null;if(!pOS)pOS=(new RegExp("(("+oses+")[^ );]*)")).test(ua)?RegExp.$1:null;var pName=/(Chrome|MSIE|Safari|Opera|Firefox)/.test(ua)?RegExp.$1:null;var vre=new RegExp("(Version|"+pName+")[ /]([^ ;]*)");var pVersion=pName&&vre.test(ua)?RegExp.$2:null;var platform=pOS&&pName&&pVersion?pName+" "+pVersion+" on "+pOS:"unknown platform";var jsl={escape:function(s){s=s.replace(/,/g,"\\,");s=escape(s);s=s.replace(/\+/g,"%2b");s=s.replace(/ /g,"+");return s},$:function(id){return document.getElementById(id)},F:function(){},status:function(msg){var el=jsl.$("jsl_status");if(el)el.innerHTML=msg||""},toLabel:function(n){if(n==Infinity)return"Infinity";else if(n>1E9){n=Math.round(n/1E8);return n/10+"B"}else if(n>1E6){n=Math.round(n/1E5);return n/10+"M"}else if(n>1E3){n=Math.round(n/100);return n/10+"K"}return n},extend:function(dst,src){for(var k in src)dst[k]=src[k];return dst},join:function(o,delimit1,delimit2){if(o.join)return o.join(delimit1);var pairs=[];for(var k in o)pairs.push(k+delimit1+o[k]);return pairs.join(delimit2)},indexOf:function(arr,o){if(arr.indexOf)return arr.indexOf(o);for(var i=0;i<this.length;i++)if(arr[i]===o)return i;return-1}};var Test=function(name,f){if(!f)throw new Error("Undefined test function");if(!/function[^\(]*\(([^,\)]*)/.test(f.toString()))throw new Error('"'+name+'" test: Test is not a valid Function object');this.loopArg=RegExp.$1;this.name=name;this.f=f};jsl.extend(Test,{CALIBRATIONS:[new Test("calibrating loop",function(count){while(count--);}),new Test("calibrating function",jsl.F)],calibrate:function(onCalibrated){for(var i=0;i<Test.CALIBRATIONS.length;i++){var cal=Test.CALIBRATIONS[i];if(cal.running)return true;if(!cal.count){cal.isCalibration=true;cal.onStop=onCalibrated;cal.run(2E4);return true}}return false}});jsl.extend(Test.prototype,{INIT_COUNT:10,MAX_COUNT:1E9,MIN_TIME:.5,onChange:jsl.F,onStop:jsl.F,reset:function(){delete this.count;delete this.time;delete this.running;delete this.error},run:function(count){count=count||this.INIT_COUNT;jsl.status(this.name+" x "+count);this.running=true;var me=this;setTimeout(function(){me._run(count)},200)},_run:function(count){var me=this;if(!me.isCalibration&&Test.calibrate(function(){me.run(count)}))return;this.error=null;try{var start,f=this.f,now,i=count;start=new Date;if(this.loopArg)f(count);else while(i--)f();this.time=Math.max(1,new Date-start)/1E3;this.count=count;this.period=this.time/count;this.running=this.time<=this.MIN_TIME;if(this.running){var x=this.MIN_TIME/this.time;var pow=Math.pow(2,Math.max(1,Math.ceil(Math.log(x)/Math.log(2))));count*=pow;if(count>this.MAX_COUNT)throw new Error("Max count exceeded. If this test uses a looping function, make sure the iteration loop is working properly.");}}catch(e){this.reset();this.error=e}if(this.running)me.run(count);else{jsl.status("");me.onStop(me)}this.onChange(this)},getHz:function(normalize){var p=this.period;if(normalize&&!this.isCalibration){var cal=Test.CALIBRATIONS[this.loopArg?0:1];p=p<cal.period*1.2?0:p-cal.period}return Math.round(1/p)},toString:function(){return this.name+" - "+this.time/this.count+" secs"}});var STYLESHEET="<style> #jslitmus {font-family:sans-serif; font-size: 12px;} #jslitmus a {text-decoration: none;} #jslitmus a:hover {text-decoration: underline;} #jsl_status { margin-top: 10px; font-size: 10px; color: #888; } A IMG {border:none} #test_results { margin-top: 10px; font-size: 12px; font-family: sans-serif; border-collapse: collapse; border-spacing: 0px; } #test_results th, #test_results td { border: solid 1px #ccc; vertical-align: top; padding: 3px; } #test_results th { vertical-align: bottom; background-color: #ccc; padding: 1px; font-size: 10px; } #test_results #test_platform { color: #444; text-align:center; } #test_results .test_row { color: #006; cursor: pointer; } #test_results .test_nonlooping { border-left-style: dotted; border-left-width: 2px; } #test_results .test_looping { border-left-style: solid; border-left-width: 2px; } #test_results .test_name {white-space: nowrap;} #test_results .test_pending { } #test_results .test_running { font-style: italic; } #test_results .test_done {} #test_results .test_done { text-align: right; font-family: monospace; } #test_results .test_error {color: #600;} #test_results .test_error .error_head {font-weight:bold;} #test_results .test_error .error_body {font-size:85%;} #test_results .test_row:hover td { background-color: #ffc; text-decoration: underline; } #chart { margin: 10px 0px; width: 250px; } #chart img { border: solid 1px #ccc; margin-bottom: 5px; } #chart #tiny_url { height: 40px; width: 250px; } #jslitmus_credit { font-size: 10px; color: #888; margin-top: 8px; } </style>";var MARKUP='<div id="jslitmus"> <button onclick="JSLitmus.runAll(event)">Run Tests</button> <button id="stop_button" disabled="disabled" onclick="JSLitmus.stop()">Stop Tests</button> <br > <br > <input type="checkbox" style="vertical-align: middle" id="test_normalize" checked="checked" onchange="JSLitmus.renderAll()""> Normalize results <table id="test_results"> <colgroup> <col /> <col width="100" /> </colgroup> <tr><th id="test_platform" colspan="2">'+platform+'</th></tr> <tr><th>Test</th><th>Ops/sec</th></tr> <tr id="test_row_template" class="test_row" style="display:none"> <td class="test_name"></td> <td class="test_result">Ready</td> </tr> </table> <div id="jsl_status"></div> <div id="chart" style="display:none"> <a id="chart_link" target="_blank"><img id="chart_image"></a> TinyURL (for chart): <iframe id="tiny_url" frameBorder="0" scrolling="no" src=""></iframe> </div> <a id="jslitmus_credit" title="JSLitmus home page" href="http://code.google.com/p/jslitmus" target="_blank">Powered by JSLitmus</a> </div>';window.JSLitmus={_tests:[],_queue:[],params:{},_init:function(){var match=(location+"").match(/([^?#]*)(#.*)?$/);if(match){var pairs=match[1].split("&");for(var i=0;i<pairs.length;i++){var pair=pairs[i].split("=");if(pair.length>1){var key=pair.shift();var value=pair.length>1?pair.join("="):pair[0];this.params[key]=value}}}document.write(STYLESHEET);if(window.addEventListener)window.addEventListener("load",this._setup,false);else if(document.addEventListener)document.addEventListener("load",this._setup,false);else if(window.attachEvent)window.attachEvent("onload",this._setup);return this},_setup:function(){var el=jsl.$("jslitmus_container");if(!el)document.body.appendChild(el=document.createElement("div"));el.innerHTML=MARKUP;for(var i=0;i<JSLitmus._tests.length;i++)JSLitmus.renderTest(JSLitmus._tests[i])},renderAll:function(){for(var i=0;i<JSLitmus._tests.length;i++)JSLitmus.renderTest(JSLitmus._tests[i]);JSLitmus.renderChart()},renderChart:function(){var url=JSLitmus.chartUrl();jsl.$("chart_link").href=url;jsl.$("chart_image").src=url;jsl.$("chart").style.display="";jsl.$("tiny_url").src="http://tinyurl.com/api-create.php?url="+escape(url)},renderTest:function(test){if(!test._row){var trow=jsl.$("test_row_template");if(!trow)return;test._row=trow.cloneNode(true);test._row.style.display="";test._row.id="";test._row.onclick=function(){JSLitmus._queueTest(test)};test._row.title="Run "+test.name+" test";trow.parentNode.appendChild(test._row);test._row.cells[0].innerHTML=test.name}var cell=test._row.cells[1];var cns=[test.loopArg?"test_looping":"test_nonlooping"];if(test.error){cns.push("test_error");cell.innerHTML='<div class="error_head">'+test.error+"</div>"+'<ul class="error_body"><li>'+jsl.join(test.error,": ","</li><li>")+"</li></ul>"}else if(test.running){cns.push("test_running");cell.innerHTML="running"}else if(jsl.indexOf(JSLitmus._queue,test)>=0){cns.push("test_pending");cell.innerHTML="pending"}else if(test.count){cns.push("test_done");var hz=test.getHz(jsl.$("test_normalize").checked);cell.innerHTML=hz!=Infinity?hz:"&infin;";cell.title="Looped "+test.count+" times in "+test.time+" seconds"}else cell.innerHTML="ready";cell.className=cns.join(" ")},test:function(name,f){var test=new Test(name,f);JSLitmus._tests.push(test);test.onChange=JSLitmus.renderTest;test.onStop=function(test){if(JSLitmus.onTestFinish)JSLitmus.onTestFinish(test);JSLitmus.currentTest=null;JSLitmus._nextTest()};this.renderTest(test)},runAll:function(e){e=e||window.event;var reverse=e&&e.shiftKey,len=JSLitmus._tests.length;for(var i=0;i<len;i++)JSLitmus._queueTest(JSLitmus._tests[!reverse?i:len-i-1])},stop:function(){while(JSLitmus._queue.length){var test=JSLitmus._queue.shift();JSLitmus.renderTest(test)}},_nextTest:function(){if(!JSLitmus.currentTest){var test=JSLitmus._queue.shift();if(test){jsl.$("stop_button").disabled=false;JSLitmus.currentTest=test;test.run();JSLitmus.renderTest(test);if(JSLitmus.onTestStart)JSLitmus.onTestStart(test)}else{jsl.$("stop_button").disabled=true;JSLitmus.renderChart()}}},_queueTest:function(test){if(jsl.indexOf(JSLitmus._queue,test)>=0)return;JSLitmus._queue.push(test);JSLitmus.renderTest(test);JSLitmus._nextTest()},chartUrl:function(){var n=JSLitmus._tests.length,markers=[],data=[];var d,min=0,max=-1E10;var normalize=jsl.$("test_normalize").checked;for(var i=0;i<JSLitmus._tests.length;i++){var test=JSLitmus._tests[i];if(test.count){var hz=test.getHz(normalize);var v=hz!=Infinity?hz:0;data.push(v);markers.push("t"+jsl.escape(test.name+"("+jsl.toLabel(hz)+")")+",000000,0,"+markers.length+",10");max=Math.max(v,max)}}if(markers.length<=0)return null;var title=document.getElementsByTagName("title");title=title&&title.length?title[0].innerHTML:null;var chart_title=[];if(title)chart_title.push(title);chart_title.push("Ops/sec ("+platform+")");var labels=[jsl.toLabel(min),jsl.toLabel(max)];var w=250,bw=15;var bs=5;var h=markers.length*(bw+bs)+30+chart_title.length*20;var params={chtt:escape(chart_title.join("|")),chts:"000000,10",cht:"bhg",chd:"t:"+data.join(","),chds:min+","+max,chxt:"x",chxl:"0:|"+labels.join("|"),chsp:"0,1",chm:markers.join("|"),chbh:[bw,0,bs].join(","),chs:w+"x"+h};return"http://chart.apis.google.com/chart?"+jsl.join(params,"=","&")}};JSLitmus._init()})();

検証結果

Internet Explorer 11

toStringが異常な程高速。

Google Chrome 47

toStringは全体的に遅く、 everyは値が同じ場合と違う場合で速度の差が著しく大きい。 for文がそれなりに早く、バランスも良い。

Firefox 43

for文もそれなりに速いが、toStringが最速。 toStringとは比べ物にならない。

Opera 34

Google Chromeとほぼ同じ。 内部エンジンがGoogle Chromeと同じBlinkなので、区別する必要性は無いかもしれない。

Opera 12.17

for文は遅め。対して、toStringは非常に速い。 Firefox以上、Internet Explorer未満。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment