
著者はGoogleでのChromeのパフォーマンスの最適化に取り組んでいます-約 あたり
2017年の夏、Windowsのパフォーマンスの問題に苦労しました。 プロセスの終了が遅く、シリアル化され、システム入力キューがブロックされたため、Chromeのアセンブリ中にマウスカーソルが何度もフリーズしました。 主な理由は、プロセスの最後に、WindowsがGDIオブジェクトの検索に多くの時間を費やし、システムグローバルユーザー32の重要なセクションを押さえることでした。 これについては「24コアプロセッサですが、カーソルを移動できません」という記事で説明しました。
マイクロソフトはバグを修正し、ビジネスに戻りましたが、バグが戻ったことが判明しました。 入力が頻繁にフリーズする、LLVMテストの遅い動作について苦情がありました。
しかし、実際には、バグは再発しませんでした。 その理由は、コードの変更です。
2017年号

Microsoftのパッチは、GDIオブジェクトのないプロセスでこれらの関数を呼び出さないことでした。 詳細はわかりませんが、Microsoftのパッチは次のようなものだったと思います。
+ if (IsGUIProcess())
+ NtGdiCloseProcess();
– NtGdiCloseProcess();
つまり、プロセスがGUI / GDIプロセスでない場合は、GDIクリーンアップをスキップします。
すぐに作成および終了するコンパイラおよびその他のプロセスはGDIオブジェクトを使用しなかったため、このパッチはUIのフリーズを修正するのに十分であることが判明しました。
2018年号
プロセスには、いくつかの標準GDIオブジェクトが実際に非常に簡単に割り当てられることが判明しました。 プロセスがgdi32.dllをロードすると、必要かどうかにかかわらず、GDIオブジェクト(DC、サーフェス、リージョン、ブラシ、フォントなど)を自動的に受け取ります(これらの標準GDIオブジェクトはタスクマネージャーに表示されないことに注意してください)プロセスのGDIオブジェクト間)。
しかし、それは問題ではないはずです。 つまり、コンパイラはなぜgdi32.dllをロードするのでしょうか? さて、user32.dll、shell32.dll、ole32.dll、または他の多くのDLLをロードすると、gdi32.dll(前述の標準GDIオブジェクト)が自動的に追加されることがわかりました。 また、これらのライブラリのいずれかを誤ってダウンロードすることは非常に簡単です。
LLVMは、 CommandLineToArgvW (shell32.dll)と呼ばれる各プロセス、およびSHGetKnownFolderPath (shell32.dll)と呼ばれることもある各プロセスをロードするときにテストします。 LLVMテストスイートは非常に多くのプロセスを生成するため 、最終的にプロセスの完了時にシリアル化され、2017年の場合よりも大幅に遅延して入力がフリーズします。
しかし、今回はブロッキングの主な問題について知っていたので、すぐに何をすべきかがわかりました。
まず、コマンドラインを手動で解析して CommandLineToArgvW呼び出しを削除しました 。 その後、LLVMテストスイートは、問題のあるDLLから関数を呼び出すことはほとんどありませんでした。 ただし、これがパフォーマンスに影響を与えないことは事前にわかっていました。 その理由は、残りの条件付き呼び出しでさえ常にshell32.dllをプルするのに十分であり、それが標準のGDIオブジェクトを作成するgdi32.dllをプルするためでした。
2番目の修正は、 shell32.dllの遅延ロードでした 。 遅延ロードとは、プロセスの開始時にロードする代わりに、関数が呼び出されたときに、ライブラリがオンデマンドでロードされることを意味します。 これは、shell32.dllとgdi32.dllがほとんど読み込まれないことを意味しますが、常にではありません。
その後、LLVMテストスイートは5倍ではなく1分間で5倍の速度で実行を開始しました。 また、開発マシンでマウスがフリーズすることはないため、テストの実行中に従業員は通常どおり作業できます。 これは、このようなささいな変更に対する非常識な加速であり、パッチの作成者は私の調査にとても感謝していたので、彼は私に企業ボーナスを提案しました。
時々、小さな変更が最大の結果をもたらすことがあります。 「ゼロ」をダイヤルする場所を知る必要があります 。
実行パスは受け入れられません

int main(int argc, char* argv[]) { if (argc < 0) { CommandLineToArgvW(nullptr, nullptr); // shell32.dll, pulls in gdi32.dll } }
そのため、コードが実行されない場合でも、関数呼び出しを削除するだけで、場合によってはパフォーマンスを大幅に改善できます。
病理学の再現

> ProcessCreatetests.exe
Process creation took 2.448 s (2.448 ms per process).
Lock blocked for 0.008 s total, maximum was 0.001 s.
Process destruction took 0.801 s (0.801 ms per process).
Lock blocked for 0.004 s total, maximum was 0.001 s.
> ProcessCreatetests.exe -user32
Testing with 1000 descendant processes with user32.dll loaded.
Process creation took 3.154 s (3.154 ms per process).
Lock blocked for 0.032 s total, maximum was 0.007 s.
Process destruction took 2.240 s (2.240 ms per process).
Lock blocked for 1.991 s total, maximum was 0.864 s.
楽しみのために深く掘り下げる
問題をより詳細に研究するために使用できるいくつかのETWメソッドについて考え、すでにそれらを書き始めました。 しかし、私はそのような不可解な振る舞いに出くわしたので、別の記事を書くことにしました。 この場合、Windowsの動作はさらに奇妙になります。
シリーズの他の記事:
- Windowsの速度低下、パート0: VirtualAllocの任意の減速
- Windowsの速度を下げる、パート1: ファイルアクセス
- Windowsの速度を下げる、パート2: プロセスの作成
- Windowsの速度を下げる、パート3:これ
文学
- 最初のUI一時停止レポート: 「24コアプロセッサですが、カーソルを移動できません」
- 問題の理解につながる次の記事: 「* Windows *このロックを保持する機能」
- Gmail、ASLR v8ワーカー、CFGメモリ割り当てポリシー、および低速WMIスキャンの間の相互作用による別の UIブロックに関する記事: 「24コアCPU、ただしメールを入力できません」
- gdi32.dllをロードするコンパイラは奇妙に思えますが、 VC ++が場合によっては使用していた mshtml.dllをコンパイラがロードすることはさらに奇妙です
- 記事「ゼロを取得する場所を知る」で説明されているように、時には数週間の研究が小さなながらも重大な変化をもたらすことがあります。
- ProcessCreateTestsとETWを使用してバグ修正を検証するビデオ
- コマンドラインの手動解析によるLLVMの最初の変更
- shell32.dllの読み込み遅延を使用したLLVMの2番目の修正