ネットワーク ドライバーのパフォーマンス

送受信パスの長さを最小限に抑える

送受信パスはドライバーによって異なりますが、パフォーマンスの最適化のための一般的な規則がいくつかあります。

  • 共通パスを最適化します。 Kernprof.exe ツールには、必要な情報を抽出する Windows の開発者ビルドと IDW ビルドが用意されています。 開発者は、CPU サイクルを最も多く消費するルーチンを調べ、これらのルーチンが呼び出される頻度またはこれらのルーチンで費やされる時間を減らすように努める必要があります。

  • ネットワーク アダプター ドライバーが過剰なシステム リソースを使用しないように、DPC で費やされる時間を短縮します。これにより、全体的なシステム パフォーマンスが低下します。

  • デバッグ コードがドライバーの最終リリース バージョンにコンパイルされていないことを確認します。これにより、余分なコードの実行が回避されます。

データとコードをパーティション分割してプロセッサ間の共有を最小限に抑える

パーティション分割は、プロセッサ間の共有データとコードを最小限に抑えるために必要です。 パーティション分割により、システム バスの使用率が低下し、プロセッサ キャッシュの有効性が向上します。 共有を最小限に抑えるために、ドライバーライターは次の点を考慮する必要があります。

  • 逆シリアル化された NDIS ミニポート ドライバー の説明に従って、ドライバーを逆シリアル化されたミニポートとして実装します。

  • プロセッサごとのデータ構造を使用して、グローバルおよび共有データ アクセスを削減します。 これにより、統計カウンターを同期せずに保持できるため、コード パスの長さが短くなり、パフォーマンスが向上します。 人口動態統計の場合は、クエリ時に一緒に追加されるプロセッサごとのカウンターがあります。 グローバル カウンターが必要な場合は、スピン ロックの代わりにインターロック操作を使用してカウンターを操作します。 スピン ロックの使用を回避する方法については、以下の「ロック メカニズムを適切に使用する」を参照してください。

    これを容易にするために、 KeGetCurrentProcessorNumberEx を使用して現在のプロセッサを特定できます。 プロセッサごとのデータ構造を割り当てるときにプロセッサの数を決定するために、 KeQueryGroupAffinity を使用できます。

    アフィニティ マスクに設定されているビットの合計数は、システム内のアクティブなプロセッサの数を示します。 ドライバーは、プロセッサがオペレーティング システムの将来のリリースで連続して番号付けされない可能性があるため、マスク内のすべての設定ビットが連続していると想定しないでください。 SMP マシン内のプロセッサの数は 0 から始まる値です。

    ドライバーにプロセッサごとのデータが含まれている場合は、 KeQueryGroupAffinity 関数を使用してキャッシュ ラインの競合を減らすことができます。

偽共有の回避

偽共有は、プロセッサが互いに独立した共有変数を要求するときに発生します。 ただし、変数は同じキャッシュ ライン上にあるため、プロセッサ間で共有されます。 このような状況では、キャッシュ ラインは、その中の任意の変数にアクセスするたびにプロセッサ間を行き来し、キャッシュ フラッシュと再読み込みの増加を引き起こします。 これにより、システム バスの使用率が増加し、システム全体のパフォーマンスが低下します。

偽共有を回避するには、 NdisGetSharedDataAlignment を使用して、重要なデータ構造 (スピン ロック、バッファー キュー ヘッダー、単方向リストなど) をキャッシュ ライン境界に合わせます。

ロック 機構を適切に使用する

スピン ロックは、適切に使用しないとパフォーマンスが低下する可能性があります。 ドライバーは、可能な限りインターロック操作を使用して、スピン ロックの使用を最小限に抑える必要があります。 ただし、目的によってはスピン ロックが最適な場合もあります。 たとえば、ドライバーに戻って示されていないパケットの数の参照カウントを処理するときに、ドライバーがスピン ロックを取得する場合、インターロック操作を使用する必要はありません。 ネットワーク ドライバーの同期と通知 を参照してください。

ロック 機構を効果的に使用するためのヒントを次に示します。

  • リソース プールを管理するために、次のような NDIS の単方向リスト関数を使用します。

    NdisInitializeSListHead

    NdisInterlockedPushEntrySList

    NdisInterlockedPopEntrySList

    NdisQueryDepthSList

  • スピン ロックを使用する必要がある場合は、コードではなくデータのみを保護するために使用します。 共通パスで使用されるすべてのデータを保護するために 1 つのロックを使用しないでください。 たとえば、送信パスと受信パスで使用されるデータを 2 つのデータ構造に分割して、送信パスがデータをロックする必要がある場合に、受信パスが影響を受けないようにします。

  • スピン ロックを使用していて、パスが既に DPC レベルにある場合は、NdisDprAcquireSpinLock 関数とNdisDprReleaseSpinLock 関数を使用して、ロックを取得および解放するときに余分なコードを回避します。

  • スピン ロックの取得とリリースの数を最小限に抑えるには、次の NDIS RWLock 関数を使用します。

    NdisAllocateRWLock

    NdisAcquireRWLockRead

    NdisAcquireRWLockWrite

    NdisReleaseRWLock

64 ビット DMA の使用

64 ビット DMA ネットワーク アダプターが 64 ビット DMA をサポートしている場合は、4 GB の範囲を超えるアドレスの余分なコピーを回避するための手順を実行する必要があります。 ドライバーが NdisMRegisterScatterGatherDma を呼び出すときは、NDIS_SG_DMA_64_BIT_ADDRESS フラグを Flags パラメーターに設定する必要があります。

バッファーの適切な配置の確保

キャッシュ ライン境界でのバッファー配置により、あるバッファーから別のバッファーにデータをコピーするときのパフォーマンスが向上します。 ほとんどのネットワーク アダプター受信バッファーは、最初に割り当てられるときに適切に配置されますが、最終的にアプリケーション バッファーにコピーする必要があるユーザー データは、ヘッダー領域が消費されるため、正しく配置されません。 TCP データの場合 (最も一般的なシナリオ)、TCP、IP、およびイーサネット ヘッダーによるシフトにより、0x36 バイトのシフトが発生します。 この問題を解決するには、ドライバーが少し大きなバッファーを割り当て、0xA バイトのオフセットにパケット データを挿入することをお勧めします。 これにより、バッファーがヘッダーの 0x36 バイト単位でシフトされた後、ユーザー データが適切に配置されます。 キャッシュ ライン境界の詳細については、NdisMAllocateSharedMemory の「解説」セクションを参照してください。

スキャッター/ギャザー DMA の使用

NDIS スキャッター/ギャザー DMA は、物理メモリの連続しない範囲との間でデータを転送するためのサポートをハードウェアに提供します。 スキャッター-ギャザー DMA では、SCATTER_GATHER_LIST 構造体が使用されます。これには、SCATTER_GATHER_ELEMENT 構造体の配列と配列内の要素の数が含まれます。 この構造体は、ドライバーの送信関数に渡されるパケット記述子から取得されます。 配列の各要素は、物理的に連続するスキャッター-ギャザー領域の長さと開始物理アドレスを提供します。 ドライバーは、データの転送に長さとアドレスの情報を使用します。

DMA 操作にスキャッター-ギャザー ルーチンを使用すると、マップ レジスタが使用された場合と同様に、これらのリソースを静的にロックダウンしないことで、システム リソースの使用率を向上させることができます。 詳細については、「NDIS スキャッター/ギャザー DMA」を参照してください。

ネットワーク アダプターが TCP セグメント化オフロード (Large Send Offload) をサポートしている場合、ドライバーは、TCP/IP から取得できる最大バッファー サイズを MaximumPhysicalMapping パラメーター (NdisMRegisterScatterGatherDma 関数内) に渡す必要があります。 これにより、ドライバーがスキャッター-ギャザー リストを作成し、バッファーの割り当てとコピーの可能性を排除するのに十分なマップ レジスタがあることを保証します。 詳細については、次のトピックを参照してください。

受信側スロットルのサポート

マルチメディア アプリケーションでのメディア再生中の中断を最小限に抑えるには、NDIS 6.20 以降のドライバーは、受信割り込みの処理で受信側スロットル (RST) をサポートする必要があります。 詳細については、以下を参照してください:

NDIS 6.20 の受信側スロットルミニポートドライバを NDIS 6.20 に移植するために必要な変更の概要における「送信および受信コード パス」