数理最適化は計算集約型の作業です。このため、(仮想)ハードウェアの選択は、モデルやソルバーのパフォーマンスに影響を与える可能性があるため非常に重要です。コアやスレッドのような概念にまつわる関連用語はあいにく混乱を招くことがあります。この記事では、Gurobiの観点からこれらの言葉のいくつかを明確にしようとしています。
まず、「計算能力」(容量、供給)と「計算」(作業、需要)を区別しましょう。この視点から概念を見ることで、それぞれの側面の変化がパフォーマンスにどのように影響するかを理解しやすくなります。
計算能力
個人用のノートパソコン、クラウド内の仮想マシン、あるいはサーバーレス環境で作業しているかに関わらず、計算は最終的に物理的なハードウェア上で実行されます。具体的には、物理コアやCPUが、命令を順次実行するコンポーネントです。より詳しく見ると、1つの物理コアはさまざまな実行リソースで構成されています。実行エンジン自体のほかに、キャッシュやシステムバスインターフェースなどのリソースがあります。
実行エンジンがストールしている状況(例えば、データの取得待ちなど)があるかもしれません。ここでハイパースレッディングが登場します。最新のハードウェアとオペレーティングシステムは、1つの物理コアを2つ以上の仮想コアとして扱い、それぞれが命令の一連を実行できます。これは物理的なマシンの容量を実際に増やすわけではありませんが、ストールしている時間を無駄にすることなく、実行エンジンをより有効に活用する機会が生まれます。余計な混乱を生み出しがちなのは、仮想コアがしばしばスレッドと呼ばれることがある点で、この概念は次の節で紹介します。
計算
一般にソフトウェアを実行するとき、通常プロセスと呼ばれるものを開始します。Gurobiでも同様で、Pythonスクリプトを実行してGurobiを呼び出すと、そのすべてのロジックは単一のプロセス内で動作します。ソフトウェア内の命令は通常、順次実行されることが期待されます。しかし、複数コアのマシンが登場すると、利用可能な計算能力を最大限に活用するために複数の命令を同時に実行したいという欲求が生まれました。
ここでスレッドが導入されます。1つのプロセスにはメインスレッドがあり、これはプロセス開始時に実行される命令を表します。その命令の1つが、新しいスレッドを作成する要求であり、そのスレッドは独自の命令セットを持ちます。これがマルチスレッディングと呼ばれます。
Gurobiがスレッドを利用する理由はいくつかります。例えば、LPモデルや緩和問題を解くための内点法(バリア法)アルゴリズムの一部の計算は互いに独立しているため、うまく実行を並列化できます。同様に、MIPの分枝限定法において複数のノードを同時に検討することもあります。また、ルート緩和問題を解く複数の手法を同時に実行し、最初に見つかった解を使用することもあります。
スレッドスケジューリング
これで計算リソース(物理コアおよび仮想コアの形態)とそれらのリソースに対する需要(プロセス内のソフトウェアスレッドの形態)を定義しました。最後のピースは、これらのスレッドがどのようにコアに割り当てられるかです。これはオペレーティングシステムの責任です。
実際には、1つのGurobiプロセスに1つ以上のスレッドが存在する場合があります。正確な数はクライアントアプリケーションがThreadsパラメータを使って選択できます。オペレーティングシステムはこれらのスレッドをコアに割り当てます。理想的には各スレッドに専用のコアが割り当てられますが、常に可能とは限りません。
最後に
デフォルトでは、Gurobiが使用するスレッド数はマシンのコア数に基づいて自動的に計算されます。これは多くの場合に望ましい動作ですが、以下のような状況ではその動作から逸脱したい場合があります:
- ライセンスによっては、複数のモデルを同時に実行することがあり、それぞれのモデルが1つ以上のスレッドを必要とします。スレッド数が仮想コア数を超えないようにする必要があります。物理コア数を超えた場合にパフォーマンスが低下するかどうかは、テストによってのみ確認できます。
- 単一モデルを解く場合でも、利用可能なすべてのコアをGurobiに使わせることが必ずしも最良のパフォーマンスをもたらすわけではありません。スレッドは作業を同期する必要があります(例:情報交換、残作業の分配、決定的な動作の保証)。スレッド数が増えると既存および新規スレッド間の依存関係が増加し、ある時点からは、追加のオーバーヘッドが並列でより多くの作業を行う利点を上回ります。