[インデックス 14874] ファイルの概要
このコミットは、Go言語のテストスイートにおけるrunoutput
形式のテストが、特にARMアーキテクチャ上で大量のメモリを消費する問題に対処するためのものです。具体的には、ARMプラットフォームでのrunoutput
テストの並列実行数を制限することで、テスト実行時のメモリ不足やパフォーマンス低下を防ぐことを目的としています。
コミット
commit dc75670ae244a3f26771288d82eefc819b1e6716
Author: Dave Cheney <dave@cheney.net>
Date: Sat Jan 12 17:52:52 2013 +1100
test: limit runoutput tests on arm platforms
runoutput styles tests generally consume a lot of memory. On arm platforms rotate?.go consume around 200mb each to compile, and as tests are sorted alphabetically, they all tend to run at once.
This change limits the number of runoutput jobs to 2 on arm platforms.
R=minux.ma, remyoudompheng, bradfitz, lucio.dere
CC=golang-dev
https://golang.org/cl/7099047
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/dc75670ae244a3f26771288d82eefc819b1e6716
元コミット内容
test: limit runoutput tests on arm platforms
runoutput
形式のテストは一般的に多くのメモリを消費します。ARMプラットフォームでは、rotate?.go
のようなテストがコンパイルにそれぞれ約200MBを消費し、テストがアルファベット順にソートされているため、それらが一度に実行される傾向がありました。
この変更は、ARMプラットフォームでのrunoutput
ジョブの数を2に制限します。
変更の背景
Go言語のテストスイートは、様々なプラットフォームで実行されることを想定して設計されています。しかし、特定のテスト、特にrunoutput
形式のテストは、コンパイル時や実行時に大量のメモリを必要とすることが判明しました。
コミットメッセージによると、この問題は特にARMプラットフォームで顕著でした。rotate?.go
のようなテストファイルは、コンパイルするだけで約200MBものメモリを消費すると記述されています。さらに、Goのテストランナーはデフォルトでテストをアルファベット順に実行する傾向があるため、メモリを大量に消費するこれらのテストが同時に実行され、結果としてARMデバイスの限られたメモリリソースを圧迫し、テストの失敗やシステムの不安定化を引き起こす可能性がありました。
このコミットは、このようなメモリ消費の問題を緩和し、ARMプラットフォーム上でのGoテストスイートの安定性と信頼性を向上させるために導入されました。具体的には、runoutput
テストの並列実行数を制限することで、同時に消費されるメモリの総量を抑制するアプローチが取られました。
前提知識の解説
Go言語のテスト (go test
)
Go言語には、標準でテストフレームワークが組み込まれており、go test
コマンドを使用してテストを実行します。テストファイルは通常、テスト対象のファイルと同じディレクトリに_test.go
というサフィックスを付けて配置されます。go test
は、これらのファイルを自動的に検出し、テスト関数(TestXxx
という命名規則に従う関数)を実行します。
runoutput
形式のテスト
Goのテストスイートには、様々な種類のテストが含まれています。runoutput
形式のテストは、Goプログラムを実行し、その標準出力(stdout)や標準エラー出力(stderr)をキャプチャし、期待される出力と比較するタイプのテストです。これは、コマンドラインツールの動作検証や、特定の出力フォーマットの確認などに利用されます。
この種のテストは、テスト対象のGoプログラムをコンパイルし、実行する必要があるため、通常のユニットテストに比べてリソース(CPU、メモリ)を多く消費する傾向があります。特に、コンパイルされるプログラム自体が大規模であったり、多くの依存関係を持っていたりする場合、コンパイルプロセスが大量のメモリを要求することがあります。
ARMアーキテクチャ
ARM(Advanced RISC Machine)は、モバイルデバイス、組み込みシステム、IoTデバイスなどで広く使用されているCPUアーキテクチャです。x86アーキテクチャと比較して、一般的に消費電力が低く、リソース(特にメモリ)が限られている環境で利用されることが多いです。そのため、ARMプラットフォームで動作するソフトウェアは、メモリ使用量に特に注意を払う必要があります。
runtime.NumCPU()
Go言語の標準ライブラリruntime
パッケージには、NumCPU()
関数があります。この関数は、現在のシステムで利用可能な論理CPUの数を返します。Goのテストランナーや並列処理を行うプログラムは、この値を利用して、デフォルトの並列実行数を決定することがよくあります。
Goのチャネル (chan
) を用いた並行性制御
Go言語のチャネルは、ゴルーチン(軽量スレッド)間で値を送受信するための通信メカニズムであり、並行処理の同期と調整に広く使用されます。バッファ付きチャネルは、指定された数の要素を保持できるキューとして機能します。チャネルに要素を送信する(chan <- value
)操作は、チャネルが満杯の場合にブロックされ、チャネルから要素を受信する(<- chan
)操作は、チャネルが空の場合にブロックされます。
この特性を利用して、バッファ付きチャネルは並行処理の「ゲート」として機能させることができます。チャネルのバッファサイズを最大並列実行数に設定し、各並行タスクの開始時にチャネルに要素を送信し、終了時に要素を受信することで、同時に実行されるタスクの数を制限できます。
技術的詳細
このコミットは、test/run.go
ファイルに変更を加えています。このファイルは、Go言語のテストスイートを実行するための内部ツールの一部です。
変更の核心は、runoutput
テストの並列実行を制御するための新しいメカニズムの導入です。
-
runoutputLimit
フラグの追加:flag.Int("l", defaultRunOutputLimit(), "number of parallel runoutput tests to run")
新しいコマンドラインフラグ-l
(または--l
)が追加されました。これにより、ユーザーはrunoutput
テストの並列実行数を手動で設定できるようになります。デフォルト値はdefaultRunOutputLimit()
関数によって決定されます。 -
rungatec
チャネルの導入:rungatec = make(chan bool, *runoutputLimit)
rungatec
という名前の新しいバッファ付きチャネルが導入されました。このチャネルのバッファサイズは、runoutputLimit
フラグの値によって初期化されます。このチャネルは、runoutput
テストの並列実行数を制限するためのセマフォとして機能します。 -
test.run()
メソッド内の変更:test.run()
メソッドは、個々のテストを実行するロジックを含んでいます。runoutput
テストの場合、以下の変更が加えられました。case "runoutput": rungatec <- true // テスト開始前にチャネルに送信 defer func() { <-rungatec // テスト終了後にチャネルから受信 }() // ... 既存のrunoutputテスト実行ロジック ...
runoutput
テストが開始される直前に、rungatec <- true
という操作が行われます。これは、rungatec
チャネルにブール値(true
)を送信しようとします。チャネルが満杯(つまり、既にrunoutputLimit
個のテストが並列で実行中)の場合、この操作はブロックされ、テストの実行は待機します。 テストが完了すると(defer
ステートメントにより)、<-rungatec
という操作が行われ、チャネルから値が受信されます。これにより、チャネルに空きができ、別のrunoutput
テストが実行を開始できるようになります。 -
defaultRunOutputLimit()
関数の追加:func defaultRunOutputLimit() int { const maxArmCPU = 2 cpu := runtime.NumCPU() if runtime.GOARCH == "arm" && cpu > maxArmCPU { cpu = maxArmCPU } return cpu }
この新しい関数は、
runoutputLimit
のデフォルト値を計算します。- まず、
runtime.NumCPU()
を呼び出して、現在のシステムで利用可能な論理CPUの数を取得します。 - もし現在のアーキテクチャが
"arm"
であり、かつCPUの数がmaxArmCPU
(定数で2と定義されている)よりも大きい場合、cpu
の値をmaxArmCPU
に制限します。 - それ以外の場合は、
runtime.NumCPU()
が返した値がそのまま使用されます。 これにより、ARMプラットフォームではデフォルトでrunoutput
テストの並列実行数が最大2に制限されるようになります。
- まず、
これらの変更により、ARMプラットフォームでのrunoutput
テストの同時実行数が厳密に制御され、メモリ消費が抑制されることで、テストスイート全体の安定性が向上します。
コアとなるコードの変更箇所
変更はtest/run.go
ファイルに集中しています。
--- a/test/run.go
+++ b/test/run.go
@@ -30,10 +30,11 @@ import (
)
var (
- verbose = flag.Bool("v", false, "verbose. if set, parallelism is set to 1.")
- numParallel = flag.Int("n", runtime.NumCPU(), "number of parallel tests to run")
- summary = flag.Bool("summary", false, "show summary of results")
- showSkips = flag.Bool("show_skips", false, "show skipped tests")
+ verbose = flag.Bool("v", false, "verbose. if set, parallelism is set to 1.")
+ numParallel = flag.Int("n", runtime.NumCPU(), "number of parallel tests to run")
+ summary = flag.Bool("summary", false, "show summary of results")
+ showSkips = flag.Bool("show_skips", false, "show skipped tests")
+ runoutputLimit = flag.Int("l", defaultRunOutputLimit(), "number of parallel runoutput tests to run")
)
var (
@@ -53,6 +54,10 @@ var (
// toRun is the channel of tests to run.
// It is nil until the first test is started.
toRun chan *test
+
+ // rungatec controls the max number of runoutput tests
+ // executed in parallel as they can each consume a lot of memory.
+ rungatec chan bool
)
// maxTests is an upper bound on the total number of tests.
@@ -68,6 +73,7 @@ func main() {
}
ratec = make(chan bool, *numParallel)
+ rungatec = make(chan bool, *runoutputLimit)
var err error
letter, err = build.ArchChar(build.Default.GOARCH)
check(err)
@@ -504,14 +510,17 @@ func (t *test) run() {
}
case "runoutput":
+ rungatec <- true
+ defer func() {
+ <-rungatec
+ }()
useTmp = false
out, err := runcmd(append([]string{"go", "run", t.goFileName()}, args...)...)
if err != nil {
t.err = err
}
tfile := filepath.Join(t.tempDir, "tmp__.go")
- err = ioutil.WriteFile(tfile, out, 0666)
- if err != nil {
+ if err := ioutil.WriteFile(tfile, out, 0666); err != nil {
t.err = fmt.Errorf("write tempfile:%s", err)
return
}
@@ -735,3 +744,15 @@ var skipOkay = map[string]bool{
"fixedbugs/bug429.go": true,\n
"bugs/bug395.go": true,\n
}\n
+\n
+// defaultRunOutputLimit returns the number of runoutput tests that\n
+// can be executed in parallel.\n
+func defaultRunOutputLimit() int {\n
+ const maxArmCPU = 2\n
+\n
+ cpu := runtime.NumCPU()\n
+ if runtime.GOARCH == "arm" && cpu > maxArmCPU {\n
+ cpu = maxArmCPU\n
+ }\n
+ return cpu\n
+}\n
コアとなるコードの解説
var
ブロックの変更
runoutputLimit
という新しいflag.Int
変数が追加されました。これは、runoutput
テストの並列実行数を制御するためのコマンドラインフラグ-l
に対応します。デフォルト値はdefaultRunOutputLimit()
関数の結果によって設定されます。rungatec
という新しいchan bool
変数が宣言されました。これは、runoutput
テストの並列実行を制御するためのチャネル(セマフォ)として機能します。
main()
関数の変更
rungatec = make(chan bool, *runoutputLimit)
:main
関数内で、rungatec
チャネルが初期化されます。チャネルのバッファサイズは、runoutputLimit
フラグの値(デフォルトではdefaultRunOutputLimit()
の結果)によって決定されます。これにより、同時に実行できるrunoutput
テストの最大数が設定されます。
test.run()
メソッドの変更
case "runoutput":
ブロック内:rungatec <- true
:runoutput
テストが実行される直前に、rungatec
チャネルに値を送信します。これにより、チャネルのバッファが一つ消費されます。もしチャネルが既に満杯(つまり、設定された並列実行数の上限に達している)であれば、この操作はブロックされ、現在のテストは他のテストが完了してチャネルに空きができるまで待機します。defer func() { <-rungatec }()
:defer
キーワードにより、現在のテスト関数が終了する直前に、rungatec
チャネルから値を読み出す操作が実行されるようにスケジュールされます。これにより、チャネルのバッファが一つ解放され、別のrunoutput
テストが実行を開始できるようになります。
ioutil.WriteFile
のエラーハンドリングが、より簡潔な形式に変更されました。これは機能的な変更ではなく、コードスタイルの改善です。
defaultRunOutputLimit()
関数の追加
- この関数は、
runoutputLimit
フラグのデフォルト値を計算します。 runtime.NumCPU()
を呼び出して、現在のシステムの論理CPU数を取得します。runtime.GOARCH == "arm"
のチェックにより、現在の実行環境がARMアーキテクチャであるかどうかを判断します。- もしARMアーキテクチャであり、かつCPU数が
maxArmCPU
(定数2
)よりも大きい場合、返される並列実行数を2
に制限します。これは、ARMプラットフォームでのメモリ制約を考慮したものです。 - それ以外のプラットフォームや、ARMであってもCPU数が2以下の場合は、
runtime.NumCPU()
が返す値がそのまま使用されます。
これらの変更により、Goのテストスイートは、特にリソースが限られたARMプラットフォームにおいて、runoutput
テストによる過剰なメモリ消費を効果的に抑制できるようになりました。
関連リンク
- Go言語のテストに関する公式ドキュメント: https://go.dev/doc/code#testing
- Go言語の
runtime
パッケージ: https://pkg.go.dev/runtime - Go言語の
flag
パッケージ: https://pkg.go.dev/flag - Go言語のチャネルに関する公式ドキュメント: https://go.dev/tour/concurrency/2
参考にした情報源リンク
- https://golang.org/cl/7099047 (このコミットのChangeListページ)
- Go言語の公式ドキュメントおよびパッケージドキュメント
- 一般的なGo言語の並行性パターンに関する知識
- ARMアーキテクチャに関する一般的な知識
- Go言語のテスト実行時のメモリ消費に関する一般的な情報(Web検索結果に基づく)
[インデックス 14874] ファイルの概要
このコミットは、Go言語のテストスイートにおけるrunoutput
形式のテストが、特にARMアーキテクチャ上で大量のメモリを消費する問題に対処するためのものです。具体的には、ARMプラットフォームでのrunoutput
テストの並列実行数を制限することで、テスト実行時のメモリ不足やパフォーマンス低下を防ぐことを目的としています。
コミット
commit dc75670ae244a3f26771288d82eefc819b1e6716
Author: Dave Cheney <dave@cheney.net>
Date: Sat Jan 12 17:52:52 2013 +1100
test: limit runoutput tests on arm platforms
runoutput styles tests generally consume a lot of memory. On arm platforms rotate?.go consume around 200mb each to compile, and as tests are sorted alphabetically, they all tend to run at once.
This change limits the number of runoutput jobs to 2 on arm platforms.
R=minux.ma, remyoudompheng, bradfitz, lucio.dere
CC=golang-dev
https://golang.org/cl/7099047
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/dc75670ae244a3f26771288d82eefc819b1e6716
元コミット内容
test: limit runoutput tests on arm platforms
runoutput
形式のテストは一般的に多くのメモリを消費します。ARMプラットフォームでは、rotate?.go
のようなテストがコンパイルにそれぞれ約200MBを消費し、テストがアルファベット順にソートされているため、それらが一度に実行される傾向がありました。
この変更は、ARMプラットフォームでのrunoutput
ジョブの数を2に制限します。
変更の背景
Go言語のテストスイートは、様々なプラットフォームで実行されることを想定して設計されています。しかし、特定のテスト、特にrunoutput
形式のテストは、コンパイル時や実行時に大量のメモリを必要とすることが判明しました。
コミットメッセージによると、この問題は特にARMプラットフォームで顕著でした。rotate?.go
のようなテストファイルは、コンパイルするだけで約200MBものメモリを消費すると記述されています。さらに、Goのテストランナーはデフォルトでテストをアルファベット順に実行する傾向があるため、メモリを大量に消費するこれらのテストが同時に実行され、結果としてARMデバイスの限られたメモリリソースを圧迫し、テストの失敗やシステムの不安定化を引き起こす可能性がありました。
このコミットは、このようなメモリ消費の問題を緩和し、ARMプラットフォーム上でのGoテストスイートの安定性と信頼性を向上させるために導入されました。具体的には、runoutput
テストの並列実行数を制限することで、同時に消費されるメモリの総量を抑制するアプローチが取られました。
前提知識の解説
Go言語のテスト (go test
)
Go言語には、標準でテストフレームワークが組み込まれており、go test
コマンドを使用してテストを実行します。テストファイルは通常、テスト対象のファイルと同じディレクトリに_test.go
というサフィックスを付けて配置されます。go test
は、これらのファイルを自動的に検出、コンパイルし、テスト関数(TestXxx
という命名規則に従う関数)を実行します。
runoutput
形式のテスト
Goのテストスイートには、様々な種類のテストが含まれています。runoutput
形式のテストは、Goプログラムをコンパイル・実行し、その標準出力(stdout)や標準エラー出力(stderr)をキャプチャし、期待される出力と比較するタイプのテストです。これは、コマンドラインツールの動作検証や、特定の出力フォーマットの確認などに利用されます。
この種のテストは、テスト対象のGoプログラムをコンパイルし、実行する必要があるため、通常のユニットテストに比べてリソース(CPU、メモリ)を多く消費する傾向があります。特に、コンパイルされるプログラム自体が大規模であったり、多くの依存関係を持っていたりする場合、コンパイルプロセスが大量のメモリを要求することがあります。
ARMアーキテクチャ
ARM(Advanced RISC Machine)は、モバイルデバイス、組み込みシステム、IoTデバイスなどで広く使用されているCPUアーキテクチャです。x86アーキテクチャと比較して、一般的に消費電力が低く、リソース(特にメモリ)が限られている環境で利用されることが多いです。そのため、ARMプラットフォームで動作するソフトウェアは、メモリ使用量に特に注意を払う必要があります。
runtime.NumCPU()
Go言語の標準ライブラリruntime
パッケージには、NumCPU()
関数があります。この関数は、現在のシステムで利用可能な論理CPUの数を返します。Goのテストランナーや並列処理を行うプログラムは、この値を利用して、デフォルトの並列実行数を決定することがよくあります。
Goのチャネル (chan
) を用いた並行性制御
Go言語のチャネルは、ゴルーチン(軽量スレッド)間で値を送受信するための通信メカニズムであり、並行処理の同期と調整に広く使用されます。バッファ付きチャネルは、指定された数の要素を保持できるキューとして機能します。チャネルに要素を送信する(chan <- value
)操作は、チャネルが満杯の場合にブロックされ、チャネルから要素を受信する(<- chan
)操作は、チャネルが空の場合にブロックされます。
この特性を利用して、バッファ付きチャネルは並行処理の「ゲート」として機能させることができます。チャネルのバッファサイズを最大並列実行数に設定し、各並行タスクの開始時にチャネルに要素を送信し、終了時に要素を受信することで、同時に実行されるタスクの数を制限できます。
技術的詳細
このコミットは、test/run.go
ファイルに変更を加えています。このファイルは、Go言語のテストスイートを実行するための内部ツールの一部です。
変更の核心は、runoutput
テストの並列実行を制御するための新しいメカニズムの導入です。
-
runoutputLimit
フラグの追加:flag.Int("l", defaultRunOutputLimit(), "number of parallel runoutput tests to run")
新しいコマンドラインフラグ-l
(または--l
)が追加されました。これにより、ユーザーはrunoutput
テストの並列実行数を手動で設定できるようになります。デフォルト値はdefaultRunOutputLimit()
関数によって決定されます。 -
rungatec
チャネルの導入:rungatec = make(chan bool, *runoutputLimit)
rungatec
という名前の新しいバッファ付きチャネルが導入されました。このチャネルのバッファサイズは、runoutputLimit
フラグの値によって初期化されます。このチャネルは、runoutput
テストの並列実行数を制限するためのセマフォとして機能します。 -
test.run()
メソッド内の変更:test.run()
メソッドは、個々のテストを実行するロジックを含んでいます。runoutput
テストの場合、以下の変更が加えられました。case "runoutput": rungatec <- true // テスト開始前にチャネルに送信 defer func() { <-rungatec // テスト終了後にチャネルから受信 }() // ... 既存のrunoutputテスト実行ロジック ...
runoutput
テストが開始される直前に、rungatec <- true
という操作が行われます。これは、rungatec
チャネルにブール値(true
)を送信しようとします。チャネルが満杯(つまり、既にrunoutputLimit
個のテストが並列で実行中)の場合、この操作はブロックされ、テストの実行は待機します。 テストが完了すると(defer
ステートメントにより)、<-rungatec
という操作が行われ、チャネルから値が受信されます。これにより、チャネルに空きができ、別のrunoutput
テストが実行を開始できるようになります。 -
defaultRunOutputLimit()
関数の追加:func defaultRunOutputLimit() int { const maxArmCPU = 2 cpu := runtime.NumCPU() if runtime.GOARCH == "arm" && cpu > maxArmCPU { cpu = maxArmCPU } return cpu }
この新しい関数は、
runoutputLimit
のデフォルト値を計算します。- まず、
runtime.NumCPU()
を呼び出して、現在のシステムで利用可能な論理CPUの数を取得します。 - もし現在のアーキテクチャが
"arm"
であり、かつCPUの数がmaxArmCPU
(定数で2と定義されている)よりも大きい場合、cpu
の値をmaxArmCPU
に制限します。 - それ以外の場合は、
runtime.NumCPU()
が返した値がそのまま使用されます。 これにより、ARMプラットフォームではデフォルトでrunoutput
テストの並列実行数が最大2に制限されるようになります。
- まず、
これらの変更により、ARMプラットフォームでのrunoutput
テストの同時実行数が厳密に制御され、メモリ消費が抑制されることで、テストスイート全体の安定性が向上します。
コアとなるコードの変更箇所
変更はtest/run.go
ファイルに集中しています。
--- a/test/run.go
+++ b/test/run.go
@@ -30,10 +30,11 @@ import (
)
var (
- verbose = flag.Bool("v", false, "verbose. if set, parallelism is set to 1.")
- numParallel = flag.Int("n", runtime.NumCPU(), "number of parallel tests to run")
- summary = flag.Bool("summary", false, "show summary of results")
- showSkips = flag.Bool("show_skips", false, "show skipped tests")
+ verbose = flag.Bool("v", false, "verbose. if set, parallelism is set to 1.")
+ numParallel = flag.Int("n", runtime.NumCPU(), "number of parallel tests to run")
+ summary = flag.Bool("summary", false, "show summary of results")
+ showSkips = flag.Bool("show_skips", false, "show skipped tests")
+ runoutputLimit = flag.Int("l", defaultRunOutputLimit(), "number of parallel runoutput tests to run")
)
var (
@@ -53,6 +54,10 @@ var (
// toRun is the channel of tests to run.
// It is nil until the first test is started.
toRun chan *test
+
+ // rungatec controls the max number of runoutput tests
+ // executed in parallel as they can each consume a lot of memory.
+ rungatec chan bool
)
// maxTests is an upper bound on the total number of tests.
@@ -68,6 +73,7 @@ func main() {
}
ratec = make(chan bool, *numParallel)
+ rungatec = make(chan bool, *runoutputLimit)
var err error
letter, err = build.ArchChar(build.Default.GOARCH)
check(err)
@@ -504,14 +510,17 @@ func (t *test) run() {
}
case "runoutput":
+ rungatec <- true
+ defer func() {
+ <-rungatec
+ }()
useTmp = false
out, err := runcmd(append([]string{"go", "run", t.goFileName()}, args...)...)
if err != nil {
t.err = err
}
tfile := filepath.Join(t.tempDir, "tmp__.go")
- err = ioutil.WriteFile(tfile, out, 0666)
- if err != nil {
+ if err := ioutil.WriteFile(tfile, out, 0666); err != nil {
t.err = fmt.Errorf("write tempfile:%s", err)
return
}
@@ -735,3 +744,15 @@ var skipOkay = map[string]bool{
"fixedbugs/bug429.go": true,\n
"bugs/bug395.go": true,\n
}\n
+\n
+// defaultRunOutputLimit returns the number of runoutput tests that\n
+// can be executed in parallel.\n
+func defaultRunOutputLimit() int {\n
+ const maxArmCPU = 2\n
+\n
+ cpu := runtime.NumCPU()\n
+ if runtime.GOARCH == "arm" && cpu > maxArmCPU {\n
+ cpu = maxArmCPU\n
+ }\n
+ return cpu\n
+}\n
コアとなるコードの解説
var
ブロックの変更
runoutputLimit
という新しいflag.Int
変数が追加されました。これは、runoutput
テストの並列実行数を制御するためのコマンドラインフラグ-l
に対応します。デフォルト値はdefaultRunOutputLimit()
関数の結果によって設定されます。rungatec
という新しいchan bool
変数が宣言されました。これは、runoutput
テストの並列実行を制御するためのチャネル(セマフォ)として機能します。
main()
関数の変更
rungatec = make(chan bool, *runoutputLimit)
:main
関数内で、rungatec
チャネルが初期化されます。チャネルのバッファサイズは、runoutputLimit
フラグの値(デフォルトではdefaultRunOutputLimit()
の結果)によって決定されます。これにより、同時に実行できるrunoutput
テストの最大数が設定されます。
test.run()
メソッドの変更
case "runoutput":
ブロック内:rungatec <- true
:runoutput
テストが実行される直前に、rungatec
チャネルに値を送信します。これにより、チャネルのバッファが一つ消費されます。もしチャネルが既に満杯(つまり、設定された並列実行数の上限に達している)であれば、この操作はブロックされ、現在のテストは他のテストが完了してチャネルに空きができるまで待機します。defer func() { <-rungatec }()
:defer
キーワードにより、現在のテスト関数が終了する直前に、rungatec
チャネルから値を読み出す操作が実行されるようにスケジュールされます。これにより、チャネルのバッファが一つ解放され、別のrunoutput
テストが実行を開始できるようになります。
ioutil.WriteFile
のエラーハンドリングが、より簡潔な形式に変更されました。これは機能的な変更ではなく、コードスタイルの改善です。
defaultRunOutputLimit()
関数の追加
- この関数は、
runoutputLimit
フラグのデフォルト値を計算します。 runtime.NumCPU()
を呼び出して、現在のシステムの論理CPU数を取得します。runtime.GOARCH == "arm"
のチェックにより、現在の実行環境がARMアーキテクチャであるかどうかを判断します。- もしARMアーキテクチャであり、かつCPU数が
maxArmCPU
(定数2
)よりも大きい場合、返される並列実行数を2
に制限します。これは、ARMプラットフォームでのメモリ制約を考慮したものです。 - それ以外のプラットフォームや、ARMであってもCPU数が2以下の場合は、
runtime.NumCPU()
が返す値がそのまま使用されます。
これらの変更により、Goのテストスイートは、特にリソースが限られたARMプラットフォームにおいて、runoutput
テストによる過剰なメモリ消費を効果的に抑制できるようになりました。
関連リンク
- Go言語のテストに関する公式ドキュメント: https://go.dev/doc/code#testing
- Go言語の
runtime
パッケージ: https://pkg.go.dev/runtime - Go言語の
flag
パッケージ: https://pkg.go.dev/flag - Go言語のチャネルに関する公式ドキュメント: https://go.dev/tour/concurrency/2
参考にした情報源リンク
- https://golang.org/cl/7099047 (このコミットのChangeListページ)
- Go言語の公式ドキュメントおよびパッケージドキュメント
- Go言語のテストにおけるメモリプロファイリングに関する情報:
go test -memprofile
: https://go.dev/blog/pprofgo test -memprofilerate
: https://go.dev/cmd/go/#hdr-Test_packages
- ARMアーキテクチャにおけるGoテストの考慮事項:
- Go on ARM: https://go.dev/doc/install/source#arm
- Stack Overflow: Go tests out of memory on Raspberry Pi: https://stackoverflow.com/questions/30987800/go-tests-out-of-memory-on-raspberry-pi