Movatterモバイル変換


[0]ホーム

URL:


latest log

酩酊状態で書いたエンジニアポエムです。酩酊状態で読んでください。

この広告は、90日以上更新していないブログに表示しています。

Chromium: Issue 173941: Canvas performance degrades significantly after 10 seconds

https://code.google.com/p/chromium/issues/detail?id=173941 についてです。

この issue は、Chrome forAndroid 25 β がリリースされ、いつものようにHTML5 canvas bench: particle 30000 - latest log を試したところ、パフォーマンスが10秒程で半分になり、あれ? となったところから始まっています。

issue 173941 を要約すると

  1. GC かな?
  2. +new Date; → new Date() or Date.now() で解決するんじゃない?
  3. +new Date; (単項演算子のプラス) は最適化されてないから、https://code.google.com/p/v8/issues/detail?id=2527 に登録するね

です。

http://mofmof-js.googlecode.com/svn/trunk/test/showcase/particle/30000.htm の 30000.js を修正し、3つのパターン( +new Date, new Date(), Date.now() )でベンチマークを取得し、メモリの使用状況について1分ほどグラフを取得してみました。

GCが発生するとグラフがストンと落ちています。



各パターンとも主要となるcanvasレンダリング処理のコードは同じで、違いはfpsを表示するために現在時刻を取得している部分です。

// +new Date versionvar _fps ={        count: 0,        node:document.getElementById("fps"),        last: +newDate//  [*]};/* omit */var now = +newDate;// [*]
// new Date() versionvar _fps ={        count: 0,        node:document.getElementById("fps"),        last:newDate()//  [*]};/* omit */var now =newDate();// [*]
var _fps ={        count: 0,        node:document.getElementById("fps"),        last:Date.now()//  [*]};/* omit */var now =Date.now();// [*]

いや、まさか、そんな… そこですか… という感じですね。ハイ


Chrome forAndroid 25 (beta 25.0.1364.64)

+new Date version
f:id:uupaa:20130206160851p:plain
10秒ぐらいから津波のようにGCが発生しています。描画パフォーマンスもがくっと落ちています。


new Date() version
f:id:uupaa:20130206160845p:plain
定期的にGCが発生しています。ずっと動かしていてもパフォーマンスは落ちません。安定しています。


Date.now() version
f:id:uupaa:20130206160844p:plain
定期的にGCが発生しています。ずっと動かしていてもパフォーマンスは落ちません。安定しています。


Chrome for Mac (stable 24.0.1312.57)

+new Date version
f:id:uupaa:20130206160827p:plain
非常に多くのGCが発生しています。GCは発生していますが描画速度に影響は出ていません。

new Date() version
f:id:uupaa:20130206160838p:plain
Chrome forAndroid と同様のグラフになっています。安定しています。

Date.now() version
f:id:uupaa:20130206160837p:plain
このグラフではこまめにGCが発生していますが、場合によっては3分間に1度ぐらいしか発生せず、安定しています。


色々と

  • 当初canvas 周りかと思ったらそうではなく、+new Date が問題だったという予想外の展開。
  • +new Date ではなく Date.now() を使いましょう。
  • DateObject - DateObject は数値に変換されます。つまり new Date() - new Date() は 0 です。
  • Timeline はほぼ初めて使ったのですが、普段は知ることができない役立つ情報を沢山提供してくれる優れ物でした。
  • まさか… と思いChromium を uupaa でエゴサーチしたら5つ程見つかり、報告サボっててすいませんすいません ><



source code

テストで使用したソースコードです。

<!DOCTYPE html><htmllang="ja"><head><metacharset="utf-8" /><title></title></head><body><canvasid="view"width="320"height="320"></canvas><divid="fps"></div><divid="userAgent"></div><divid="methodName"></div><scriptsrc="30000.js"></script><divid="pannel"><buttononclick="action(1)">setTimeout(4)</button><buttononclick="action(2)">setInterval(16)</button><buttononclick="action(3)">setImmdiate()</button><buttononclick="action(4)">requestAnimationFrame()</button><buttononclick="action(0)">stop</button></div></body></html>
// see: http://uupaa.hatenablog.com/entry/2012/02/08/160039document.getElementById("userAgent").textContent = navigator.userAgent;var _particles  = 30000;var _particle   =newArray(_particles * 4);var _canvas     =document.getElementById("view");var _ctx        = _canvas.getContext("2d");var _pixels     = _ctx.getImageData(0, 0, _canvas.width, _canvas.height);var _width      = _canvas.width;var _height     = _canvas.height;var _immediate  =window.setImmediate       ||window.oSetImmediate      ||window.msSetImmediate     ||window.mozSetImmediate    ||window.webkitSetImmediate;var _stopImmediate =window.clearImmediate       ||window.oClearImmediate      ||window.msClearImmediate     ||window.mozClearImmediate    ||window.webkitClearImmediate;var _animate    =window.requestAnimationFrame    ||window.oRequestAnimationFrame   ||window.msRequestAnimationFrame  ||window.mozRequestAnimationFrame ||window.webkitRequestAnimationFrame;var _stopAnimate=window.cancelRequestAnimationFrame    ||window.oCancelRequestAnimationFrame   ||window.msCancelRequestAnimationFrame  ||window.mozCancelRequestAnimationFrame ||window.webkitCancelRequestAnimationFrame;var _interval_id  =null;var _immediate_id =null;var _animate_id   =null;var _fps ={        count: 0,        node:document.getElementById("fps"),//      last: +new Date    //  [*]//      last: new Date()   //  [*]        last:Date.now()//  [*]};var _method = 0,    _methodNames ={        0:"stop",        1:"setTimeout",        2:"setInterval",        3:"setImmdiate",        4:"requestAnimationFrame"};// 【最適化】リンクリストを止めてArrayに(初期化速度と処理速度の向上)for (var i = 0, iz = _particles * 4; i < iz; i += 4){    _particle[i] = Math.random() * _canvas.width;    _particle[i + 1] = Math.random() * _canvas.height;    _particle[i + 2] = 0;    _particle[i + 3] = 0;}var _mouseOffset = _canvas.getBoundingClientRect();var _mouse ={ x: 0, y: 0};// 【最適化】毎回getBoundingClientRectを呼ばずに事前計算(追従性向上)_canvas.onmousemove =function(evt){    _mouse.x = evt.pageX - _mouseOffset.left;    _mouse.y = evt.pageY - _mouseOffset.top;};function tick(timestamp){var particle = _particle,        height = _height,        width = _width,        data = _pixels.data,        mx = _mouse.x,        my = _mouse.y,        i = 3, iz = data.length, j, x, y, vx, vy, dx, dy, acc, dd;// 【最適化】Alphaを半分にすることで「canvas全体クリア」ステップを省略// 【最適化】ループ展開でfps +5~10ぐらい稼いでる// poormans-effect (alpha effect)for (; i < iz; i += 32){        data[i] >>= 2;        data[i +  4] >>= 2;        data[i +  8] >>= 2;        data[i + 12] >>= 2;        data[i + 16] >>= 2;        data[i + 20] >>= 2;        data[i + 24] >>= 2;        data[i + 28] >>= 2;}for (i = 0, iz = particle.length; i < iz; i += 4){        x  = particle[i];        y  = particle[i + 1];        vx = particle[i + 2];        vy = particle[i + 3];        dx = mx - (x | 0);        dy = my - (y | 0);        dd = dx * dx + dy * dy;if (!dd){// [avoid] division by zero            dd = 1;}        acc = 50 / dd;        vx += acc * dx;        vy += acc * dy;        x  += vx;        y  += vy;        x = x > width  ? 0 : x < 0 ? width  - 1 : x;        y = y > height ? 0 : y < 0 ? height - 1 : y;        particle[i] = x;        particle[i + 1] = y;        particle[i + 2] = vx * 0.96;        particle[i + 3] = vy * 0.96;// 【最適化】紫のドットを打つ, alpha=200 は適当        j = ((x | 0) + (y | 0) * width) * 4;        data[j] = 230;        data[j + 2] = 230;        data[j + 3] = 200;}    _ctx.putImageData(_pixels, 0, 0);if (++_fps.count > 60){//      var now = +new Date;   // [*]//      var now = new Date();  // [*]var now =Date.now();// [*]var fps = 1000 / ((now - _fps.last) / _fps.count);        _fps.node.textContent ="FPS " + fps.toFixed(2);        _fps.last = now;        _fps.count = 0;}switch (_method){case 0:break;case 1: setTimeout(tick, 4);break;case 2:break;case 3: _immediate && (_immediate_id = _immediate(tick));break;case 4: _animate   && (_animate_id   = _animate(tick));break;}}function action(method){// @param Number: 0~4    _method = method;if (_interval_id){        clearInterval(_interval_id);        _interval_id =null;}if (_animate_id){        _stopAnimate(_animate_id);        _animate_id =null;}if (_immediate_id){        _stopImmediate(_immediate_id);        _immediate_id =null;}switch (_method){case 0:break;case 1: setTimeout(tick, 4);break;case 2: _interval_id = setInterval(tick, 16);break;case 3: _immediate && (_immediate_id = _immediate(tick));break;case 4: _animate   && (_animate_id   = _animate(tick));break;}document.getElementById("methodName").textContent = _methodNames[method];}

古いIEに対応する場合は以下のコードを追加し、+new Date ではなく Date.now() を使いましょう。

if (!Date.prototype.now){Date.prototype.now =function(){returnnewDate() * 1;};}
検索
注目記事

引用をストックしました

引用するにはまずログインしてください

引用をストックできませんでした。再度お試しください

限定公開記事のため引用できません。

読者です読者をやめる読者になる読者になる

[8]ページ先頭

©2009-2025 Movatter.jp