[インデックス 16508] ファイルの概要
このコミットは、Goランタイムにおけるチャネル(Hchan
構造体)の内部実装に関する変更です。具体的には、Hchan
構造体から未使用のフィールドelemalign
を削除し、それに伴うアライメント(メモリ配置の調整)ロジックも除去しています。
コミット
commit 8bf57c3dcba00951a96de3d8ee58d844253da621
Author: Dmitriy Vyukov <dvyukov@google.com>
Date: Thu Jun 6 23:06:12 2013 +0400
runtime: remove unused field from Hchan
Remove alignment logic as well, it's not respected by chanbuf() anyway.
R=golang-dev, dave, minux.ma, r, iant, rsc
CC=golang-dev
https://golang.org/cl/9678046
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/8bf57c3dcba00951a96de3d8ee58d844253da621
元コミット内容
runtime: remove unused field from Hchan
Remove alignment logic as well, it's not respected by chanbuf() anyway.
変更の背景
このコミットの背景には、Goランタイムの効率化とコードのクリーンアップがあります。Goのチャネルは、ゴルーチン間の安全な通信を可能にする重要なプリミティブです。その内部実装はHchan
という構造体によって管理されています。
コミットメッセージによると、Hchan
構造体にはelemalign
というフィールドが存在していましたが、これは実際には使用されていませんでした。未使用のフィールドはメモリを無駄に消費し、コードの可読性を低下させます。
また、elemalign
フィールドに関連して、チャネルのバッファのアライメントを調整するロジックが存在していましたが、このロジックはchanbuf()
関数によって適切に尊重されていなかったようです。つまり、意図したアライメントが実際には適用されておらず、そのロジック自体が無意味になっていたと考えられます。
このような状況を改善するため、未使用フィールドの削除と、機能していないアライメントロジックの除去が行われました。これにより、ランタイムのフットプリントがわずかに削減され、コードベースがより簡潔になります。
前提知識の解説
このコミットを理解するためには、以下の概念について知っておく必要があります。
-
Goチャネル (Channels): Goにおけるチャネルは、ゴルーチン間で値を送受信するための型付きコンジットです。チャネルは、同期と通信の両方を提供し、共有メモリによる競合状態を避けるためのGoの並行処理モデルの中心的な要素です。チャネルはバッファリングされる場合とされない場合があります。バッファリングされたチャネルは、指定された数の要素を格納できるキューのようなものです。
-
Hchan
構造体:Hchan
はGoランタイム内部でチャネルを表現するために使用されるC言語の構造体です。この構造体には、チャネルのバッファ、要素のサイズ、送受信インデックス、待機中のゴルーチンキューなど、チャネルの動作に必要なすべての情報が含まれています。 -
メモリのアライメント (Memory Alignment): メモリのアライメントとは、データがメモリ内で特定の境界に配置されることを保証するプロセスです。CPUは通常、特定のバイト境界(例: 4バイト、8バイト)に配置されたデータにアクセスする方が効率的です。アライメントされていないデータにアクセスすると、パフォーマンスが低下したり、一部のアーキテクチャではエラーが発生したりする可能性があります。構造体のフィールドは、その型に応じて特定のアライメント要件を持つことがあります。
-
runtime·mal
: Goランタイム内部で使用されるメモリ割り当て関数です。これはGoのガベージコレクタによって管理されるヒープメモリを割り当てます。 -
MAXALIGN
: この定数は、Goランタイムがメモリ割り当てを行う際に考慮する最大アライメント値を示します。このコミットでは、MAXALIGN
が7
から8
に変更されています。これは、64ビットシステムにおけるポインタや一部のデータ型のデフォルトアライメントが8バイトであることに対応している可能性があります。 -
chanbuf()
: チャネルのバッファに関連するランタイム内部関数であると推測されます。コミットメッセージから、この関数がアライメントロジックを適切に扱っていなかったことが示唆されています。
技術的詳細
このコミットは、src/pkg/runtime/chan.c
ファイルに対して行われています。主な変更点は以下の通りです。
-
Hchan
構造体からのelemalign
フィールドの削除: 変更前:struct Hchan { // ... bool closed; uint8 elemalign; // <-- 削除されるフィールド Alg* elemalg; // ... };
変更後:
struct Hchan { // ... uint16 elemsize; uint16 pad; // <-- 新しく追加されたパディングフィールド bool closed; Alg* elemalg; // ... };
elemalign
は要素のアライメント情報を保持する意図があったと思われますが、実際には使用されていなかったため削除されました。代わりにuint16 pad;
が追加されています。これは、Hchan
構造体の直後に続くバッファの適切なアライメントを保証するためのパディングフィールドです。elemsize
がuint16
であるため、その後にuint16
のpad
を追加することで、Hchan
構造体全体のサイズがMAXALIGN
の倍数になるように調整し、その後のバッファが適切にアライメントされるようにしています。 -
MAXALIGN
の変更:#define MAXALIGN 7
から#define MAXALIGN 8
へと変更されました。これは、64ビットシステムでのメモリ効率を向上させるため、またはより厳密なアライメント要件に対応するための変更と考えられます。 -
runtime·makechan_c
関数内のアライメント計算ロジックの削除: 変更前は、Hchan
構造体のサイズをMAXALIGN
の倍数に丸めるための以下のロジックがありました。uintptr n; // ... // calculate rounded size of Hchan n = sizeof(*c); while(n & MAXALIGN) // MAXALIGNが7の場合、n & 7 は n % 8 と同等 n++; // ... c = (Hchan*)runtime·mal(n + hint*elem->size);
このロジックは削除され、代わりに
Hchan
構造体自体の定義にpad
フィールドを追加することで、構造体のアライメントをコンパイラに任せる形になりました。これにより、runtime·mal
への呼び出しはよりシンプルになります。 変更後:c = (Hchan*)runtime·mal(sizeof(*c) + hint*elem->size);
この変更は、
Hchan
構造体内のpad
フィールドが、構造体自体のサイズをMAXALIGN
の倍数に調整する役割を担うようになったため、手動でのアライメント計算が不要になったことを意味します。 -
runtime·makechan_c
関数内のデバッグ出力の変更:elemalign
フィールドが削除されたため、デバッグ出力からもその情報が削除されました。 変更前:runtime·printf("makechan: chan=%p; elemsize=%D; elemalg=%p; elemalign=%d; dataqsiz=%D\n", c, (int64)elem->size, elem->alg, elem->align, (int64)c->dataqsiz);
変更後:
runtime·printf("makechan: chan=%p; elemsize=%D; elemalg=%p; dataqsiz=%D\n", c, (int64)elem->size, elem->alg, (int64)c->dataqsiz);
-
アライメントチェックの追加:
runtime·makechan_c
関数に新しいアライメントチェックが追加されました。if((sizeof(*c)%MAXALIGN) != 0 || elem->align > MAXALIGN) runtime·throw("makechan: bad alignment");
これは、
Hchan
構造体のサイズがMAXALIGN
の倍数であることを確認し、チャネル要素のアライメントがMAXALIGN
を超えないことを保証するためのものです。これにより、将来的なアライメントの問題を早期に検出できます。
コアとなるコードの変更箇所
--- a/src/pkg/runtime/chan.c
+++ b/src/pkg/runtime/chan.c
@@ -8,7 +8,7 @@
#include "race.h"
#include "malloc.h"
-#define MAXALIGN 7
+#define MAXALIGN 8
#define NOSELGEN 1
typedef struct WaitQ WaitQ;
@@ -38,8 +38,8 @@ struct Hchan
uintgo qcount; // total data in the q
uintgo dataqsiz; // size of the circular q
uint16 elemsize;
++ uint16 pad; // ensures proper alignment of the buffer that follows Hchan in memory
bool closed;
-- uint8 elemalign;
Alg* elemalg; // interface for element type
uintgo sendx; // send index
uintgo recx; // receive index
@@ -93,7 +93,6 @@ Hchan*
runtime·makechan_c(ChanType *t, int64 hint)
{
Hchan *c;
-- uintptr n;
Type *elem;
elem = t->elem;
@@ -101,26 +100,22 @@ runtime·makechan_c(ChanType *t, int64 hint)
// compiler checks this but be safe.
if(elem->size >= (1<<16))
runtime·throw("makechan: invalid channel element type");
++ if((sizeof(*c)%MAXALIGN) != 0 || elem->align > MAXALIGN)
++ runtime·throw("makechan: bad alignment");
if(hint < 0 || (intgo)hint != hint || (elem->size > 0 && hint > MaxMem / elem->size))
runtime·panicstring("makechan: size out of range");
-- // calculate rounded size of Hchan
-- n = sizeof(*c);
-- while(n & MAXALIGN)
-- n++;
--
// allocate memory in one call
-- c = (Hchan*)runtime·mal(n + hint*elem->size);
++ c = (Hchan*)runtime·mal(sizeof(*c) + hint*elem->size);
c->elemsize = elem->size;
c->elemalg = elem->alg;
-- c->elemalign = elem->align;
c->dataqsiz = hint;
runtime·settype(c, (uintptr)t | TypeInfo_Chan);
if(debug)
-- runtime·printf("makechan: chan=%p; elemsize=%D; elemalg=%p; elemalign=%d; dataqsiz=%D\n",
-- c, (int64)elem->size, elem->alg, elem->align, (int64)c->dataqsiz);
++ runtime·printf("makechan: chan=%p; elemsize=%D; elemalg=%p; dataqsiz=%D\n",
++ c, (int64)elem->size, elem->alg, (int64)c->dataqsiz);
return c;
}
コアとなるコードの解説
このコミットの核心は、Hchan
構造体の定義とruntime·makechan_c
関数におけるメモリ割り当てロジックの簡素化です。
-
Hchan
構造体の変更:uint8 elemalign;
が削除されました。これは、チャネル要素のアライメント情報を保持する意図があったものの、実際にはランタイムの他の部分で利用されていなかったため、冗長なフィールドでした。uint16 pad;
が追加されました。これは、elemsize
フィールド(uint16
)の直後に配置され、Hchan
構造体全体のサイズをMAXALIGN
(新しい値は8)の倍数に調整するためのパディングとして機能します。これにより、Hchan
構造体の直後に続くチャネルバッファが常に適切にアライメントされたアドレスから開始されることが保証されます。これは、特に64ビットシステムでポインタや64ビット整数などのデータ型が8バイト境界にアライメントされることを期待する場合に重要です。
-
runtime·makechan_c
関数の変更:uintptr n;
変数の削除と、それに続くHchan
構造体のサイズをMAXALIGN
の倍数に手動で丸めるロジックが削除されました。これは、Hchan
構造体自体にpad
フィールドが追加されたことで、コンパイラが構造体のアライメントを適切に処理するようになったため、手動での調整が不要になったためです。c = (Hchan*)runtime·mal(sizeof(*c) + hint*elem->size);
という行に変更されました。これにより、runtime·mal
への呼び出しが簡潔になり、Hchan
構造体の実際のサイズとチャネルバッファのサイズを直接渡すようになりました。if((sizeof(*c)%MAXALIGN) != 0 || elem->align > MAXALIGN)
という新しいアライメントチェックが追加されました。これは、Hchan
構造体のサイズがMAXALIGN
の倍数でない場合、またはチャネル要素のアライメントがMAXALIGN
を超える場合にパニックを発生させます。これにより、将来的にアライメントに関する問題が発生する可能性を早期に検出し、ランタイムの堅牢性を高めます。- デバッグ出力から
elemalign
に関する情報が削除されました。
これらの変更は、Goランタイムのメモリ管理とチャネル実装をより効率的かつクリーンにするためのものです。未使用のフィールドを削除し、冗長なアライメントロジックを排除することで、コードベースの保守性が向上し、わずかながらメモリフットプリントも削減されます。特に、chanbuf()
がアライメントを尊重していなかったという指摘は、既存のアライメントロジックが機能していなかったことを示しており、その削除は正当化されます。
関連リンク
- Go言語のチャネルに関する公式ドキュメント: https://go.dev/tour/concurrency/2
- Goのランタイムソースコード(
chan.c
が含まれるリポジトリ): https://github.com/golang/go/tree/master/src/runtime
参考にした情報源リンク
- Goのコミット履歴 (GitHub): https://github.com/golang/go/commits/master
- Goのコードレビューシステム (Gerrit): https://go.dev/cl/ (コミットメッセージに記載されている
https://golang.org/cl/9678046
は、このコミットのGerritレビューへのリンクです。) - メモリのアライメントに関する一般的な情報 (例: Wikipedia): https://ja.wikipedia.org/wiki/%E3%83%A1%E3%83%A2%E3%83%AA%E3%82%A2%E3%83%A9%E3%82%A4%E3%83%B3%E3%83%A1%E3%83%B3%E3%83%88
- Goのランタイム内部に関するブログ記事やドキュメント (一般的な検索): "Go runtime internals", "Go channel implementation" など