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

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

このコミットは、Goランタイムのメモリ管理に関連するファイル src/pkg/runtime/malloc.goc に変更を加えています。具体的には、メモリのアライメント(整列)処理において、冗長なビット演算による丸め処理を ROUND マクロの使用に置き換えることで、コードの可読性と保守性を向上させています。

コミット

commit 2ffaefd1618efda434e3176f9bff658fbe70b003
Author: Dmitriy Vyukov <dvyukov@google.com>
Date:   Thu Jun 13 16:02:50 2013 +0400

    runtime: use ROUND macro for rounding
    
    R=golang-dev, minux.ma
    CC=golang-dev
    https://golang.org/cl/10256043

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

https://github.com/golang/go/commit/2ffaefd1618efda434e3176f9bff658fbe70b003

元コミット内容

    runtime: use ROUND macro for rounding
    
    R=golang-dev, minux.ma
    CC=golang-dev
    https://golang.org/cl/10256043

変更の背景

このコミットの背景には、Goランタイムのメモリ管理コードにおける一貫性と保守性の向上が挙げられます。以前のコードでは、特定のバイト境界に値を丸める(アライメントする)ために、((value + (alignment - 1)) & ~(alignment - 1)) のようなビット演算が複数箇所で直接使用されていました。このパターンはメモリ管理において一般的ですが、コード中に散在すると冗長になり、理解しにくく、また将来的な変更やバグ修正の際にエラーを引き起こす可能性がありました。

ROUND マクロを導入することで、この共通の丸めロジックが抽象化され、コードの重複が排除されます。これにより、malloc.goc 内のメモリ割り当てやサイズ計算のロジックがより明確になり、開発者がコードの意図を素早く把握できるようになります。また、もし丸めロジック自体に変更が必要になった場合でも、ROUND マクロの定義を一度修正するだけで済むため、メンテナンスが容易になります。

前提知識の解説

1. Goランタイム (Go Runtime)

Goランタイムは、Goプログラムの実行を管理する低レベルのシステムです。これには、ガベージコレクション(GC)、スケジューラ(ゴルーチンの管理)、メモリ割り当て、システムコールインターフェースなどが含まれます。Goプログラムは、オペレーティングシステム上で直接実行されるのではなく、このランタイム上で動作します。src/pkg/runtime/ ディレクトリには、これらのランタイムのコアコンポーネントのソースコードが含まれています。

2. メモリのアライメント (Memory Alignment)

メモリのアライメントとは、データがメモリ上で特定のバイト境界に配置されることを保証するプロセスです。CPUは、特定のバイト境界(例: 4バイト、8バイト、16バイトなど)にアライメントされたデータにアクセスする方が、アライメントされていないデータにアクセスするよりも効率的です。アライメントされていないアクセスは、パフォーマンスの低下や、場合によってはハードウェアエラー(特に一部のアーキテクチャで)を引き起こす可能性があります。

例えば、4バイトのアライメントが必要な場合、データの開始アドレスは4の倍数でなければなりません。もしデータがアドレス 0x1001 から始まる場合、これは4の倍数ではないため、アライメントされていません。アライメントを保証するためには、アドレスを次の4の倍数(0x1004)に「丸める」必要があります。

3. ビット演算による丸め (Bitwise Rounding)

特定のバイト境界 N に値を X を丸める一般的なビット演算のテクニックは以下の通りです。

Y = (X + (N - 1)) & ~(N - 1)

ここで、N は2のべき乗である必要があります(例: 4, 8, 16, 256, PageSizeなど)。

  • N - 1: N が2のべき乗の場合、N - 1 はすべての下位ビットが1であるバイナリ表現になります(例: N=4 (0100b) の場合、N-1=3 (0011b))。
  • ~(N - 1): N - 1 のビットを反転させると、N の位置より下位のビットがすべて0になります(例: N=4 の場合、~(0011b) = 1100b)。これは、N の倍数を示すマスクとして機能します。
  • X + (N - 1): XN - 1 を加えることで、XN の倍数でない場合でも、次の N の倍数に「到達」するように値を持ち上げます。
  • & ~(N - 1): このビットAND演算により、N の倍数でない部分(下位ビット)が切り捨てられ、結果として N の最も近い上位の倍数に丸められます。

例: X = 10N = 4 の場合

  1. N - 1 = 3
  2. ~(N - 1) = ~3 = ...1100b (2の補数表現では 0xFFFFFFFC など)
  3. X + (N - 1) = 10 + 3 = 13 (1101b)
  4. (X + (N - 1)) & ~(N - 1) = 13 & (~3) = 1101b & ...1100b = 1100b = 12 結果として10が4の倍数である12に丸められます。

4. malloc.goc

malloc.goc はGoランタイムのメモリ割り当て(malloc)システムの一部を実装しているファイルです。Goのガベージコレクタと連携して、ヒープメモリの管理、アリーナ(大きなメモリ領域)の予約、スパン(連続したメモリページ)の管理などを行います。このファイル内の処理は、Goプログラムのパフォーマンスと安定性に直接影響します。

技術的詳細

このコミットは、Goランタイムのメモリ管理コードにおける特定のパターンを抽象化し、ROUND マクロとしてカプセル化することで、コードの品質を向上させています。

ROUND マクロは、おそらく以下のような定義を持つと推測されます(GoのC言語風のソースコードにおける慣例から):

#define ROUND(val, align) (((val) + ((align) - 1)) & ~((align) - 1))

このマクロは、与えられた valalign の倍数に切り上げる処理を行います。align は2のべき乗である必要があります。

コミットの変更点を見ると、spans_sizeneeded といったメモリ領域のサイズ計算、および want のようなアドレス計算において、この ROUND マクロが適用されています。これらの変数は、Goランタイムがヒープメモリを管理する上で重要な役割を果たします。

  • spans_size: メモリのスパン(連続したメモリページ)を管理するためのメタデータ領域のサイズ。このサイズは PageSize の倍数に丸められる必要があります。
  • arena_size: Goランタイムが使用する大きなメモリ領域(アリーナ)のサイズ。
  • needed: 新たに確保する必要があるアリーナの追加サイズ。これは 256MB の倍数に丸められています。
  • want: SysReserve(OSからメモリを予約するシステムコール)に渡す、希望する仮想アドレス。これは 1MB の倍数に丸められています。
  • nret: SetFinalizer 関数内で、戻り値のサイズを計算する際に、型のアライメントに合わせて丸められています。

これらの丸め処理を ROUND マクロに統一することで、コードの意図がより明確になり、「この値は特定のアライメントに丸められている」ということが一目でわかるようになります。これは、低レベルのシステムプログラミング、特にメモリ管理において非常に重要です。

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

変更は src/pkg/runtime/malloc.goc ファイルに集中しています。

--- a/src/pkg/runtime/malloc.goc
+++ b/src/pkg/runtime/malloc.goc
@@ -354,8 +354,7 @@ runtime·mallocinit(void)
 		arena_size = MaxMem;
 		bitmap_size = arena_size / (sizeof(void*)*8/4);
 		spans_size = arena_size / PageSize * sizeof(runtime·mheap.spans[0]);
-		// round spans_size to pages
-		spans_size = (spans_size + ((1<<PageShift) - 1)) & ~((1<<PageShift) - 1);
+		spans_size = ROUND(spans_size, PageSize);
 		for(i = 0; i <= 0x7f; i++) {
 			p = (void*)(i<<40 | 0x00c0ULL<<32);
 			p = runtime·SysReserve(p, bitmap_size + spans_size + arena_size);
@@ -389,8 +388,7 @@ runtime·mallocinit(void)
 			arena_size = bitmap_size * 8;
 			spans_size = arena_size / PageSize * sizeof(runtime·mheap.spans[0]);
 		}
-		// round spans_size to pages
-		spans_size = (spans_size + ((1<<PageShift) - 1)) & ~((1<<PageShift) - 1);
+		spans_size = ROUND(spans_size, PageSize);
 
 		// SysReserve treats the address we ask for, end, as a hint,
 		// not as an absolute requirement.  If we ask for the end
@@ -401,7 +399,7 @@ runtime·mallocinit(void)
 		// So adjust it upward a little bit ourselves: 1/4 MB to get
 		// away from the running binary image and then round up
 		// to a MB boundary.
-		want = (byte*)(((uintptr)end + (1<<18) + (1<<20) - 1)&~((1<<20)-1));
+		want = (byte*)ROUND((uintptr)end + 1<<18, 1<<20);
 		p = runtime·SysReserve(want, bitmap_size + spans_size + arena_size);
 		if(p == nil)
 			runtime·throw("runtime: cannot reserve arena virtual address space");
@@ -438,8 +436,7 @@ runtime·MHeap_SysAlloc(MHeap *h, uintptr n)
 		uintptr needed;
 
 		needed = (uintptr)h->arena_used + n - (uintptr)h->arena_end;
-		// Round wanted arena size to a multiple of 256MB.
-		needed = (needed + (256<<20) - 1) & ~((256<<20)-1);
+		needed = ROUND(needed, 256<<20);
 		new_end = h->arena_end + needed;
 		if(new_end <= h->arena_start + MaxArena32) {
 			p = runtime·SysReserve(h->arena_end, new_end - h->arena_end);
@@ -865,10 +862,9 @@ func SetFinalizer(obj Eface, finalizer Eface) {
 		// compute size needed for return parameters
 		for(i=0; i<ft->out.len; i++) {
 			t = ((Type**)ft->out.array)[i];
-			nret = (nret + t->align - 1) & ~(t->align - 1);
-			nret += t->size;
+			nret = ROUND(nret, t->align) + t->size;
 		}
-		nret = (nret + sizeof(void*)-1) & ~(sizeof(void*)-1);
+		nret = ROUND(nret, sizeof(void*));
 	}
 	
 	if(!runtime·addfinalizer(obj.data, finalizer.data, nret)) {

コアとなるコードの解説

このコミットでは、malloc.goc 内の複数の箇所で、特定のバイト境界への丸め処理が、直接的なビット演算から ROUND マクロの呼び出しへと変更されています。

  1. spans_size の丸め:

    • 変更前: spans_size = (spans_size + ((1<<PageShift) - 1)) & ~((1<<PageShift) - 1);
    • 変更後: spans_size = ROUND(spans_size, PageSize);
    • PageShift はページサイズを決定するシフト量であり、1<<PageShiftPageSize に相当します。この変更は、spans_size をメモリページの境界に丸める処理を ROUND マクロに置き換えています。これは、スパンのメタデータがページ境界に整列されることを保証し、メモリ管理の効率を高めます。
  2. want アドレスの丸め:

    • 変更前: want = (byte*)(((uintptr)end + (1<<18) + (1<<20) - 1)&~((1<<20)-1));
    • 変更後: want = (byte*)ROUND((uintptr)end + 1<<18, 1<<20);
    • 1<<18 は256KB、1<<20 は1MBを表します。この行は、SysReserve に渡す希望アドレス want を計算しています。元の end アドレスから256KBオフセットし、その結果を1MBの境界に丸めています。これは、OSがメモリを割り当てる際に、既存のバイナリイメージから十分に離れたアドレスを選択し、かつ効率的な1MB境界に整列させるための処理です。
  3. needed サイズの丸め:

    • 変更前: needed = (needed + (256<<20) - 1) & ~((256<<20)-1);
    • 変更後: needed = ROUND(needed, 256<<20);
    • 256<<20 は256MBを表します。この変更は、新たに必要となるアリーナのサイズ needed を256MBの倍数に丸める処理を ROUND マクロに置き換えています。これは、大きなメモリブロックの割り当てを効率的に行うための戦略です。
  4. SetFinalizer 内の nret の丸め:

    • 変更前: nret = (nret + t->align - 1) & ~(t->align - 1);
    • 変更後: nret = ROUND(nret, t->align) + t->size;
    • SetFinalizer 関数は、オブジェクトがガベージコレクションされる前に実行されるファイナライザを設定します。この部分では、ファイナライザの戻り値のサイズ nret を計算しています。t->align は型の推奨アライメントです。変更前は、nrett->align の倍数に丸めてから t->size を加えていましたが、変更後は ROUND マクロを使用してより簡潔に表現されています。
    • 変更前: nret = (nret + sizeof(void*)-1) & ~(sizeof(void*)-1);
    • 変更後: nret = ROUND(nret, sizeof(void*));
    • この行は、最終的な nret をポインタサイズ(sizeof(void*))の倍数に丸めています。これは、スタックフレームやレジスタに値を配置する際の効率と正確性を保証するためです。

これらの変更はすべて、特定のバイト境界への丸め処理を ROUND マクロに集約することで、コードの重複を排除し、可読性を高め、将来的なメンテナンスを容易にすることを目的としています。

関連リンク

  • Go言語のソースコード: https://github.com/golang/go
  • Goのメモリ管理に関するドキュメント(Goのバージョンによって異なる場合がありますが、一般的な概念は共通です): Goの公式ドキュメントやブログ記事を参照。

参考にした情報源リンク

  • Goのソースコード(src/pkg/runtime/malloc.goc および関連ファイル)
  • 一般的なメモリ管理とアライメントに関するプログラミングの知識
  • ビット演算による丸め処理の一般的なテクニック
  • Goのコミット履歴とコードレビューコメント (golang.org/cl/10256043)# [インデックス 16564] ファイルの概要

このコミットは、Goランタイムのメモリ管理に関連するファイル src/pkg/runtime/malloc.goc に変更を加えています。具体的には、メモリのアライメント(整列)処理において、冗長なビット演算による丸め処理を ROUND マクロの使用に置き換えることで、コードの可読性と保守性を向上させています。

コミット

commit 2ffaefd1618efda434e3176f9bff658fbe70b003
Author: Dmitriy Vyukov <dvyukov@google.com>
Date:   Thu Jun 13 16:02:50 2013 +0400

    runtime: use ROUND macro for rounding
    
    R=golang-dev, minux.ma
    CC=golang-dev
    https://golang.org/cl/10256043

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

https://github.com/golang/go/commit/2ffaefd1618efda434e3176f9bff658fbe70b003

元コミット内容

    runtime: use ROUND macro for rounding
    
    R=golang-dev, minux.ma
    CC=golang-dev
    https://golang.org/cl/10256043

変更の背景

このコミットの背景には、Goランタイムのメモリ管理コードにおける一貫性と保守性の向上が挙げられます。以前のコードでは、特定のバイト境界に値を丸める(アライメントする)ために、((value + (alignment - 1)) & ~(alignment - 1)) のようなビット演算が複数箇所で直接使用されていました。このパターンはメモリ管理において一般的ですが、コード中に散在すると冗長になり、理解しにくく、また将来的な変更やバグ修正の際にエラーを引き起こす可能性がありました。

ROUND マクロを導入することで、この共通の丸めロジックが抽象化され、コードの重複が排除されます。これにより、malloc.goc 内のメモリ割り当てやサイズ計算のロジックがより明確になり、開発者がコードの意図を素早く把握できるようになります。また、もし丸めロジック自体に変更が必要になった場合でも、ROUND マクロの定義を一度修正するだけで済むため、メンテナンスが容易になります。

前提知識の解説

1. Goランタイム (Go Runtime)

Goランタイムは、Goプログラムの実行を管理する低レベルのシステムです。これには、ガベージコレクション(GC)、スケジューラ(ゴルーチンの管理)、メモリ割り当て、システムコールインターフェースなどが含まれます。Goプログラムは、オペレーティングシステム上で直接実行されるのではなく、このランタイム上で動作します。src/pkg/runtime/ ディレクトリには、これらのランタイムのコアコンポーネントのソースコードが含まれています。

2. メモリのアライメント (Memory Alignment)

メモリのアライメントとは、データがメモリ上で特定のバイト境界に配置されることを保証するプロセスです。CPUは、特定のバイト境界(例: 4バイト、8バイト、16バイトなど)にアライメントされたデータにアクセスする方が、アライメントされていないデータにアクセスするよりも効率的です。アライメントされていないアクセスは、パフォーマンスの低下や、場合によってはハードウェアエラー(特に一部のアーキテクチャで)を引き起こす可能性があります。

例えば、4バイトのアライメントが必要な場合、データの開始アドレスは4の倍数でなければなりません。もしデータがアドレス 0x1001 から始まる場合、これは4の倍数ではないため、アライメントされていません。アライメントを保証するためには、アドレスを次の4の倍数(0x1004)に「丸める」必要があります。

3. ビット演算による丸め (Bitwise Rounding)

特定のバイト境界 N に値を X を丸める一般的なビット演算のテクニックは以下の通りです。

Y = (X + (N - 1)) & ~(N - 1)

ここで、N は2のべき乗である必要があります(例: 4, 8, 16, 256, PageSizeなど)。

  • N - 1: N が2のべき乗の場合、N - 1 はすべての下位ビットが1であるバイナリ表現になります(例: N=4 (0100b) の場合、N-1=3 (0011b))。
  • ~(N - 1): N - 1 のビットを反転させると、N の位置より下位のビットがすべて0になります(例: N=4 の場合、~(0011b) = 1100b)。これは、N の倍数を示すマスクとして機能します。
  • X + (N - 1): XN - 1 を加えることで、XN の倍数でない場合でも、次の N の倍数に「到達」するように値を持ち上げます。
  • & ~(N - 1): このビットAND演算により、N の倍数でない部分(下位ビット)が切り捨てられ、結果として N の最も近い上位の倍数に丸められます。

例: X = 10N = 4 の場合

  1. N - 1 = 3
  2. ~(N - 1) = ~3 = ...1100b (2の補数表現では 0xFFFFFFFC など)
  3. X + (N - 1) = 10 + 3 = 13 (1101b)
  4. (X + (N - 1)) & ~(N - 1) = 13 & (~3) = 1101b & ...1100b = 1100b = 12 結果として10が4の倍数である12に丸められます。

4. malloc.goc

malloc.goc はGoランタイムのメモリ割り当て(malloc)システムの一部を実装しているファイルです。Goのガベージコレクタと連携して、ヒープメモリの管理、アリーナ(大きなメモリ領域)の予約、スパン(連続したメモリページ)の管理などを行います。このファイル内の処理は、Goプログラムのパフォーマンスと安定性に直接影響します。

技術的詳細

このコミットは、Goランタイムのメモリ管理コードにおける特定のパターンを抽象化し、ROUND マクロとしてカプセル化することで、コードの品質を向上させています。

ROUND マクロは、おそらく以下のような定義を持つと推測されます(GoのC言語風のソースコードにおける慣例から):

#define ROUND(val, align) (((val) + ((align) - 1)) & ~((align) - 1))

このマクロは、与えられた valalign の倍数に切り上げる処理を行います。align は2のべき乗である必要があります。

コミットの変更点を見ると、spans_sizeneeded といったメモリ領域のサイズ計算、および want のようなアドレス計算において、この ROUND マクロが適用されています。これらの変数は、Goランタイムがヒープメモリを管理する上で重要な役割を果たします。

  • spans_size: メモリのスパン(連続したメモリページ)を管理するためのメタデータ領域のサイズ。このサイズは PageSize の倍数に丸められる必要があります。
  • arena_size: Goランタイムが使用する大きなメモリ領域(アリーナ)のサイズ。
  • needed: 新たに確保する必要があるアリーナの追加サイズ。これは 256MB の倍数に丸められています。
  • want: SysReserve(OSからメモリを予約するシステムコール)に渡す、希望する仮想アドレス。これは 1MB の倍数に丸められています。
  • nret: SetFinalizer 関数内で、戻り値のサイズを計算する際に、型のアライメントに合わせて丸められています。

これらの丸め処理を ROUND マクロに統一することで、コードの意図がより明確になり、「この値は特定のアライメントに丸められている」ということが一目でわかるようになります。これは、低レベルのシステムプログラミング、特にメモリ管理において非常に重要です。

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

変更は src/pkg/runtime/malloc.goc ファイルに集中しています。

--- a/src/pkg/runtime/malloc.goc
+++ b/src/pkg/runtime/malloc.goc
@@ -354,8 +354,7 @@ runtime·mallocinit(void)
 		arena_size = MaxMem;
 		bitmap_size = arena_size / (sizeof(void*)*8/4);
 		spans_size = arena_size / PageSize * sizeof(runtime·mheap.spans[0]);
-		// round spans_size to pages
-		spans_size = (spans_size + ((1<<PageShift) - 1)) & ~((1<<PageShift) - 1);
+		spans_size = ROUND(spans_size, PageSize);
 		for(i = 0; i <= 0x7f; i++) {
 			p = (void*)(i<<40 | 0x00c0ULL<<32);
 			p = runtime·SysReserve(p, bitmap_size + spans_size + arena_size);
@@ -389,8 +388,7 @@ runtime·mallocinit(void)
 			arena_size = bitmap_size * 8;
 			spans_size = arena_size / PageSize * sizeof(runtime·mheap.spans[0]);
 		}
-		// round spans_size to pages
-		spans_size = (spans_size + ((1<<PageShift) - 1)) & ~((1<<PageShift) - 1);
+		spans_size = ROUND(spans_size, PageSize);
 
 		// SysReserve treats the address we ask for, end, as a hint,
 		// not as an absolute requirement.  If we ask for the end
@@ -401,7 +399,7 @@ runtime·mallocinit(void)
 		// So adjust it upward a little bit ourselves: 1/4 MB to get
 		// away from the running binary image and then round up
 		// to a MB boundary.
-		want = (byte*)(((uintptr)end + (1<<18) + (1<<20) - 1)&~((1<<20)-1));
+		want = (byte*)ROUND((uintptr)end + 1<<18, 1<<20);
 		p = runtime·SysReserve(want, bitmap_size + spans_size + arena_size);
 		if(p == nil)
 			runtime·throw("runtime: cannot reserve arena virtual address space");
@@ -438,8 +436,7 @@ runtime·MHeap_SysAlloc(MHeap *h, uintptr n)
 		uintptr needed;
 
 		needed = (uintptr)h->arena_used + n - (uintptr)h->arena_end;
-		// Round wanted arena size to a multiple of 256MB.
-		needed = (needed + (256<<20) - 1) & ~((256<<20)-1);
+		needed = ROUND(needed, 256<<20);
 		new_end = h->arena_end + needed;
 		if(new_end <= h->arena_start + MaxArena32) {
 			p = runtime·SysReserve(h->arena_end, new_end - h->arena_end);
@@ -865,10 +862,9 @@ func SetFinalizer(obj Eface, finalizer Eface) {
 		// compute size needed for return parameters
 		for(i=0; i<ft->out.len; i++) {
 			t = ((Type**)ft->out.array)[i];
-			nret = (nret + t->align - 1) & ~(t->align - 1);
-			nret += t->size;
+			nret = ROUND(nret, t->align) + t->size;
 		}
-		nret = (nret + sizeof(void*)-1) & ~(sizeof(void*)-1);
+		nret = ROUND(nret, sizeof(void*));
 	}
 	
 	if(!runtime·addfinalizer(obj.data, finalizer.data, nret)) {

コアとなるコードの解説

このコミットでは、malloc.goc 内の複数の箇所で、特定のバイト境界への丸め処理が、直接的なビット演算から ROUND マクロの呼び出しへと変更されています。

  1. spans_size の丸め:

    • 変更前: spans_size = (spans_size + ((1<<PageShift) - 1)) & ~((1<<PageShift) - 1);
    • 変更後: spans_size = ROUND(spans_size, PageSize);
    • PageShift はページサイズを決定するシフト量であり、1<<PageShiftPageSize に相当します。この変更は、spans_size をメモリページの境界に丸める処理を ROUND マクロに置き換えています。これは、スパンのメタデータがページ境界に整列されることを保証し、メモリ管理の効率を高めます。
  2. want アドレスの丸め:

    • 変更前: want = (byte*)(((uintptr)end + (1<<18) + (1<<20) - 1)&~((1<<20)-1));
    • 変更後: want = (byte*)ROUND((uintptr)end + 1<<18, 1<<20);
    • 1<<18 は256KB、1<<20 は1MBを表します。この行は、SysReserve に渡す希望アドレス want を計算しています。元の end アドレスから256KBオフセットし、その結果を1MBの境界に丸めています。これは、OSがメモリを割り当てる際に、既存のバイナリイメージから十分に離れたアドレスを選択し、かつ効率的な1MB境界に整列させるための処理です。
  3. needed サイズの丸め:

    • 変更前: needed = (needed + (256<<20) - 1) & ~((256<<20)-1);
    • 変更後: needed = ROUND(needed, 256<<20);
    • 256<<20 は256MBを表します。この変更は、新たに必要となるアリーナのサイズ needed を256MBの倍数に丸める処理を ROUND マクロに置き換えています。これは、大きなメモリブロックの割り当てを効率的に行うための戦略です。
  4. SetFinalizer 内の nret の丸め:

    • 変更前: nret = (nret + t->align - 1) & ~(t->align - 1);
    • 変更後: nret = ROUND(nret, t->align) + t->size;
    • SetFinalizer 関数は、オブジェクトがガベージコレクションされる前に実行されるファイナライザを設定します。この部分では、ファイナライザの戻り値のサイズ nret を計算しています。t->align は型の推奨アライメントです。変更前は、nrett->align の倍数に丸めてから t->size を加えていましたが、変更後は ROUND マクロを使用してより簡潔に表現されています。
    • 変更前: nret = (nret + sizeof(void*)-1) & ~(sizeof(void*)-1);
    • 変更後: nret = ROUND(nret, sizeof(void*));
    • この行は、最終的な nret をポインタサイズ(sizeof(void*))の倍数に丸めています。これは、スタックフレームやレジスタに値を配置する際の効率と正確性を保証するためです。

これらの変更はすべて、特定のバイト境界への丸め処理を ROUND マクロに集約することで、コードの重複を排除し、可読性を高め、将来的なメンテナンスを容易にすることを目的としています。

関連リンク

  • Go言語のソースコード: https://github.com/golang/go
  • Goのメモリ管理に関するドキュメント(Goのバージョンによって異なる場合がありますが、一般的な概念は共通です): Goの公式ドキュメントやブログ記事を参照。

参考にした情報源リンク

  • Goのソースコード(src/pkg/runtime/malloc.goc および関連ファイル)
  • 一般的なメモリ管理とアライメントに関するプログラミングの知識
  • ビット演算による丸め処理の一般的なテクニック
  • Goのコミット履歴とコードレビューコメント (golang.org/cl/10256043)