定期的に更新されているものを止める
映像を視聴する目的でページを閲覧していると、思っている以上に操作を行うことは少ないです。 そのため、何もしてないときに無駄な処理をしているものがいないかを見つけることは効果があるかもしれません。
ブラウザの多くの処理はイベント駆動なので、何もしてない状態で自動的に処理としてタイマーを使った処理があります。 ブラウザで定期的な処理を行う場合に使われるタイマーは次のものがあります。
setTimeout
setInterval
requestAnimationFrame
これらの関数を監視してみれば、無駄な処理を見つけることができそうです。
別の方法としてChromeにはPerformance Monitorがあるので、これをじっと見ていると変化に気づくことができます。 この問題も元々はPerformance Monitorを見ていて変な処理があることを見つけたのが出発点です。
観測
次のようなスクリプトを書き、この状態で放置して動いてるタイマーを見つけます。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* ## Usage | |
* | |
* 1. Load following script | |
* 2. `window.getContexualLogResult()` output the result to console | |
* | |
* ## Description | |
* | |
* - It spy "setTimeout", "setInterval", and "requestAnimationFrame". | |
* - Collect call count and collect stack trace. | |
*/ | |
// Disapprear log less than 5 | |
const thresholdCount = 5; | |
const map = new Map([ | |
["setTimeout", {}], | |
["setInterval", {}], | |
["requestAnimationFrame", {}] | |
]); | |
window.getContexualLogResult = () => { | |
["setTimeout", "setInterval", "requestAnimationFrame"].forEach(name => { | |
const result = Object.entries(map.get(name)) | |
.filter(entry => { | |
return entry[1] > thresholdCount; | |
}).map(entry => { | |
return { | |
code: entry[0], | |
count: entry[1] | |
} | |
}); | |
console.group(name); | |
console.table(result); | |
console.groupEnd(name); | |
}); | |
} | |
const contexualLog = (type, log) => { | |
if (map.has(type)) { | |
const object = map.get(type); | |
const count = object[log] ? object[log] : 0; | |
object[log] = count + 1; | |
} else { | |
const object = {}; | |
const count = object[log] ? object[log] : 0; | |
object[log] = count + 1; | |
map.set(type, object); | |
} | |
} | |
const originalTimout = window.setTimeout; | |
window.setTimeout = function () { | |
const stack = (new Error().stack).split("\n").slice(1); | |
if (arguments.callee.caller) { | |
contexualLog("setTimeout", arguments.callee.caller.toString() + "\n" + stack); | |
} else { | |
contexualLog("setInterval", stack) | |
} | |
return originalTimout.apply(window, arguments); | |
} | |
const originalInterval = window.setInterval; | |
window.setInterval = function () { | |
const stack = (new Error().stack).split("\n").slice(1).join("\n"); | |
if (arguments.callee.caller) { | |
contexualLog("setInterval", arguments.callee.caller.toString() + "\n" + stack); | |
} else { | |
contexualLog("setInterval", stack) | |
} | |
return originalInterval.apply(window, arguments); | |
} | |
const originalRaF = window.requestAnimationFrame; | |
window.requestAnimationFrame = function (callback) { | |
const stack = (new Error().stack).split("\n").slice(1).join("\n"); | |
if (arguments.callee.caller) { | |
contexualLog("requestAnimationFrame", arguments.callee.caller.toString() + "\n" + stack); | |
} else { | |
contexualLog("requestAnimationFrame", stack) | |
} | |
return originalRaF(callback); | |
} |
参考
観測していると定期的に動いてるものとして、次のようなものがありました。
- Live Streamの映像取得
- コメントの取得
- コメントの描画
- Canvasで描画されている
requestAnimation
でメインループを回している
- 番組の状態の取得(視聴数やコメント数)
- アンケート画面の更新処理
- アンケートはゲームのようなメインループで描画を更新している。
- そのため
requestAnimation
でメインループを回している
この中で、アンケート画面が表示されていないにもかかわらず、アンケートのメインループ(requestAnimation
)が動いていることがわかりました。これは無駄な処理といえそうです。
修正方針
アンケートが非表示にのときにもアンケートのメインループが動いているの問題だった。 原因としては、アンケートがない時は単にCSSで表示を消しているだけに過ぎない状態だった。
そのため、アンケートが行われていない時は、アンケートのプログラムを完全に消すようにすれば修正できそうです。
修正
アンケートが行われていない時は、アンケートをDOMからも削除するようにしました。
計測
観測で利用したスクリプトを使い、修正後にrequestAnimation
が減っていることが確認できました。