Go FAQ から:
マルチゴルーチンプログラムが複数の CPU を使用しないのはなぜですか?
GOMAXPROCS シェル環境変数を設定するか、ランタイム パッケージの同様の名前の関数を使用して、ランタイム サポートが複数の OS スレッドを利用できるようにする必要があります。
並列計算を実行するプログラムは、GOMAXPROCS の増加の恩恵を受けるはずです。ただし、同時実行は並列処理ではないことに注意してください。
(2015 年 8 月 28 日更新: Go 1.5 は、GOMAXPROCS のデフォルト値をマシンの CPU の数と同じにするように設定されているため、これはもう問題にはなりません)
と
GOMAXPROCS > 1 を使用するとプログラムが遅くなることがあるのはなぜですか?
これは、プログラムの性質によって異なります。本質的にシーケンシャルな問題は、ゴルーチンを追加しても高速化できません。問題が本質的に並列である場合にのみ、同時実行は並列処理になります。
実際には、計算よりもチャネルでの通信に多くの時間を費やすプログラムは、複数の OS スレッドを使用するとパフォーマンスが低下します。これは、スレッド間でのデータ送信にはコンテキストの切り替えが必要であり、これにはかなりのコストがかかるためです。たとえば、Go 仕様のプライム シーブの例は、多くのゴルーチンを起動しますが、重要な並列性はありません。GOMAXPROCS を増やすと、速度が上がるよりも遅くなる可能性が高くなります。
Go のゴルーチン スケジューラは、必要なほど優れていません。将来的には、そのようなケースを認識し、OS スレッドの使用を最適化する必要があります。今のところ、GOMAXPROCS はアプリケーションごとに設定する必要があります。
要するに、Goに「すべてのコアを効率的に使用する」ことは非常に困難です。単純に 10 億個のゴルーチンを生成して GOMAXPROCS を増やすと、スレッド コンテキストが常に切り替えられるため、速度が上がるのと同じくらいパフォーマンスが低下する可能性があります。並列化可能な大規模なプログラムがある場合は、GOMAXPROCS を並列コンポーネントの数まで増やすと問題なく動作します。大部分が非並列プログラムに並列問題が埋め込まれている場合は、速度が向上するか、runtime.LockOSThread() などの関数を創造的に使用して、ランタイムがすべてを正しく分散するようにする必要がある場合があります (一般的に言えば、Go はばかげて広がるだけです)。現在、すべてのアクティブなスレッド間で無計画かつ均等にブロックされていないゴルーチン)。
また、GOMAXPROCS は使用する CPU コアの数です。それが NumCPU より大きい場合は、NumCPU に単純にクランプされると確信しています。GOMAXPROCS は厳密にはスレッド数と同じではありません。ランタイムが新しいスレッドを生成することを決定した正確な時期については 100% 確信が持てませんが、1 つのインスタンスは、runtime.LockOSThread() を使用するブロッキング ゴルーチンの数が GOMAXPROCs 以上の場合です。これは、コアよりも多くのスレッドを生成します。そのため、プログラムの残りの部分を正常に実行し続けることができます。
基本的に、GOMAXPROCS を増やして CPU のすべてのコアを使用するのは非常に簡単です。Go の開発のこの時点で、実際に CPU のすべてのコアをスマートかつ効率的に使用できるようにすることはまったく別のことであり、適切に処理するには多くのプログラム設計と仕上げが必要です。