Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 11721] ファイルの概要

このコミットで変更されている src/cmd/dist/unix.c ファイルは、Go言語のビルドシステムの一部である cmd/dist ツールに属しています。cmd/dist は、Goのソースコードからコンパイラ、リンカ、標準ライブラリなどのツールチェイン全体をビルドするために使用される、Goプロジェクトのブートストラッププロセスにおける重要なコンポーネントです。特に unix.c は、Unix系システム(Linux, macOSなど)におけるビルドプロセスの低レベルな詳細、例えば外部コマンドの実行、並列タスクの管理などをC言語で実装しています。このファイルは、Goのビルドがどのように並列に実行され、システムリソースを効率的に利用するかを制御する役割を担っています。

コミット

このコミットは、Go言語のビルドツール cmd/dist において、ARMアーキテクチャ上でビルドを行う際のバックグラウンドタスクの最大数を削減することを目的としています。具体的には、ARMホスト上でGoをビルドする際に、並列で実行されるコンパイルやリンクなどのタスク数を1に制限することで、リソースが限られた環境でのビルドの安定性と成功率を向上させます。

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/119917375bd0ecbc4f5631e0e35958a578bebd8c

元コミット内容

commit 119917375bd0ecbc4f5631e0e35958a578bebd8c
Author: Shenghou Ma <minux.ma@gmail.com>
Date:   Wed Feb 8 16:26:00 2012 -0500

    cmd/dist: redure max background tasks nr. when building on ARM
    
    R=rsc
    CC=golang-dev
    https://golang.org/cl/5642063
---
 src/cmd/dist/unix.c | 6 +++++-\
 1 file changed, 5 insertions(+), 1 deletion(-)\

diff --git a/src/cmd/dist/unix.c b/src/cmd/dist/unix.c
index e687142e90..632ebbcdba 100644
--- a/src/cmd/dist/unix.c
+++ b/src/cmd/dist/unix.c
@@ -147,6 +147,7 @@ static struct {
 	char *cmd;
 } bg[MAXBG];
 static int nbg;
+static int maxnbg = nelem(bg);\
 \
 static void bgwait1(void);\
 \
@@ -158,7 +159,7 @@ genrun(Buf *b, char *dir, int mode, Vec *argv, int wait)\
 	Buf cmd;\
 	char *q;\
 \
-\twhile(nbg >= nelem(bg))\
+\twhile(nbg >= maxnbg)\
 \t\tbgwait1();\
 \
 \t// Generate a copy of the command to show in a log.\
@@ -665,6 +666,9 @@ main(int argc, char **argv)\
 \t\t\tfatal(\"unknown architecture: %s\", u.machine);\
 \t}\
 \
+\tif(strcmp(gohostarch, \"arm\") == 0)\
+\t\tmaxnbg = 1;\
+\
 \tinit();\
 \txmain(argc, argv);\
 \tbfree(&b);\

変更の背景

この変更が行われた2012年頃のARMアーキテクチャは、現在のような高性能なマルチコアプロセッサが主流になる前であり、特に組み込みシステムや低消費電力デバイスでの利用が一般的でした。当時のARMプロセッサは、x86アーキテクチャのデスクトップ/サーバー向けプロセッサと比較して、一般的にCPUコア数やメモリ容量、I/O性能が限られていることが多かったため、並列処理能力が相対的に低い傾向にありました。

Goのビルドプロセス、特に cmd/dist は、複数のコンパイルタスクやリンクタスクを並列に実行することでビルド時間を短縮するように設計されています。これは、make -j コマンドのように、利用可能なCPUコア数に応じて並列度を調整する一般的なビルド最適化の手法です。しかし、リソースが限られたARM環境で過度に多くのバックグラウンドタスクを並列実行しようとすると、以下のような問題が発生する可能性がありました。

  1. メモリ不足: 各ビルドタスクが独立したプロセスとして実行されるため、多くのタスクが同時に走ると、利用可能な物理メモリを使い果たし、スワップが発生したり、プロセスが強制終了されたりする。
  2. CPU飽和: コア数が少ない環境で多くのタスクがCPUリソースを奪い合うことで、コンテキストスイッチのオーバーヘッドが増大し、かえってビルド時間が長くなる、あるいはシステム全体の応答性が著しく低下する。
  3. I/Oボトルネック: ストレージI/Oが遅い環境(特にSDカードなど)では、多数の並列タスクが同時にディスクアクセスを行うことで、I/Oキューが飽和し、ビルドが停滞する。

これらの問題を回避し、ARM環境でのGoのビルドをより安定して成功させるために、並列タスクの最大数を明示的に制限する必要がありました。このコミットは、ARMホストでのビルド時に maxnbg (最大バックグラウンドタスク数) を1に設定することで、これらのリソース制約による問題を緩和し、ビルドの信頼性を高めることを目的としています。

前提知識の解説

cmd/dist と Go のビルドシステム

cmd/dist は、Go言語のソースコードからGoツールチェイン(コンパイラ、アセンブラ、リンカ、標準ライブラリなど)をビルドするための内部ツールです。GoのソースコードはGo自身で書かれている部分が多く、Goのビルドには既存のGoコンパイラが必要となるため、ブートストラッププロセス(最初のコンパイラをビルドするプロセス)が重要になります。cmd/dist はこのブートストラップと、その後のGoのバージョンアップ時の再ビルドを管理します。

cmd/dist は、Goのソースツリー内の様々なパッケージをコンパイルし、それらをリンクして実行可能なバイナリを生成します。このプロセスは、複数の独立したコンパイルステップを含むため、並列処理が可能です。cmd/dist は内部的にシェルコマンドを実行し、その実行を管理する機能を持っています。

ARMアーキテクチャ

ARM (Advanced RISC Machine) は、主にモバイルデバイス、組み込みシステム、IoTデバイスなどで広く使用されているRISC (Reduced Instruction Set Computer) ベースのプロセッサアーキテクチャです。低消費電力と高い電力効率が特徴であり、スマートフォンやタブレットの普及とともにその重要性が増しました。

2012年頃のARMプロセッサは、現在のような高性能なマルチコア構成(例: ARM Cortex-A72, A76など)が一般的になる前であり、シングルコアまたはデュアルコアの構成が多く、デスクトップPCやサーバー向けのx86プロセッサと比較して、一般的に処理能力やメモリ帯域幅が限られていました。そのため、多数の並列処理を必要とする重いビルドタスクを実行する際には、リソースの制約が顕著になることがありました。

並列ビルドとリソース管理

ソフトウェアのビルドプロセスでは、複数のソースファイルを同時にコンパイルしたり、複数のモジュールを並行して処理したりすることで、全体のビルド時間を短縮することがよく行われます。これは「並列ビルド」と呼ばれ、make -jN (N個のジョブを並列実行) のように、ビルドツールが提供する機能を利用します。

並列ビルドは、利用可能なCPUコアを最大限に活用することで効率を高めますが、同時にシステムのリソース(CPU、メモリ、I/O)を大量に消費します。リソースが限られているシステムで並列度を高く設定しすぎると、以下のような問題が発生します。

  • スラッシング: メモリが不足し、OSが頻繁にディスクとメモリ間でデータをスワップする状態。これによりI/Oがボトルネックとなり、パフォーマンスが著しく低下する。
  • コンテキストスイッチのオーバーヘッド: 多数のプロセスやスレッドがCPUを奪い合うことで、OSがタスクの切り替えに費やす時間が増え、実効的な処理能力が低下する。
  • ディスクI/Oの競合: 複数のプロセスが同時にディスクにアクセスしようとすることで、ディスクの読み書きが遅延し、ビルドが停滞する。

このコミットは、ARM環境におけるこれらのリソース制約を考慮し、並列ビルドの度合いを意図的に制限することで、ビルドの安定性を確保しようとするものです。

gohostarch

gohostarch はGoのビルドシステム内で使用される環境変数または内部変数で、Goツールチェインが動作しているホストマシンのアーキテクチャを示します。例えば、amd64 (x86-64), arm, arm64, 386 (x86) などがあります。Goのビルドプロセスでは、この gohostarch の値に基づいて、特定のアーキテクチャに特化した処理を行うことがあります。このコミットでは、gohostarch"arm" である場合にのみ、並列タスク数の制限を適用しています。

技術的詳細

このコミットは、src/cmd/dist/unix.c 内のバックグラウンドタスク管理ロジックを変更しています。

  • bg[MAXBG]: bg は、バックグラウンドで実行されるコマンドの情報を保持するための構造体の配列です。MAXBG は、この配列の最大サイズ、つまり同時に実行できるバックグラウンドタスクの理論上の最大数を定義しています。
  • nbg: 現在実行中のバックグラウンドタスクの数を追跡する変数です。
  • nelem(bg): これはC言語で配列の要素数を計算するための一般的なマクロ(または関数)です。sizeof(bg) / sizeof(bg[0]) のように実装され、bg 配列が保持できる最大タスク数を返します。
  • bgwait1(): この関数は、少なくとも1つのバックグラウンドタスクが完了するまで待機します。これは、nbgmaxnbg (または変更前は nelem(bg)) に達した際に呼び出され、新しいタスクを開始する前にリソースを解放する役割を果たします。
  • genrun(): この関数は、新しいコマンドをバックグラウンドで実行する際に呼び出されます。この関数内で、nbg が最大値に達しているかどうかをチェックし、達している場合は bgwait1() を呼び出して待機します。
  • gohostarch: 前述の通り、ホストのCPUアーキテクチャを示す文字列です。
  • strcmp(s1, s2): C標準ライブラリの関数で、2つの文字列 s1s2 を比較します。文字列が等しい場合は0を返します。

変更の核心は、並列タスク数の上限を固定値 nelem(bg) から、動的に変更可能な maxnbg 変数に切り替えた点です。そして、main 関数内でホストアーキテクチャがARMである場合にのみ、この maxnbg を1に設定することで、ARM環境での並列ビルドを実質的に無効化(直列化)しています。

これにより、ARM環境では常に1つのビルドタスクしか並列に実行されなくなり、リソースの競合が大幅に緩和されます。他のアーキテクチャ(x86など)では、maxnbg は引き続き nelem(bg) の値(通常はより大きな値)を保持するため、並列ビルドの恩恵を享受できます。

コアとなるコードの変更箇所

--- a/src/cmd/dist/unix.c
+++ b/src/cmd/dist/unix.c
@@ -147,6 +147,7 @@ static struct {
 	char *cmd;
 } bg[MAXBG];
 static int nbg;
+static int maxnbg = nelem(bg);
 \
 static void bgwait1(void);\
 \
@@ -158,7 +159,7 @@ genrun(Buf *b, char *dir, int mode, Vec *argv, int wait)\
 	Buf cmd;\
 	char *q;\
 \
-\twhile(nbg >= nelem(bg))\
+\twhile(nbg >= maxnbg)\
 \t\tbgwait1();\
 \
 \t// Generate a copy of the command to show in a log.\
@@ -665,6 +666,9 @@ main(int argc, char **argv)\
 \t\t\tfatal(\"unknown architecture: %s\", u.machine);\
 \t}\
 \
+\tif(strcmp(gohostarch, \"arm\") == 0)\
+\t\tmaxnbg = 1;\
+\
 \tinit();\
 \txmain(argc, argv);\
 \tbfree(&b);\

コアとなるコードの解説

  1. static int maxnbg = nelem(bg); の追加:

    • bg 配列の定義の直後に、新しい静的変数 maxnbg が導入されました。
    • この変数は、バックグラウンドタスクの最大許容数を保持します。
    • 初期値として nelem(bg) が設定されています。これは、bg 配列が元々持っていた最大容量(つまり、この変更以前の並列タスクのデフォルト上限)と同じ値です。これにより、ARM以外のアーキテクチャでは、以前と同じ並列度でビルドが実行されます。
  2. while(nbg >= nelem(bg)) から while(nbg >= maxnbg) への変更:

    • genrun 関数内で、新しいバックグラウンドタスクを開始する前に、現在実行中のタスク数 nbg が上限に達しているかをチェックする条件が変更されました。
    • 変更前は、ハードコードされた nelem(bg) と比較していましたが、変更後は新しく導入された maxnbg 変数と比較するようになりました。
    • これにより、maxnbg の値を動的に変更することで、並列タスクの上限を制御できるようになります。nbgmaxnbg 以上の場合、bgwait1() が呼び出され、いずれかのバックグラウンドタスクが完了するまで待機します。
  3. if(strcmp(gohostarch, "arm") == 0) maxnbg = 1; の追加:

    • main 関数の初期化処理の一部として、この条件分岐が追加されました。
    • strcmp(gohostarch, "arm") == 0 は、現在のホストアーキテクチャ (gohostarch) が文字列 "arm" と完全に一致するかどうかをチェックします。
    • もしホストアーキテクチャがARMである場合、maxnbg の値が 1 に設定されます。
    • この設定により、ARM環境では genrun 関数が常に nbg >= 1 の条件で bgwait1() を呼び出すようになり、実質的にバックグラウンドタスクの並列実行が1つに制限されます。つまり、Goのビルドプロセスは、ARMホスト上ではほぼ直列に実行されることになります。

これらの変更により、Goのビルドシステムは、ホストアーキテクチャがARMである場合にのみ、並列ビルドの度合いを自動的に調整し、リソースが限られた環境でのビルドの安定性を向上させることが可能になりました。

関連リンク

参考にした情報源リンク