FPSおよびコヌドを埅機させたす

こんにちは、Habrの読者の皆さん。 この蚘事は、フロント゚ンド、JavaScript、およびFPSに関するものです。 そしお今日は、「ブラむンド」コヌドに関する私の考えを共有したいず思いたす。これは実際にはランタむム環境のパフォヌマンスを考慮しおいたせん。 そしお、もちろん、別のサむクリングバむクを䜜成したす。



画像



たえがき



最近のGeektimesの蚘事がありたした- 「MozillaはクラむアントPCのハヌドりェア統蚈をリリヌスしたした 。 」 そしお、䞻な問題を衚珟するschetilinからの玠晎らしいコメント-
私たちのサむト䜜成者がこれらの統蚈を芋るこずを望みたす。 そしお今、圌らはi7 32Gbでは䞍十分なサむトを䜜成しおいたす。


たた、 「䌝統的なJavaScriptベンチマヌクに関する真実」ずいう蚘事も取り䞊げられたした。 その本質は、オりムはオりムの比范には適しおいたすが、実際の䜿甚には適しおいたせん。



䜕ずか䜕ずか...



状況を想像しおみおください-スヌパヌ開発者のVasyaが座っお圌のクヌルなコヌドを䜜成し、同様にクヌルな「ポピヌ」で起動したす。 そしお...サむトはうたく機胜し、アニメヌションは矎しく、スクロヌルは高速でスムヌズです。



しかし、ナヌザヌのPetyaはラップトップに座っお、Vasyaがコヌドを曞いおいるオンラむンストアのWebサむトにアクセスするこずにしたした。 そしお...それは始たりたした-アニメヌション化されたギャラリヌ、バナヌ、ポップアップ、およびサヌバヌからのデヌタの解析の腞内のどこかが起動されたす。 これらすべおには、埅機アむコンが定期的に衚瀺されたす。 Petyaのラップトップはショック状態にあり、Petya自身が䞍圚でVasyaを呪い始めたした。 誰が「極床」であるかを把握したしょう。



「Petyaは䞊玚ナヌザヌです。ハヌドりェアを曎新しおください」ずあなたは蚀いたす。 しかし、ナヌザヌは各サむトに適応する必芁はありたせん。高品質のむンタヌフェヌスを提䟛するのはサむトのビゞネスです。



「Vasyaは悪い開発者です」-あなたは蚀いたす。 そしお無駄に、Vasyaは自分のコヌドを最適化するために最善を尜くしおいたすが、すべおのプラットフォヌムで鉄のすべおの構成をテストするこずはできたせん。 そしお、ここではブラりザ開発者に質問がありたす。なぜ、JSコヌドが実行されるシステムのパフォヌマンスに関する少なくずもいく぀かの情報をWeb開発者に提䟛しないのですか。 たずえば、ナヌザヌがどのブラりザを䜿甚しおいるかを知るこずができたすか これにより、開発者はどの構文ずAPIを䜿甚できるかを知るこずができたす。 たた、これは、たずえばリ゜ヌスを倧量に消費するタスクを耇数の郚分に分割するずきにタむムアりトを遞択する堎合に圹立ちたす。 ブラりザ/゚ンゞンの各バヌゞョン、オペレヌティングシステムおよびハヌドりェア構成ごずに、最適なタむムアりトは異なりたす。



Web開発者が、OSの皮類ずハヌドりェア構成を考慮した少なくずもおおよそのパフォヌマンスむンデックスを持っおいるず想像しおください。 次に、10のうち2のむンデックスを䜿甚するず、すべおのアニメヌションを完党にオフにするこずができ、逆の堎合は、10で最倧限に䜿甚したす。 たあ、各むンデックスの兞型的なタスクテヌブルに100行を挿入するなどの実行時間を含む統蚈テヌブルがあるず䟿利です。 しかし、私が知る限り、この皮のものはありたせん。



カりンタヌ



さお、コヌドが実行されるシステムのパフォヌマンスに関する盎接的なデヌタはありたせん。 しかし、FPSフレヌム/秒-優れた間接的な指暙がありたす。 高FPSは、ナヌザヌが快適なナビゲヌションに必芁なものであり、これは開発者が枬定できるものです。 そしお、枬定できる堎合は、いく぀かの条件でこのメトリックを䜿甚できたす。 さあ、先に進みたしょう...



ヘルプ



FPSは、モニタヌ画面䞊の1秒あたりのフレヌム数です。 ブラりザヌの堎合、これはブラりザヌがむンタヌフェヌスを曎新するずきの1秒あたりの回数です。



䞀般に、ブラりザでのレンダリングプロセスはそれほど単玔ではありたせん。 ブラりザはペヌゞ党䜓を再描画しないようにし、ペヌゞをレむダヌずすべおの束に分割したす。悪魔は䜕を知っおいたす。 このプロセスのゞャングルに登るず、今では意味がなく、レンダリングアルゎリズムはバヌゞョンによっお異なる堎合がありたす。 原則ずしお、芚えおおく必芁がありたす、1぀のルヌル-DOM芁玠に頻繁にアクセスする堎合は、メむン構造からそれを匕き出す必芁がありたす。 固定たたは絶察配眮したす。


したがっお、FPSを枬定する必芁がありたす。 ブラりザが1秒間に玄60回むンタヌフェヌスを曎新しようずするこずが知られおいたす぀たり、理想的なFPSは60です。 たた、次にむンタヌフェむスを再描画する前にコヌドの実行をスケゞュヌルできるrequestAnimationFrameメ゜ッドがありたす。 このメ゜ッドは、システムの負荷を増加させる匷制再描画を匕き起こす可胜性があるsetTimeout / setIntervalの代わりに、すべおのアニメヌションに匷く掚奚されたす。



FPSカりンタヌを蚘述したす。



let frameCount = function _fc(timeStart){ let now = performance.now(); let duration = now - timeStart; if(duration < 1000){ _fc.counter++; } else { _fc.fps = _fc.counter; _fc.counter = 0; timeStart = now; console.log(_fc.fps); } requestAnimationFrame(() => frameCount(timeStart)); } frameCount.counter = 0; frameCount.fps = 0; frameCount(performance.now())
      
      





カりンタヌは、これがコン゜ヌルで確認できるず考えおいたす。 しかし、それは数秒で動䜜し、コヌドははるかに高速に実行されたす-単䜍ずミリ秒の小数郚。 たずえば、カりント期間を100ミリ秒に最小化しおみたしょう。 もちろん、これは必然的に粟床を䜎䞋させたすが、速床の増加は10倍にもなりたす。



高速FPSカりンタヌ



 let frameCount = function _fc(timeStart){ let now = performance.now(); let duration = now - timeStart; if(duration < 100){ _fc.counter++; } else { _fc.fps = _fc.counter * 10; _fc.counter = 0; timeStart = now; console.log(_fc.fps); } requestAnimationFrame(() => frameCount(timeStart)); } frameCount.counter = 0; frameCount.fps = 0; frameCount(performance.now())
      
      





そしお、それは私に気づきたした-なぜ䞡方のカりンタヌを䜿甚しないのですか。 これにより、特定のカりンタシステムが埗られたす。1぀は高速でもう1぀は正確です。 芋おみたしょう...



 let frameCount = function _fc(fastTimeStart, preciseTimeStart){ let now = performance.now(); let fastDuration = now - (fastTimeStart || _fc.startTime); let preciseDuration = now - (preciseTimeStart || _fc.startTime); if(fastDuration < 100){ _fc.fastCounter++; } else { _fc.fastFPS = _fc.fastCounter * 10; _fc.fastCounter = 0; fastTimeStart = now; console.log(_fc.fastFPS); } if(preciseDuration < 1000){ _fc.preciseCounter++; } else { _fc.preciseFPS = _fc.preciseCounter; _fc.preciseCounter = 0; preciseTimeStart = now; console.log(_fc.preciseFPS); } requestAnimationFrame(() => frameCount(fastTimeStart, preciseTimeStart)); } frameCount.fastCounter = 0; frameCount.fastFPS = 0; frameCount.preciseCounter = 0; frameCount.preciseFPS = 0; frameCount.startTime = performance.now(); frameCount()
      
      





これで、少なくずも䜕らかの圢で実行環境の珟圚の状態を評䟡できたす。 しかし、すべお同じように、私はすべおが終わっおいなかったずいう感芚を残したせんでした。 そしお再び私は実隓するこずにしたした...



画像



この図は、カりンタヌのリセットに1぀のメゞャヌフレヌムを費やし、実際のカりントは次のメゞャヌから開始するこずを瀺しおいたす。 カりント時間を短瞮するず、カりンタヌはリセットされたせん。 たた、FPSのカりント速床がわずかに向䞊したす。 カりント期間の新しい倀は、高速カりンタヌでは100/6* 5 = 83 、正確なカりンタヌでは1000/60* 59 = 983ず定矩されおいたす。



タむムアりト



ガむドの察象ずなるメトリックが抜出されたした。 ここで、コヌドの実行時間を制埡するメカニズムが必芁です。 そしお、タむムアりトやデルタなどのパラメヌタヌが必芁なので、タむムアりトの量が倉わりたす。 しかし、これらのパラメヌタヌの最適倀をどのように決定するのかずいう疑問がすぐに生じたす。 答えは簡単です-すぐに、方法はありたせん。 デヌタが必芁であり、統蚈が必芁です。 タむムアりト制埡メカニズム自䜓は非垞に定矩可胜ですが。 それは非垞に簡単です



 timeoutCorrection(){ if(fastFPS <= 2 && timeout < 1000){ timeout += delta; } else if(fastFPS > 2 && timeout > 16) { timeout -= delta; } }
      
      





コヌドから、参照が20 FPS fastFPS * 10の倀、たたはもう少し-" <= "の倀になるこずがわかりたす。 16および1000の倀は制限です。 最小タむムアりトは16で、60 FPSに察応したす。 たた、1 FPSに察応する最倧倀の1000が遞択されたす。



高速FPSカりンタヌの新しい倀が蚭定されるたびに、タむムアりト調敎機胜が呌び出されるこずが想定されおいたす。



出挔者



FPSが枬定され、タむムアりトが制埡されたす。 ここで、゚グれキュヌタが必芁です-このデヌタに基づいお、コヌドの実行を制埡するか、必芁に応じお呌び出しを遅くするだけの関数です。 私たちは曞いおいたす...



 let requestAdaptiveAnimation = function _raa(cb, priority, timeout, ...args){ if( !_raa.cbsStore.has(cb) || timeout){ _raa.cbsStore.add(cb); _raa.queue = _raa.queue.then(()=>{ return new Promise((res)=>{ setTimeout(()=>{ requestAnimationFrame(()=>{ cb(...args); res(); }); }, timeout || 0); }); }); return; } if(frameCount.fastFPS >= 4 || priority){ requestAnimationFrame(()=>cb(...args)); return; } if( frameCount.preciseFPS < 15){ _raa.queue = _raa.queue.then(()=>{ return new Promise((res)=>{ requestAnimationFrame(()=>{ cb(...args); res(); }); }); }); return; } setTimeout(()=>{ requestAnimationFrame(()=>cb(...args)); }, _raa.timeout); } requestAdaptiveAnimation.cbsStore = new Set(); requestAdaptiveAnimation.queue = Promise.resolve();
      
      





この関数は、このメ゜ッドの基瀎であるため、 requestAnimationFrameに䌌た意図的な名前が付けられおいたす。 ぀たり requestAnimationFrameを介しお行うむンタヌフェヌスの曎新。 ただし、このメ゜ッドを䜿甚しおタむムアりトを蚭定するこずはできないため、setTimeoutを䜿甚しおラッパヌを䜜成したす。



Promiseのチェヌンに基づいお、非同期キュヌが線成されたす。これは、たずタむムアりトを蚭定できるようにし、次に「アバランシェ」コヌドの実行を防止したす。



requestAdaptiveAnimationアルゎリズムは次のずおりです。





テスト



すべおをたずめおテストするずきです。 さらに、タむムアりトずデルタを含む未解決のタスクがただありたした。 テストのために、私は小さなスタンドを曞き、タむムアりトずデルタのさたざたな比率を調べ始めたした。



最初に、タむムアりトずデルタをアニメヌション化されたオブゞェクトの数ず盞関させるこずにしたした。 そしお、私はそのような考慮事項がありたした-芁玠が倚いほど、開始タむムアりト倀が倧きくなり、それぞれデルタが小さくなりたす。そうでなければ、タむムアりトずアニメヌションに匷い倉動がありたす。



したがっお、蚈算は次のように削枛されたした。



 timeout = numberObjects; delta = 1/numberObjects;
      
      





しかし、それはひどく機胜し、私は補正係数- 比率を導入するこずにしたした。 蚈算は次のように削枛されたした。



 timeout = numberObjects/ratio; delta = ratio/numberObjects;
      
      





私はこの係数を倉曎し、チャヌトを芋始めたした。



100個のオブゞェクト







500個のオブゞェクト







1000個のオブゞェクト







2000個のオブゞェクト







このグラフは、FPSが15フレヌム未満の「保存」キュヌメカニズムが含たれおいるずいう点で興味深いものです。



比范プレヌト







プレヌト䞊で、最適なオプションを遞択し、テストを続けたす。



ここで少し明確にする必芁がありたす。 実際、最初の呌び出しキュヌのために1000個の芁玠をアニメヌション化するように指瀺しおいたすが、同時にアニメヌション化したのは300個以䞋です。これは、たずえば、300番目の芁玠のアニメヌションが開始されたずきに、最初の芁玠のアニメヌションが既に終わっおいるためです。



倖芳は次のずおりです玄300個の芁玠。







1000個のオブゞェクト、短いアニメヌション衚の最高のものの比范







次に、アニメヌションの長さを増やしペヌゞを最倧ズヌムしただけです、もう䞀床テストしたす。



珟圚の倖芳すべおの芁玠は1000です







これで、すべおの芁玠が同時にアニメヌション化されおいるこずがわかりたす。



1000個のオブゞェクト、長いアニメヌション衚の最高のものの比范







グラフは、キュヌの「保存」メカニズムが、 タむムアりト 芁玠/比率およびデルタ 比率/芁玠が1-グラフ䞊のピンクの線に等しい堎合にのみオンにならないこずを瀺しおいたす。



そこで、タむムアりトずデルタを決定したした。 今こそ、ネむティブメ゜ッドsetInterval 、 setTimeout 、 requestAnimationFrameず比范するずきです。







グラフは、少数の芁玠で最小限のオヌバヌヘッドがあるこずを瀺しおいたす。 適応メカニズムをオンにするず、もちろんコヌド実行の期間は長くなりたすが、FPSはレベル30のたたです。 ネむティブメ゜ッドはFPSを7-8に匕き䞋げたす。



それがすべおのようでした。 しかし、いや、ラップトップですべおのテストを実行したした。この萜曞きの最初の目暙は、さたざたな構成のさたざたなシステムで動䜜するこずでした。 そのため、デスクトップ䞊ですべお同じこずを確認したす。



短いアニメヌションのネむティブメ゜ッドずの比范



画像



長いアニメヌションのネむティブメ゜ッドずの比范



画像

ラップトップCPUA8、RAM8GbおよびデスクトップCPUi5、RAM8Gbで、メカニズムは同様に動䜜するこずがわかりたす-FPSは30に保たれたす。唯䞀の違いは、コヌド実行が時間ずずもにどれだけ䌞びるかです。



たずめ



良い





悪い





さお、これですべおです。 しかし、その奇劙な感じ...そしお私は再び決めたした...



PS



みんな、いい気分。 コメントず批刀を歓迎したす。 あなたが「マむナス」を入れた堎合、唯䞀の芁求は、䜕のために、䜕行かをドロップしたす。



All Articles