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

[インデックス 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()関数によって適切に尊重されていなかったようです。つまり、意図したアライメントが実際には適用されておらず、そのロジック自体が無意味になっていたと考えられます。

このような状況を改善するため、未使用フィールドの削除と、機能していないアライメントロジックの除去が行われました。これにより、ランタイムのフットプリントがわずかに削減され、コードベースがより簡潔になります。

前提知識の解説

このコミットを理解するためには、以下の概念について知っておく必要があります。

  1. Goチャネル (Channels): Goにおけるチャネルは、ゴルーチン間で値を送受信するための型付きコンジットです。チャネルは、同期と通信の両方を提供し、共有メモリによる競合状態を避けるためのGoの並行処理モデルの中心的な要素です。チャネルはバッファリングされる場合とされない場合があります。バッファリングされたチャネルは、指定された数の要素を格納できるキューのようなものです。

  2. Hchan構造体: HchanはGoランタイム内部でチャネルを表現するために使用されるC言語の構造体です。この構造体には、チャネルのバッファ、要素のサイズ、送受信インデックス、待機中のゴルーチンキューなど、チャネルの動作に必要なすべての情報が含まれています。

  3. メモリのアライメント (Memory Alignment): メモリのアライメントとは、データがメモリ内で特定の境界に配置されることを保証するプロセスです。CPUは通常、特定のバイト境界(例: 4バイト、8バイト)に配置されたデータにアクセスする方が効率的です。アライメントされていないデータにアクセスすると、パフォーマンスが低下したり、一部のアーキテクチャではエラーが発生したりする可能性があります。構造体のフィールドは、その型に応じて特定のアライメント要件を持つことがあります。

  4. runtime·mal: Goランタイム内部で使用されるメモリ割り当て関数です。これはGoのガベージコレクタによって管理されるヒープメモリを割り当てます。

  5. MAXALIGN: この定数は、Goランタイムがメモリ割り当てを行う際に考慮する最大アライメント値を示します。このコミットでは、MAXALIGN7から8に変更されています。これは、64ビットシステムにおけるポインタや一部のデータ型のデフォルトアライメントが8バイトであることに対応している可能性があります。

  6. chanbuf(): チャネルのバッファに関連するランタイム内部関数であると推測されます。コミットメッセージから、この関数がアライメントロジックを適切に扱っていなかったことが示唆されています。

技術的詳細

このコミットは、src/pkg/runtime/chan.cファイルに対して行われています。主な変更点は以下の通りです。

  1. Hchan構造体からのelemalignフィールドの削除: 変更前:

    struct Hchan
    {
        // ...
        bool    closed;
        uint8   elemalign; // <-- 削除されるフィールド
        Alg*    elemalg;
        // ...
    };
    

    変更後:

    struct Hchan
    {
        // ...
        uint16  elemsize;
        uint16  pad;       // <-- 新しく追加されたパディングフィールド
        bool    closed;
        Alg*    elemalg;
        // ...
    };
    

    elemalignは要素のアライメント情報を保持する意図があったと思われますが、実際には使用されていなかったため削除されました。代わりにuint16 pad;が追加されています。これは、Hchan構造体の直後に続くバッファの適切なアライメントを保証するためのパディングフィールドです。elemsizeuint16であるため、その後にuint16padを追加することで、Hchan構造体全体のサイズがMAXALIGNの倍数になるように調整し、その後のバッファが適切にアライメントされるようにしています。

  2. MAXALIGNの変更: #define MAXALIGN 7 から #define MAXALIGN 8 へと変更されました。これは、64ビットシステムでのメモリ効率を向上させるため、またはより厳密なアライメント要件に対応するための変更と考えられます。

  3. 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の倍数に調整する役割を担うようになったため、手動でのアライメント計算が不要になったことを意味します。

  4. 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);
    
  5. アライメントチェックの追加: 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関数におけるメモリ割り当てロジックの簡素化です。

  1. Hchan構造体の変更:

    • uint8 elemalign;が削除されました。これは、チャネル要素のアライメント情報を保持する意図があったものの、実際にはランタイムの他の部分で利用されていなかったため、冗長なフィールドでした。
    • uint16 pad;が追加されました。これは、elemsizeフィールド(uint16)の直後に配置され、Hchan構造体全体のサイズをMAXALIGN(新しい値は8)の倍数に調整するためのパディングとして機能します。これにより、Hchan構造体の直後に続くチャネルバッファが常に適切にアライメントされたアドレスから開始されることが保証されます。これは、特に64ビットシステムでポインタや64ビット整数などのデータ型が8バイト境界にアライメントされることを期待する場合に重要です。
  2. 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()がアライメントを尊重していなかったという指摘は、既存のアライメントロジックが機能していなかったことを示しており、その削除は正当化されます。

関連リンク

参考にした情報源リンク