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

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

このコミットは、Goランタイムのテストコードにおける軽微なクリーンアップを目的としています。具体的には、src/pkg/runtime/proc_test.goファイル内のテストロジックが修正され、テストの安定性とコードの簡潔性が向上しています。

コミット

commit 0a86b4dab8a1b943c5f90fcff0c3c95902f30744
Author: Dmitriy Vyukov <dvyukov@google.com>
Date:   Tue Jul 16 01:02:42 2013 +0400

    runtime: minor test cleanup
    
    R=golang-dev, khr, rsc
    CC=golang-dev
    https://golang.org/cl/11280043

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

https://github.com/golang/go/commit/0a86b4dab8a1b943c5f90fcff0c3c95902f30744

元コミット内容

--- a/src/pkg/runtime/proc_test.go
+++ b/src/pkg/runtime/proc_test.go
@@ -227,7 +227,7 @@ func TestPreemptSplitBig(t *testing.T) {
 	stop := make(chan int)
 	go big(stop)
 	for i := 0; i < 3; i++ {
-\t\ttime.Sleep(1 * time.Microsecond) // let big start running
+\t\ttime.Sleep(10 * time.Microsecond) // let big start running
 \t\truntime.GC()\
 \t}\
 \tclose(stop)\
@@ -237,7 +237,7 @@ func big(stop chan int) int {
 \tn := 0\
 \tfor {\
 \t\t// delay so that gc is sure to have asked for a preemption
-\t\tfor i := int64(0); i < 1e9; i++ {\
+\t\tfor i := 0; i < 1e9; i++ {\
 \t\t\tn++\
 \t\t}\
 \
@@ -286,9 +286,6 @@ func nonleaf(stop chan int) bool {\
 \t}\
 }\
 \
-func poll() {\
-}\
-\
 func TestSchedLocalQueue(t *testing.T) {\
 \truntime.TestSchedLocalQueue1()\
 }\

変更の背景

このコミットの背景は、Goランタイムのテストコードの品質向上と安定化にあります。特に、proc_test.goはGoのスケジューラやガベージコレクション(GC)の挙動をテストするための重要なファイルです。これらのテストは、Goランタイムの低レベルな動作に依存するため、タイミングの問題や不必要な複雑さがテストの信頼性を損なう可能性があります。

この「マイナーなテストクリーンアップ」は、以下の目的で行われたと考えられます。

  1. テストの安定性向上: time.Sleepの時間を増やすことで、テストが実行される環境や負荷の変動によって引き起こされる可能性のある、タイミングに起因するテストの失敗(flaky tests)を減少させる。
  2. コードの簡潔化と可読性向上: 不要なコード(poll関数)の削除や、より簡潔な型指定(int64(0)から0への変更)により、テストコードの保守性を高める。
  3. Goの慣用的な記述への準拠: int64(0)のような明示的な型キャストを、より一般的な0にすることで、Goの慣用的なコーディングスタイルに合わせる。

これらの変更は、Goランタイムのテストスイート全体の信頼性を高め、将来のランタイム開発をよりスムーズに進めるための基盤を強化します。

前提知識の解説

このコミットを理解するためには、以下のGo言語およびランタイムに関する基本的な知識が必要です。

  • Goroutine(ゴルーチン): Go言語における軽量な並行処理の単位です。OSのスレッドよりもはるかに軽量で、数百万のゴルーチンを同時に実行できます。Goランタイムのスケジューラによって管理されます。
  • Goスケジューラ: Goランタイムに組み込まれているスケジューラは、ゴルーチンをOSスレッドにマッピングし、効率的に実行を切り替える役割を担います。プリエンプティブ(preemptive)なスケジューリングも行い、長時間実行されるゴルーチンが他のゴルーチンの実行を妨げないようにします。
  • Garbage Collection (GC)(ガベージコレクション): Goは自動メモリ管理を採用しており、不要になったメモリを自動的に解放するガベージコレクタを備えています。GoのGCは並行(concurrent)かつ低遅延(low-latency)で動作するように設計されており、プログラムの実行を長時間停止させることなくメモリを回収します。GCの実行中には、ゴルーチンの実行が一時的に停止(ストップ・ザ・ワールド、STW)したり、GCの処理を助けるためにゴルーチンが協調したりする場合があります。
  • time.Sleep: 指定された期間、現在のゴルーチンの実行を一時停止させる関数です。テストにおいて、特定の処理が完了するのを待つためや、タイミングを調整するために使用されます。
  • テストにおけるタイミングの問題(Flaky Tests): 並行処理を含むテストでは、実行タイミングのわずかなずれによってテスト結果が不安定になることがあります。これは「flaky test」(不安定なテスト)と呼ばれ、再現性が低くデバッグが困難なため、開発効率を低下させます。

技術的詳細

このコミットで行われた技術的な変更は、Goランタイムのテストの信頼性とコードの品質を向上させるためのものです。

  1. time.Sleep(1 * time.Microsecond) から time.Sleep(10 * time.Microsecond) への変更:

    • TestPreemptSplitBig関数は、Goスケジューラのプリエンプション(横取り)とGCの相互作用をテストしていると考えられます。特に、bigというゴルーチンが起動し、その後にruntime.GC()が複数回呼び出されています。
    • 元の1マイクロ秒という短いスリープ時間は、bigゴルーチンが実際に実行を開始し、GCがプリエンプションを要求するのに十分な時間を与えない可能性がありました。特に、テストが実行されるマシンの負荷、OSのスケジューリング、Goランタイムの内部的な初期化時間など、様々な要因によってこのタイミングは変動します。
    • 10マイクロ秒にスリープ時間を増やすことで、bigゴルーチンが確実に実行状態に入り、GCがそのゴルーチンをプリエンプトする機会を得るための「猶予」が長くなります。これにより、テストがより安定し、環境依存のタイミング問題による失敗が減少します。これは、並行処理のテストにおいて、特定のイベントの発生を待つための一般的なプラクティスです。
  2. for i := int64(0); i < 1e9; i++ から for i := 0; i < 1e9; i++ への変更:

    • big関数内のループは、GCがプリエンプションを要求するまでの遅延をシミュレートするために、意図的にCPU時間を消費する目的で使用されています。
    • 元のコードでは、ループカウンタiint64(0)と明示的にint64型として初期化されていました。Goでは、数値リテラル0は型を持たない定数であり、コンテキストに応じて適切な型に推論されます。
    • この変更は、int64(0)という冗長な型指定を削除し、より簡潔な0を使用するように修正したものです。1e9という値は、ほとんどの64ビットシステムにおいてデフォルトのint型(通常はint64)に収まるため、明示的なint64型指定は不要です。これは、コードの簡潔性とGoの慣用的な記述に合わせたクリーンアップです。機能的な変更はありません。
  3. func poll() {} の削除:

    • pollという関数が定義されていましたが、その中身は空であり、コミットの差分を見る限り、この関数はどこからも呼び出されていませんでした。
    • 未使用のコードを削除することは、コードベースをクリーンに保ち、不必要な複雑さを排除するための標準的なプラクティスです。これにより、コードの可読性が向上し、将来のメンテナンスが容易になります。

これらの変更は、Goランタイムのテストの堅牢性を高めつつ、コードベースをよりクリーンで保守しやすい状態に保つための、細部にわたる配慮を示しています。

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

src/pkg/runtime/proc_test.go ファイルにおいて、以下の3つの変更が行われました。

  1. TestPreemptSplitBig 関数内:
    -		time.Sleep(1 * time.Microsecond) // let big start running
    +		time.Sleep(10 * time.Microsecond) // let big start running
    
  2. big 関数内:
    -		for i := int64(0); i < 1e9; i++ {
    +		for i := 0; i < 1e9; i++ {
    
  3. poll 関数の削除:
    -func poll() {
    -}
    -
    

コアとなるコードの解説

  1. time.Sleep の変更:

    • 旧コード: time.Sleep(1 * time.Microsecond)
    • 新コード: time.Sleep(10 * time.Microsecond)
    • この変更は、TestPreemptSplitBigテストの信頼性を向上させるためのものです。bigゴルーチンが起動した後、runtime.GC()が呼び出される前に、bigゴルーチンが確実に実行を開始し、Goスケジューラがその存在を認識するのに十分な時間を与えることを目的としています。1マイクロ秒では、特に高負荷時や異なるシステム環境下で、bigゴルーチンが十分に実行される前にGCが開始され、テストが意図しない結果になる可能性がありました。10マイクロ秒に増やすことで、このタイミングの問題が緩和され、テストがより安定してパスするようになります。
  2. ループカウンタの型指定の変更:

    • 旧コード: for i := int64(0); i < 1e9; i++ {
    • 新コード: for i := 0; i < 1e9; i++ {
    • この変更は、big関数内のCPUを消費するループにおける、ループカウンタiの初期化方法を簡潔にするものです。Goでは、数値リテラル0は型を持たない定数であり、代入される変数の型や使用されるコンテキストに基づいて型が推論されます。1e9という値は、Goのデフォルトのint型(通常は64ビットシステムではint64)に収まるため、int64(0)と明示的に型を指定する必要はありませんでした。この変更は機能的な影響はなく、コードの冗長性を排除し、Goの慣用的な記述に合わせるためのクリーンアップです。
  3. poll 関数の削除:

    • 旧コード: func poll() {} という空の関数が存在。
    • 新コード: この関数が完全に削除された。
    • poll関数は定義されていましたが、その中身は空であり、コードベースの他の場所から呼び出されている形跡がありませんでした。これはデッドコード(dead code)であり、プログラムの実行には影響を与えません。未使用のコードを削除することで、コードベースが整理され、可読性が向上し、将来のメンテナンス時の混乱を防ぐことができます。

これらの変更は、Goランタイムのテストの堅牢性を高めつつ、コードベースをよりクリーンで保守しやすい状態に保つための、細部にわたる配慮を示しています。

関連リンク

参考にした情報源リンク