テキストの処理や描画を行う GDI32.dll の関数を複数の CPU 上で並行して実行すると時間がかかる

現象

テキストの処理や描画を行う GDI32.dll の関数を、繰り返し大量に実行するスレッドを用意します。
1 回の関数呼び出しにかかった時間を以下の 2 つのケースで計測すると、ケース 2 よりもケース 1 の方が、1 回の関数呼び出しに時間がかかります。
  1. CPU が複数搭載されているコンピューターにおいて、それぞれ別の CPU 上でこのスレッドを 4 つ開始します。
  2. CPU が複数搭載されているコンピューターにおいて、1 つの CPU 上で 4 つすべてのスレッドを開始します。

原因

GDI32.dll は、テキストの処理や描画を行う以下のような関数を提供しています。

  • ExtTextOut 関数
  • GetCharWidth32 関数
  • PlayEnhMetaFile 関数

これらの関数内部では、テキストの処理や描画に必要な情報を得るために、Win32 の GDI が管理している非公開のグローバル データに対してアクセスを行うことがあります。
そして Win32 の GDI 内部では、複数のスレッドから同時にアクセスされることがないように、このグローバル データを排他制御しています。

ところが、複数の CPU 上でこれらの関数が並行して大量に実行されると、排他制御されているグローバル データへのアクセス待ちが大量に発生するため、それぞれの関数呼び出しに時間がかかることがあります。

また、前述した 3 つの関数だけでなく、Win32 の GDI が管理しているグローバル データに対してアクセスを行う GDI32.dll の関数すべてで、同じ理由により時間がかかることがあります。

なお、マルチスレッドを利用して PlayEnhMetaFile 関数などメタファイルを扱う API を実行した場合、正しく印刷できない場合や例外が発生する可能性があります。こちらは、GDI32.dll において メタファイルを扱う API がスレッドセーフでないために発生します。 

回避策

以下のいずれかの方法でこの問題を回避することが可能です。
  • 複数の CPU 上でそれぞれの関数が並行して大量に実行されることがないように、各スレッドまたはプロセスを実装してください。
  • それぞれの関数を呼び出すスレッドまたはプロセスができるだけ同じ CPU 上で実行されるように、各スレッドまたはプロセスに CPU アフィニティ マスクを設定して実行してください。

状況

マイクロソフトでは、この問題をこの資料の対象製品として記載されているマイクロソフト製品の問題として認識しています。
プロパティ

文書番号:2864443 - 最終更新日: 2016/09/29 - リビジョン: 1

フィードバック