.NETパフォーマンス:本物のジェダイの秘Tri

本物のJedi .NET、パフォーマンスの第一人者、複数のMicrosoft MVP、 DotNext会議の常任講演者 Sasha Goldsteinを紹介する必要がありますか? おそらくそれだけの価値はありません。 そして、突然立った場合は、 こちらをご覧ください







会話の中で、Sashaは.NETおよび.NET Core開発者向けの専門的なヒントを共有しています。 アプリケーションのプロファイリングとデバッグの際に何を探すべきか、使用するツールについて説明します。



-Sasha、.NETのパフォーマンスに関する記事とヒントがたくさんあります。 どこからでも例外をスローし、連結の代わりにStringBuilderを使用することは望ましくないという事実から始まり、低レベルの最適化で終わります。 しかし、.NETは絶えず進化しており、新しい機能と新しい問題があります。 最新の.NET 4.7の場合、コードパフォーマンスの最適化に関する実用的なアドバイスはありますか?



Sasha Goldstein:多くの人は、文字列の連結、例外、またはボックス化/ボックス化解除に関する「明らかな」ヒントにすでに精通していますが、新しいAPIまたはより高いレベルのAPIが表示されると、パフォーマンスに関する誤解が生じます。 多くのコードベースでのLINQの過剰な使用が心配です。 LINQから何年も経っており、通常のループを使用してほとんどのLINQクエリを10倍高速化できることを示す多くのデータがありますが、多くの場合、パフォーマンス重視のコードでLINQを使用することが多くあります。 明確にします:全体としてLINQに反対することはありませんが、そのようなソリューションは、サイクルが100万回実行されるとうまく機能しません。



みんなの頭の中に入れられなかったもう1つのことは、メモリを過剰に割り当てる危険性です。 .NETのガベージコレクターにはいくつかの改善点がありますが、特に古い世代で死んでしまうオブジェクトの場合、割り当てすぎるとなお役に立ちません。 一部のツールでは簡単になりますが、メモリ割り当ての問題に注意を払うように全員に教えることは困難です。 たとえば、Roslynのヒープ割り当てアナライザーまたはdotMemoryなどのプロファイラー。



最後にしたいのは、コンテナテクノロジのために、ワークステーションだけでなくサーバーアプリケーションにとって最小メモリと起動時間が重要になる時期に戻っているということです。 1つの物理ホストでDockerで実行されているマイクロサービスのインスタンスを300個パックする場合、メモリ管理に細心の注意を払い、不要な依存関係を避け、不要な作業を取り除く必要があります。 標準のランタイムとして16コアの32ギガバイトサーバーに慣れると、サービスを「--memory 256m --cpus 0.25」に圧縮しようとします。 ちなみに、「 サーバーレス 」テクノロジーでも同じ問題に直面していますが、デプロイメントユニットは非常に小さいため、通常はリソースの制約を受けやすくなります。



-今日、NETはWindows専用ではありません。 Monoおよび.NET Coreが登場しました。 特にLinuxのネイティブアプリケーションと比較して、パフォーマンスの面でこれらのツールはどれほど優れているか教えてください。



Sasha Goldstein: Monoはかなり前から存在していましたが、正直なところ、私はMonoを戦闘プロジェクトで使用していません。 彼は成熟した価値のあるランタイムを持っていることを知っていますが、Monoは私が期待していたという認識を受け取りませんでした。 いずれにせよ、私はその有効性に関する勧告を与えることはできません。



今日、.NET Coreについて詳しく説明します。 Linuxでは、.NET CoreはWindowsとほぼ同様のスタックとコードベースを使用します。 コンパイラはWindowsと同じ最適化を実行し、AOT(.NET CoreではCrossgenと呼ばれます)をコンパイルすることでJITコンパイルを回避する方法があります。いくつかの機能を備えたガベージコレクターなどがあります。 質問を提起できる.NET Coreの唯一の部分はPAL(Platform Adaptation Layer)であると言えます。これは、実際にはWindowsバージョンとはまったく異なるコードベースを持つ唯一の場所だからです。 実際、Windowsの同様のAPIと比較して、PALにはパフォーマンスの問題があり、主にLinux APIの誤用または予期しないパフォーマンス動作を伴うLinux APIの使用に関するものでした。 多くの開発者が実稼働環境で.NET Coreを使用し、これらの問題に遭遇するため、これらのしわは時間の経過とともに自然に滑らかになります。



成熟するまでに時間がかかる興味深い開発はCoreRT(Windows用.NET Native)です。これは最終的に管理対象アプリケーションの作成を試み、外部依存関係がなく、JITをまったく生成しません。 分離の簡素化(共有なし、インストールなし、依存関係管理なし)に加えて、CoreRTは不要なコードを振り落とすことで起動時間を短縮し、メモリを削減します。 これにより、.NETをネイティブLinuxアプリケーションにさらに近づけることができます。



-.NETの生産性向上に関するほぼすべての記事で言及されている主なヒントの1つは、プロファイラーの使用です。 DotNext 2017 Moscowでは、Linux用の.NET Coreアプリケーションのデバッグとプロファイリングについて説明します。 Visual Studioは.NET Coreのデバッグとプロファイリングをどのように行っていますか?



Sasha Goldstein: Visual Studioは現在、Linux上の.NET Coreアプリケーションをプロファイルするための機能を提供していません。 Windowsツールキットを使用して.NET Coreアプリケーションをプロファイリングすることはできません。Linuxでプロファイリングを行う必要があり、Windowsで結果を実際に分析する唯一の方法(これになりやすい場合)は、Visual Studio Profilerよりも少し洗練されたツールであるPerfViewを使用することです



同様に、Visual StudioはLinux上の.NET Coreアプリケーションカーネルのダンプを開くことができません。 Visual StudioでWindowsダンプファイルを開くときに使用するリッチデバッガーデータは、コアダンプには存在しません。 Visual Studio 2013で導入されたメモリ分析機能は、コアダンプでは機能しません。 実際、WindowsにはLinuxからアプリケーションダンプを開くことができるツールはありません。 これを行うには、現在はるかに多くの機能を提供しているLinuxツールを使用する必要があります。



すでに気づいたように、私はそれがあまり好きではありません。 私はとても驚いているとは言えません。 MSがプラットフォームの開発に費やしたすべての努力と、すでにバージョン2.0を使用しているという事実にもかかわらず、プロファイリングとデバッグのための優れたツールはまだありません。 Linuxでの発表から3年後、平均的な開発者が使用できる適切なプロファイラーやデバッガーはありません。 これは重大な制限であり、個人的に私を悩ますことがあります。 実稼働アプリケーションをデバッグまたはプロファイリングしようとすると壁と戦うことがわかっているので、正直に言って、クライアントがLinuxで.NET Coreに切り替えることはお勧めしません。



-Linux開発の世界では、perfとftraceが人気があり、アプリケーションのパフォーマンスを分析する良い機会を提供します。 .NET Coreでのデバッグに役立ちますか? ネイティブLinuxアプリケーションと.NET Coreのアプリケーションでperfまたはftraceを使用する場合、何か違いはありますか?



サーシャ・ゴールドスタイン:はい。 Linuxでの.NET Coreアプリケーションのプロファイリングの公式ワークフローは、perfに基づいています。 Microsoftは、perfを実行し、パフォーマンスデータを収集し、それを単一のファイルに結合し、PerfViewを使用してWindowsで開くことができる「perfcollect」と呼ばれるBashスクリプトを提供しています。 この話のばかげたことを一時的に無視し、このプロセスがどのように機能するかについて話しましょう。



Perfは、さまざまな操作モードを備えた多目的ツールです。 特に、プロファイラーとして使用できます。 さまざまなシステムイベントに接続し、これらのイベントが発生したときにスタックトレースを収集してから、このトレーススタックのアドレスをメソッド名にマッピングします。 また、いくつかの視覚化機能もありますが、多くの場合、たとえばFlame Graphsに置き換えられます。 現在、perfは単なるCPUプロファイラーではありません。プロセスにアタッチしたり、ミスイベントをキャッシュしたり、ディスクイベントを読み書きしたり、スケジューラーやその他の数千の追加の静的および動的イベントによってコンテキストスイッチにアタッチしたりできます。 Windowsに最も近いのはETWツール(PerfView、Windows Performance Recorderなど)ですが、perfは動的ツール(カーネルまたはユーザースペースライブラリの任意の関数にアタッチ)を提供できます。 ETWはできません。



.NET Coreアプリケーションにperfを使用する場合、克服しなければならない問題が1つあります。 アドレス変換に関連しています。 perfがスタックトレースをキャプチャするとき、スタック上のリターンアドレスをメソッド名にマッピングできる必要があります。



.NET CoreはJITコンパイルを使用するため、このマッピングを実行できる静的な場所はありません(デバッグ情報、シンボル、呼び出し方に関係なく)。 この場合のperfの動作方法は、ターゲットアプリケーションがメソッドアドレスの名前へのマッピングを含む/tmp/perf-$PID.mapという単純なテキストファイルを書き込むことを期待することです。



実際、.NET Coreはこの規則をサポートしています。COMPlus_PerfMapEnabled環境変数を1に設定すると、JITはメソッドがコンパイルされるたびにこのテキストファイルに書き込み、perfはこの情報を使用してアドレスを正常にマップできます。 事前にこれを行う必要があるのは少し残念です-環境変数を設定しておらず、既に実行中のプロセスのプロファイルを作成したい場合は不運です-しかし、たとえばNode.jsも機能するので、これは多かれ少なかれ受け入れられると思います。



ストーリーには別の工夫があります。今回はAOTでコンパイルされたビルドです。 AOTコンパイルにCrossgenを使用する場合(.NET Coreは、ディストリビューションのパッケージマネージャーから取得する.NET Coreパッケージの一部のアセンブリでそれを使用します)、これらのアセンブリのデバッグ情報を取得する別の方法が必要です。



Crossgenでコンパイルされたアセンブリでこれを指定すると、Crossgenツール自体がこの情報を生成できます。 これまでのところ、いいですか? そうでもない。 まず、Crossgenは.NET Coreと共にインストールされないため、ソースからCoreCLRをコンパイルして取得するか、バカなNuGetリカバリトリックを使用する必要があります。 第二に、Crossgenは、perfが期待するものと互換性のない形式でデバッグ情報を提供するため、出力をフォーマットしてメインペフトマップファイルと組み合わせる必要があります。 3番目に、現在、perfは、Crossgenのように、ディスクに書き込まれたメモリパーティションのperf-mapsファイルの表示をサポートしていません。 したがって、これらのアセンブリの適切なperf-mapを取得できたとしても、perfはそれらを無視します。 幸いなことに、この場合でも機能する他のツールがあります。



ところで、.NET Coreプロセスを「ライトアップ」して動作させるツールは他にもたくさんあります。 DotNextレポートでは、perfをBCCのBPFベースのツールと共に使用します。 数か月前に、BPFでのJVMプロファイリングに関するJPoint会議でBPFとBCCについて話し合いました。



-LTTngについて質問があります。 公式文書によると、LTTngではパフォーマンスが最前線にあります。 .NET Coreアプリケーションをトレースする場合、このルールは維持されますか? トレースポイントまたはkprobesを使用する場合、LTTngの制限はありますか?



Sasha Goldstein: 「and」にドットを付けましょう。 LTTngは、2つの動作モードを持つ強力なLinuxトレースフレームワークです。 トレースポイントに接続できるカーネルモジュールがあります。これは、カーネル全体に散在する静的に定義されたトレース位置です:スケジューライベント、ディスクアクセス、プロセス実行など。 さらに、LTTngには、ユーザーアプリケーションのイベントを追跡するために使用できるユーザー空間ライブラリがあります。これは、Linuxで.NET Coreが使用するものです。 どちらの場合も、LTTngは共有メモリバッファー、コンパクトなバイナリ形式、ディスクへの高速書き込みにより、高いイベントレートに最適化されています。



ご存知かもしれませんが、Windows上の.NETには、パフォーマンスのプロファイルとシステムの動作の理解に使用できる多くのETWイベントがあります。 これらには、GCイベント、アセンブリ、JITコンパイル、オブジェクト作成などが含まれます。 Linuxでは、もちろんETW(Windowsのイベントトレース)は利用できないため、MicrosoftはLTTngの使用を選択しました。 同じイベントを取得しますが、それらはETWではなくLTTngを介して生成されますが、いくつか注意点があります。



最初に、環境変数を設定する必要があります(COMPlus_EnableEventLog = 1)。 これが行われない場合、LTTngはイベントをまったく生成しません。 第二に、LTTngはユーザー空間イベントのスタックトレースをサポートしていません。 これは、GCイベントをインターセプトできますが、それらが呼び出された呼び出しスタックがないことを意味します。 アセンブリロードイベントをキャッチできますが、このアセンブリをロードしたコードがわかりません。 これらは、実際のトラブルシューティングシナリオでこれらのイベントの使用を制限する非常に痛い瞬間です。



-Linux用のLLDBデバッガーは、Windows用のWinDbgと非常に似ています。 彼にとっては、SOS拡張機能も利用可能で、マネージコードをデバッグできます。 現在、.NET Codeアプリケーションのデバッグにどの程度適用できますか?



Sasha Goldstein: LLDBは非常に強力なデバッガーであり、Microsoftはlibsosplugin.soライブラリを提供しています。これはLinux用のSOS.dllのバージョンです。 ほぼ同じコマンドセットを同じセマンティクスで提供します。これは、SOSに精通している場合に適しています(ただし、WinDbgとは大幅に異なる独自のLLDBコマンドを学習する必要があります)。 しかし、これはこの会話のトピックではありませんか? libsospluginを使用したLLDBの場合、次の障害が発生します。





-高品質のマルチスレッドアプリケーションの開発は困難な作業です。 しかし、さらに複雑なのは、そのようなアプリケーションのボトルネックとエラーの検索です。 開発者は、.NET用のマルチスレッドアプリケーションのデバッグとプロファイリングに何を使用できますか?



サーシャ・ゴールドスタイン:あなたが好きな限りそれについて話すことができるので、簡単に説明します。 最新のツールを使用して自動化できるいくつかの重要な方法があります。





-DotNext 2017に向けてどのようなレポートを準備しましたか?



Sasha Goldstein:モスクワで、Linuxでの.NET Coreアプリケーションのプロファイリングとデバッグについて説明します。 これは何ヶ月にも及ぶ研究の結果です。 .NET Coreアプリケーションの一般的なパフォーマンスの問題のライブデモと共に、上記のツールと方法のいくつかについて説明します。 特別なボーナスとして、これらのツールのいくつかを使用して、Dockerコンテナで実行されている.NET Coreアプリケーションのプロファイルを作成する方法を紹介します。 私のデモはすべてGitHubで入手できるため、会議後にデモを試すことができます。



そして、完全な没入を望む人のために、モスクワは11月11日に、 製品のパフォーマンス問題を監視および解決するためのツールとアプローチに特化した8時間の実践トレーニング「 .NETアプリケーションの生産パフォーマンスとトラブルシューティング 」を開催します。






私たちと同様に.NETのインテリアが好きな人は、30人以上のスピーカーが.NETプラットフォームの現在と未来、パフォーマンスの最適化とマルチスレッド、内部のプレゼンテーションを行うDotNext 2017モスクワ会議での他の専門家のスピーチに興味を持つかもしれません.NETおよびCLRプラットフォームデバイス。.NETコードのプロファイリングとデバッグについて。



All Articles