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

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

コミット

commit 610f395189528d5eb6a9dde36e39314403d0b7a3
Author: Rob Pike <r@golang.org>
Date:   Fri Mar 28 12:55:37 2014 +1100

    doc/go1.3.html: contiguous stacks
    
    LGTM=bradfitz
    R=golang-codereviews, bradfitz
    CC=golang-codereviews
    https://golang.org/cl/81650043

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

https://github.com/golang/go/commit/610f395189528d5eb6a9dde36e39314403d0b7a3

元コミット内容

このコミットは、Go 1.3のリリースノートドキュメント doc/go1.3.html を更新し、Goランタイムにおけるゴルーチンのスタック実装の変更について記述を追加するものです。具体的には、従来の「セグメント化された(segmented)」スタックモデルから「連続した(contiguous)」スタックモデルへの移行について説明しています。

変更の背景

Go 1.3より前のGoランタイムでは、ゴルーチンのスタックは「セグメント化されたスタック(segmented stacks)」というモデルで管理されていました。このモデルでは、ゴルーチンが必要とするスタック領域が不足すると、新しい小さなスタックセグメントが割り当てられ、既存のスタックに連結される形で拡張されていました。しかし、このセグメント化されたスタックには「ホットスポット問題(hot spot problem)」という既知のパフォーマンス上の課題がありました。

「ホットスポット問題」とは、特にループ内で関数呼び出しが頻繁に行われ、そのたびにスタックがセグメント境界をまたぐような状況で発生しました。スタックがセグメント境界に近づくと、新しいセグメントの割り当てと既存セグメントとの連結、そして古いセグメントの解放といった一連の処理が頻繁に発生し、これがオーバーヘッドとなり、アプリケーションのパフォーマンスを著しく低下させる原因となっていました。

このコミットは、Go 1.3で導入された「連続したスタック(contiguous stacks)」モデルへの変更をドキュメントに反映させるものです。連続したスタックモデルは、この「ホットスポット問題」を根本的に解決し、ゴルーチンのスタック管理をより効率的にすることを目的としています。

前提知識の解説

ゴルーチン (Goroutine)

Go言語におけるゴルーチンは、軽量な並行実行単位です。OSのスレッドよりもはるかに軽量であり、数百万のゴルーチンを同時に実行することも可能です。ゴルーチンはGoランタイムによってスケジューリングされ、必要に応じてOSスレッドにマッピングされます。各ゴルーチンは独自のスタックを持っています。

スタック (Stack)

プログラムの実行において、関数呼び出しやローカル変数の格納に使用されるメモリ領域です。関数が呼び出されるたびにスタックフレームが積まれ、関数から戻るとスタックフレームが解放されます。スタックは通常、連続したメモリ領域として扱われます。

セグメント化されたスタック (Segmented Stacks)

Go 1.3より前のGoランタイムで採用されていたスタック管理モデルです。ゴルーチンのスタックは、必要に応じて小さなメモリブロック(セグメント)を動的に割り当て、それらを連結することで拡張されていました。これにより、初期のスタックサイズを小さく保ち、メモリ使用量を抑えることができました。しかし、前述の「ホットスポット問題」というパフォーマンス上の課題がありました。

連続したスタック (Contiguous Stacks)

Go 1.3で導入された新しいスタック管理モデルです。このモデルでは、ゴルーチンのスタックは単一の連続したメモリブロックとして割り当てられます。スタック領域が不足した場合、より大きな連続したメモリブロックが割り当てられ、既存のスタックの内容が新しいブロックにコピーされます。このコピー操作のオーバーヘッドは償却され、頻繁なセグメントの割り当て・解放によるパフォーマンス低下を防ぎます。

エスケープ解析 (Escape Analysis)

Goコンパイラが行う最適化の一つです。変数がスタックに割り当てられるべきか、ヒープに割り当てられるべきかを決定します。エスケープ解析により、ポインタがスタックの外部に「エスケープ」しないことが保証されるため、スタックのコピー操作が安全かつ効率的に行えるようになります。連続したスタックモデルが機能する上で重要な前提となります。

技術的詳細

Go 1.3における連続したスタックの実装は、以下の技術的詳細に基づいています。

  1. スタックの動的拡張: ゴルーチンがスタックを使い果たそうとすると、Goランタイムはより大きな連続したメモリブロックを割り当てます。
  2. スタック内容のコピー: 既存のスタックの内容(関数呼び出しの戻りアドレス、ローカル変数など)は、新しく割り当てられた大きなメモリブロックにすべてコピーされます。
  3. ポインタの更新: Goのエスケープ解析の特性により、スタック上のデータへのポインタは、そのスタック内でのみ有効であることが保証されます。これにより、スタックがコピーされた際に、スタック内のポインタを新しいメモリ位置に合わせて効率的に更新することが可能になります。
  4. 償却されたオーバーヘッド: スタックのコピー操作は、一見するとコストが高いように見えますが、スタックの拡張が頻繁に起こるわけではないため、そのオーバーヘッドは償却されます。つまり、全体的な実行時間で見ると、セグメント化されたスタックの頻繁なセグメント管理よりも効率的になります。
  5. 「ホットスポット問題」の解消: 連続したスタックモデルでは、スタックの拡張が必要な場合にのみコピーが発生するため、セグメント境界をまたぐたびに発生していた頻繁なセグメント操作が不要になり、「ホットスポット問題」が解消されます。
  6. スタックオーバーフローチェックの簡素化: 連続したスタックでは、スタックオーバーフローのチェックがより単純になります。スタックが不足した場合、ランタイムは単にスタックサイズを倍にして再試行するだけでよく、スタックフレームのサイズなどを詳細に知る必要がなくなります。

この変更は、特にスタックを深く使用するアプリケーションや、多数のゴルーチンが頻繁にスタックを拡張するようなシナリオにおいて、Goプログラムのパフォーマンスと安定性を向上させました。

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

このコミット自体は、Goランタイムの実際のスタック実装を変更するものではなく、Go 1.3のリリースノートドキュメント doc/go1.3.html の内容を更新するものです。

具体的には、doc/go1.3.html ファイルの <h3 id="stacks">Stack</h3> セクションにおいて、以下の変更が行われています。

  • TODO stack copying, no longer segmented という一時的な記述が削除されました。
  • Go 1.3でゴルーチンのスタック実装が「セグメント化されたモデル」から「連続したモデル」に変更されたこと、スタックが不足した場合に「より大きな単一のメモリブロックに転送される」こと、そしてこの転送操作のオーバーヘッドが「償却され、古い『ホットスポット』問題を解消する」ことが追記されました。
  • 詳細な情報とパフォーマンス数値が記載されたデザインドキュメントへのリンク http://golang.org/s/contigstacks が追加されました。

これは、Go 1.3の重要な変更点をユーザーに正確に伝えるためのドキュメント更新であり、実際のランタイムコードの変更は、このドキュメント更新よりも前のコミットで行われています。

コアとなるコードの解説

このコミットにおける「コアとなるコード」は、Go 1.3のリリースノートドキュメント doc/go1.3.html の該当箇所です。

変更前は、スタックに関する記述が TODO stack copying, no longer segmented となっており、これは将来の変更を示唆するプレースホルダーでした。

変更後には、以下のHTMLスニペットが追加されました。

Go 1.3 has changed the implementation of goroutine stacks away from the old,
"segmented" model to a contiguous model.
When a goroutine needs more stack
than is available, its stack is transferred to a larger single block of memory.
The overhead of this transfer operation amortizes well and eliminates the old "hot spot"
problem when a calculation repeatedly steps across a segment boundary.
Details including performance numbers are in this
<a href="http://golang.org/s/contigstacks">design document</a>.

この記述は、Go 1.3で導入された連続したスタックモデルの主要な特徴と利点を簡潔に説明しています。

  • Go 1.3 has changed the implementation of goroutine stacks away from the old, "segmented" model to a contiguous model.: Go 1.3でゴルーチンのスタック実装が、古いセグメント化されたモデルから連続したモデルに変更されたことを明確に述べています。
  • When a goroutine needs more stack than is available, its stack is transferred to a larger single block of memory.: スタックが不足した場合の具体的な動作、すなわち、より大きな単一のメモリブロックへのスタックの転送(コピー)を説明しています。
  • The overhead of this transfer operation amortizes well and eliminates the old "hot spot" problem when a calculation repeatedly steps across a segment boundary.: この転送操作のオーバーヘッドが償却されること、そしてセグメント境界を頻繁にまたぐ計算で発生していた古い「ホットスポット問題」が解消されるという、この変更の主要な利点を強調しています。
  • <a href="http://golang.org/s/contigstacks">design document</a>: 詳細な情報とパフォーマンスデータが記載されたデザインドキュメントへの公式リンクを提供し、ユーザーがさらに深く理解するための道筋を示しています。

このドキュメントの更新は、Go 1.3の重要なパフォーマンス改善とランタイムの内部変更を、Goユーザーコミュニティに正確に伝える上で不可欠な役割を果たしています。

関連リンク

  • Go 1.3 Release Notes (公式ドキュメント): このコミットが更新しているドキュメントの最終版。
  • Go contiguous stacks design document: http://golang.org/s/contigstacks (このコミットで参照されているデザインドキュメント)
  • Go issue tracker (関連するバグ報告や議論): Goのスタックに関する過去の議論や問題報告を検索することで、より深い背景を理解できる可能性があります。

参考にした情報源リンク

  • Go contiguous stacks design document (http://golang.org/s/contigstacks)
  • Stack Overflowの関連する議論 (例: "What is the difference between segmented and contiguous stacks in Go?")
  • Go言語の公式ドキュメントおよびリリースノート
  • Go言語のソースコードリポジトリ (golang/go)
  • Goに関する技術ブログや記事 (Goのスタック実装に関する解説記事など)