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

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

このコミットでは、Goランタイムのガベージコレクタ(GC)のパフォーマンス改善を目的とした変更が行われています。具体的には、GCのコアロジックの簡素化、GCプロセスの並列実行数の増加、およびGCベンチマークの更新と新規追加が含まれます。

変更されたファイルは以下の通りです。

  • src/pkg/runtime/malloc.h: GCプロセスの最大数を定義する定数の変更。
  • src/pkg/runtime/mgc0.c: ガベージコレクタの主要なロジックが含まれるファイル。不要なコードの削除と簡素化が行われています。
  • test/bench/garbage/Makefile: ベンチマークのビルド設定ファイル。新しいベンチマークの追加。
  • test/bench/garbage/parser.go: 既存のGCベンチマーク。GoのAPI変更への対応。
  • test/bench/garbage/peano.go: 既存のGCベンチマーク。GoのAPI変更への対応。
  • test/bench/garbage/stats.go: 既存のGCベンチマークの統計出力関連ファイル。GoのAPI変更への対応。
  • test/bench/garbage/tree.go: 既存のGCベンチマーク。GoのAPI変更への対応。
  • test/bench/garbage/tree2.go: 新規追加されたGCベンチマーク。

コミット

commit a6d8b483b6b2a33a9a2c4deebb9e94188b3c1d23
Author: Russ Cox <rsc@golang.org>
Date:   Tue Jan 10 19:49:11 2012 -0800

    runtime: make garbage collector faster by deleting code
    
    Suggested by Sanjay Ghemawat.  5-20% faster depending
    on the benchmark.
    
    Add tree2 garbage benchmark.
    Update other garbage benchmarks to build again.
    
    R=golang-dev, r, adg
    CC=golang-dev
    https://golang.org/cl/5530074

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

https://github.com/golang/go/commit/a6d8b483b6b2a33a9a2c4deebb9e94188b3c1d23

元コミット内容

runtime: make garbage collector faster by deleting code

Suggested by Sanjay Ghemawat.  5-20% faster depending
on the benchmark.

Add tree2 garbage benchmark.
Update other garbage benchmarks to build again.

変更の背景

このコミットの主な背景は、Go言語のガベージコレクタ(GC)のパフォーマンス向上です。コミットメッセージには「コードを削除することでガベージコレクタを高速化する」と明記されており、Sanjay Ghemawat氏からの提案に基づいていることが示されています。ベンチマークによっては5%から20%の高速化が見込まれるとのことです。

Goの初期のGCは、そのシンプルさゆえに停止時間(Stop-the-World時間)が長くなる傾向がありました。そのため、Goの進化の過程でGCの効率化と停止時間の短縮は常に重要な課題でした。このコミットは、GCの内部ロジックを簡素化し、不要な処理を排除することで、その効率を改善しようとする試みの一つです。

また、変更の検証のために新しいベンチマーク(tree2)が追加され、既存のベンチマークも最新のGo APIに合わせて更新されています。これは、パフォーマンス改善が実際に測定可能であり、かつ既存のテストスイートで検証可能であることを保証するためです。

前提知識の解説

Goのガベージコレクション (GC)

Go言語は自動メモリ管理を採用しており、ガベージコレクタが不要になったメモリを自動的に解放します。GoのGCは、主に「マーク&スイープ」アルゴリズムをベースにしています。これは、プログラムが使用しているオブジェクト(到達可能なオブジェクト)をマークし、マークされなかったオブジェクト(到達不可能なオブジェクト、つまりガベージ)をスイープ(解放)するプロセスです。

GoのGCは、初期のバージョンでは「Stop-the-World (STW)」方式を採用していました。これは、GCが実行される間、アプリケーションの実行が完全に停止することを意味します。STW時間はアプリケーションの応答性に直接影響するため、Goの開発チームは常にこの時間を短縮する努力を続けてきました。

runtime パッケージ

runtime パッケージは、Goプログラムのランタイムシステムと対話するための機能を提供します。これには、ガベージコレクション、スケジューリング、メモリ割り当て、プロファイリングなどの低レベルな操作が含まれます。GCの内部実装は、このパッケージ内のC言語(またはGoの内部実装言語)で書かれたファイル群に存在します。

runtime.MemStats 構造体

runtime.MemStats は、Goプログラムのメモリ割り当てに関する統計情報を提供する構造体です。GCの実行回数、ヒープの使用量、GCによる一時停止時間などの詳細な情報が含まれており、プログラムのメモリ使用状況やGCのパフォーマンスを監視するために使用されます。

runtime.GC() 関数

runtime.GC() は、ガベージコレクタを手動で実行する関数です。通常、GCはランタイムによって自動的にトリガーされますが、この関数を呼び出すことで明示的にGCを実行させることができます。ベンチマークや特定のテストシナリオでGCの動作を制御する際に利用されます。

MHeap_LookupMaybeMHeap_Lookup

これらはGoランタイムの内部関数で、ヒープメモリ管理に関連します。Goのヒープは「スパン(span)」と呼ばれる連続したメモリブロックの集合で構成されています。MHeap_Lookup系の関数は、特定のアドレスがどのスパンに属しているか、そのスパンが管理するオブジェクトのサイズクラスは何か、といった情報を効率的に検索するために使用されます。GCがオブジェクトをスキャンする際に、そのオブジェクトのサイズや型情報を取得するためにこれらのルックアップが必要になります。

PageShift

PageShift は、メモリページサイズに関連するビットシフト値です。Goのランタイムはメモリをページ単位で管理しており、アドレスをページオフセットに変換したり、ページサイズを計算したりする際にこの値が使用されます。例えば、アドレス >> PageShift でアドレスが属するページ番号を計算できます。

GCにおけるビットフラグ (bitBlockBoundary, bitAllocated, bitMarked, bitSpecial)

GoのGCは、ヒープ上の各オブジェクトの状態を管理するために、ビットマップを使用します。これらのビットフラグは、ビットマップ内でオブジェクトの特性や状態を示すために使われます。

  • bitBlockBoundary: メモリブロックの境界を示す。
  • bitAllocated: そのメモリ領域がオブジェクトに割り当てられていることを示す。
  • bitMarked: GCのマークフェーズで、オブジェクトが到達可能(使用中)であるとマークされたことを示す。
  • bitSpecial: 特殊なオブジェクト(例えば、ファイナライザを持つオブジェクト)を示す。

これらのフラグを組み合わせることで、GCはヒープ上のオブジェクトのレイアウトと状態を効率的に把握します。

bitShift, PtrSize

  • bitShift: ビットマップのワード内で、オブジェクトのビット情報がどれだけシフトされるかを示す値。
  • PtrSize: システムのポインタサイズ(32ビットシステムでは4バイト、64ビットシステムでは8バイト)。メモリ計算やアライメントに用いられます。

arena_start

arena_start は、Goランタイムが管理するヒープアリーナ(メモリ領域)の開始アドレスを示します。ヒープ上のオブジェクトのアドレスを相対的なオフセットに変換する際などに使用されます。

runtime.GOROOT()

runtime.GOROOT() は、Goのインストールルートディレクトリのパスを返します。ベンチマークコードがGoの標準ライブラリのソースコードを読み込む際に使用されます。

ast.Package

go/ast パッケージは、Goのソースコードの抽象構文木(AST)を表現するための型を提供します。ast.Package は、Goのパッケージ全体のASTを表す構造体です。parser.go ベンチマークでは、Goのソースコードを解析してASTを構築する処理が含まれており、この型が使用されます。

os.FileInfo

os.FileInfo は、ファイルやディレクトリの情報を抽象的に表現するインターフェースです。ファイル名、サイズ、変更時刻、ディレクトリかどうかなどの情報を提供します。Goのファイルシステム操作で広く使われます。

time.Nanoseconds(), time.Now(), time.Duration

Goの初期のバージョンでは、time.Nanoseconds() のようにナノ秒単位のタイムスタンプを直接取得する関数が使われていました。しかし、Go 1.0以降、より柔軟で型安全な時間表現のために time.Now()time.Time 型の値を返し、時間の差分は time.Duration 型で表現されるようになりました。このコミットのベンチマークコードの変更は、このAPIの進化に対応したものです。

runtime.GOMAXPROCS()

runtime.GOMAXPROCS() は、Goランタイムが同時に実行できるOSスレッドの最大数を設定する関数です。Goのスケジューラは、この設定に基づいてゴルーチンをOSスレッドにマッピングします。GCの並列処理能力に影響を与えるため、GCベンチマークで重要な設定です。

pprof

runtime/pprof パッケージは、Goプログラムのプロファイリング機能を提供します。CPU使用率、メモリ割り当て、ゴルーチンのスタックトレースなどを収集し、パフォーマンスのボトルネックを特定するのに役立ちます。ベンチマークで詳細なパフォーマンス分析を行う際に利用されます。

ベンチマーク

Goにおけるベンチマークは、特定のコードのパフォーマンスを測定するためのテストです。go test -bench=. コマンドで実行され、実行時間やメモリ割り当てなどのメトリクスを報告します。GCベンチマークは、ガベージコレクタの効率、特にGCによる一時停止時間やメモリ使用量を評価するために設計されています。

技術的詳細

このコミットの技術的詳細は、主に以下の3つの側面に分けられます。

  1. ガベージコレクタのコアロジックの簡素化と高速化 (src/pkg/runtime/mgc0.c):

    • 不要な統計変数の削除: nlookup, nsizelookup, naddrlookup といったGC内部のルックアップ回数をカウントする統計変数が削除されました。これらの変数はデバッグやプロファイリングのために存在した可能性がありますが、本番環境でのパフォーマンスには寄与せず、オーバーヘッドとなっていた可能性があります。
    • オブジェクトサイズ計算ロジックの削除: scanblock 関数内で、オブジェクトのサイズをビットマップ情報から推測しようとする複雑なループ処理が削除されました。この処理は、bitBlockBoundarybitAllocated といったビットフラグを調べてオブジェクトの境界を特定しようとするものでした。
      • 削除されたコードは、arena_start からのオフセット、bitp (ビットマップポインタ)、shift (ビットシフト量) を用いて、現在のオブジェクトのビット情報 bits と、その前後のビット情報 xbits を参照し、boundary (ブロック境界を示すビット) を探していました。
      • この複雑なロジックは、最終的に「スパンにサイズクラスを問い合わせる」という既存のより信頼性の高いメカニズムにフォールバックしていました。コミットの変更は、この複雑な推測ロジックを完全に削除し、常にスパン情報に依存するようにすることで、GCのパスを簡素化し、オーバーヘッドを削減しています。これにより、GCがオブジェクトのサイズを決定する際のパスが短縮され、高速化に繋がります。
  2. GCプロセスの並列実行数の増加 (src/pkg/runtime/malloc.h):

    • MaxGcproc 定数が 2 から 4 に変更されました。これは、ガベージコレクタが同時に利用できるCPU(またはゴルーチン)の最大数を意味します。
    • コミットメッセージのコメントにもあるように、「ガベージコレクタは4つのCPUでうまくスケールする」という知見に基づいています。これにより、マルチコアプロセッサ環境において、GCの並列処理能力が向上し、GCの実行時間を短縮できる可能性が高まります。特に、GCのマークフェーズなど、並列化が可能な処理において効果が期待されます。
  3. ベンチマークの更新と新規追加 (test/bench/garbage/*):

    • APIの更新: 既存のベンチマークファイル (parser.go, peano.go, tree.go, stats.go) では、Goの標準ライブラリAPIの変更に対応しています。
      • os.FileInfo の使用方法が *os.FileInfo から os.FileInfo に変更され、メソッド呼び出しも dir.Name から dir.Name() に更新されています。これは、Goのインターフェースの進化と、構造体フィールドからメソッドへのアクセス変更を反映しています。
      • 時間計測において、time.Nanoseconds() から time.Now()time.Duration を使用する形式に移行しています。これは、Goの time パッケージのより現代的な使用方法に準拠したものです。
      • パッケージのインポートパスが変更されています(例: asn1 -> encoding/asn1, big -> math/big, http -> net/http, json -> encoding/json など)。これは、Goの標準ライブラリのパッケージ構成が整理され、より論理的なパスに移動したことを示しています。
    • tree2 ベンチマークの追加: test/bench/garbage/tree2.go という新しいベンチマークが追加されました。
      • このベンチマークは、BranchingFactor を持つ Object 構造体を使用して、ツリー状のヒープ構造を構築し、GCのパフォーマンスを測定します。
      • buildHeapbuildTree 関数で、指定されたヒープサイズに基づいて再帰的にオブジェクトツリーを構築します。
      • gc() 関数は、runtime.GC() を呼び出し、runtime.MemStats を更新して、GCの一時停止時間、収集されたメモリ量、ヒープサイズなどの統計情報を出力します。
      • main 関数では、runtime.GOMAXPROCS を設定し、pprof を使用してCPUプロファイルを収集するオプションも提供しています。
      • この新しいベンチマークは、特に複雑なオブジェクトグラフを持つアプリケーションにおけるGCの挙動とパフォーマンスを評価するために設計されたと考えられます。

これらの変更は、GoのGCがより効率的でスケーラブルになるように、内部ロジックの最適化と並列処理の活用を進める一環として行われたものです。

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

このコミットのコアとなるコードの変更は、主に src/pkg/runtime/mgc0.c に集中しています。

src/pkg/runtime/malloc.h の変更

--- a/src/pkg/runtime/malloc.h
+++ b/src/pkg/runtime/malloc.h
@@ -123,10 +123,9 @@ enum
 
 	// Max number of threads to run garbage collection.
 	// 2, 3, and 4 are all plausible maximums depending
-\t// on the hardware details of the machine.  The second
-\t// proc is the one that helps the most (after the first),\n-\t// so start with just 2 for now.\n-\tMaxGcproc = 2,\n+\t// on the hardware details of the machine.  The garbage
+\t// collector scales well to 4 cpus.\n+\tMaxGcproc = 4,\n };

MaxGcproc2 から 4 に変更され、GCが利用できる並列プロセスの最大数が増加しました。

src/pkg/runtime/mgc0.c の変更

最も重要な変更は、scanblock 関数内のオブジェクトサイズ計算ロジックの削除です。

--- a/src/pkg/runtime/mgc0.c
+++ b/src/pkg/runtime/mgc0.c
@@ -301,49 +296,8 @@ scanblock(byte *b, int64 n)\
 	\tb = *--wp;\n \t\tnobj--;\n \n-\t\t// Figure out n = size of b.  Start by loading bits for b.\n-\t\toff = (uintptr*)b - (uintptr*)arena_start;\n-\t\tbitp = (uintptr*)arena_start - off/wordsPerBitmapWord - 1;\n-\t\tshift = off % wordsPerBitmapWord;\n-\t\txbits = *bitp;\n-\t\tbits = xbits >> shift;\n-\n-\t\t// Might be small; look for nearby block boundary.\n-\t\t// A block boundary is marked by either bitBlockBoundary\n-\t\t// or bitAllocated being set (see notes near their definition).\n-\t\tenum {\n-\t\t\tboundary = bitBlockBoundary|bitAllocated\n-\t\t};\n-\t\t// Look for a block boundary both after and before b\n-\t\t// in the same bitmap word.\n-\t\t//\n-\t\t// A block boundary j words after b is indicated by\n-\t\t//\tbits>>j & boundary\n-\t\t// assuming shift+j < bitShift.  (If shift+j >= bitShift then\n-\t\t// we\'ll be bleeding other bit types like bitMarked into our test.)\n-\t\t// Instead of inserting the conditional shift+j < bitShift into the loop,\n-\t\t// we can let j range from 1 to bitShift as long as we first\n-\t\t// apply a mask to keep only the bits corresponding\n-\t\t// to shift+j < bitShift aka j < bitShift-shift.\n-\t\tbits &= (boundary<<(bitShift-shift)) - boundary;\n-\n-\t\t// A block boundary j words before b is indicated by\n-\t\t//\txbits>>(shift-j) & boundary\n-\t\t// (assuming shift >= j).  There is no cleverness here\n-\t\t// avoid the test, because when j gets too large the shift\n-\t\t// turns negative, which is undefined in C.\n-\n-\t\tfor(j=1; j<bitShift; j++) {\n-\t\t\tif(((bits>>j)&boundary) != 0 || shift>=j && ((xbits>>(shift-j))&boundary) != 0) {\n-\t\t\t\tn = j*PtrSize;\n-\t\t\t\tgoto scan;\n-\t\t\t}\n-\t\t}\n-\n-\t\t// Fall back to asking span about size class.\n+\t\t// Ask span about size class.\n \t\t// (Manually inlined copy of MHeap_Lookup.)\n-\t\tnlookup++;\n-\t\tnsizelookup++;\n \t\tx = (uintptr)b>>PageShift;\n \t\tif(sizeof(void*) == 8)\n \t\t\tx -= (uintptr)arena_start>>PageShift;\n@@ -352,7 +306,6 @@ scanblock(byte *b, int64 n)\
 \t\t\tn = s->npages<<PageShift;\n \t\telse\n \t\t\tn = runtime·class_to_size[s->sizeclass];\n-\tscan:;\n \t}\n }\n```
この差分は、`scanblock` 関数から約50行にわたる複雑なオブジェクトサイズ推測ロジックが削除されたことを示しています。このロジックは、ビットマップを直接調べてオブジェクトの境界を特定しようとするものでしたが、最終的にはスパン情報にフォールバックしていました。変更後は、常にスパン情報 (`MHeap_Lookup` の手動インラインコピー) を使用してオブジェクトサイズを取得するようになり、コードが大幅に簡素化されました。

また、GC統計情報に関する変数の削除も行われています。
```diff
--- a/src/pkg/runtime/mgc0.c
+++ b/src/pkg/runtime/mgc0.c
@@ -53,9 +53,6 @@ enum {\
 #define bitMask (bitBlockBoundary | bitAllocated | bitMarked | bitSpecial)\
 \n // TODO: Make these per-M.\
-static uint64 nlookup;\
-static uint64 nsizelookup;\
-static uint64 naddrlookup;\
 static uint64 nhandoff;\
 \n static int32 gctrace;\
@@ -212,8 +209,6 @@ scanblock(byte *b, int64 n)\
 \n \t\t\t// Otherwise consult span table to find beginning.\
 \t\t\t// (Manually inlined copy of MHeap_LookupMaybe.)\
-\t\t\tnlookup++;\n-\t\t\tnaddrlookup++;\
 \t\t\tk = (uintptr)obj>>PageShift;\
 \t\t\tx = k;\
 \t\t\tif(sizeof(void*) == 8)\
@@ -953,9 +906,6 @@ runtime·gc(int32 force)\
 \t}\n \n \tt0 = runtime·nanotime();\n-\tnlookup = 0;\n-\tnsizelookup = 0;\n-\tnaddrlookup = 0;\
 \tnhandoff = 0;\
 \n \tm->gcing = 1;\
@@ -1020,11 +970,11 @@ runtime·gc(int32 force)\
 \t\truntime·printf(\"pause %D\\n\", t3-t0);\
 \n \tif(gctrace) {\n-\t\truntime·printf(\"gc%d(%d): %D+%D+%D ms %D -> %D MB %D -> %D (%D-%D) objects %D pointer lookups (%D size, %D addr) %D handoff\\n\",\n+\t\truntime·printf(\"gc%d(%d): %D+%D+%D ms %D -> %D MB %D -> %D (%D-%D) objects %D handoff\\n\",\n \t\t\tmstats.numgc, work.nproc, (t1-t0)/1000000, (t2-t1)/1000000, (t3-t2)/1000000,\n \t\t\theap0>>20, heap1>>20, obj0, obj1,\n \t\t\tmstats.nmalloc, mstats.nfree,\n-\t\t\tnlookup, nsizelookup, naddrlookup, nhandoff);\n+\t\t\tnhandoff);\
 \t}\n \n \truntime·semrelease(&gcsema);\

nlookup, nsizelookup, naddrlookup の各変数の宣言、インクリメント、およびGCトレース出力からの削除が行われています。

test/bench/garbage/tree2.go の新規追加

このファイルは、新しいGCベンチマーク tree2 を定義しています。

// Copyright 2012 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"runtime"
	"runtime/pprof"
	"unsafe"
)

const BranchingFactor = 4

type Object struct {
	child [BranchingFactor]*Object
}

var (
	cpus       = flag.Int("cpus", 1, "number of cpus to use")
	heapsize   = flag.Int64("heapsize", 100*1024*1024, "size of the heap in bytes")
	cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")

	lastPauseNs uint64 = 0
	lastFree    uint64 = 0
	heap        *Object
	calls       [20]int
	numobjects  int64
)

func buildHeap() {
	objsize := int64(unsafe.Sizeof(Object{}))
	heap, _ = buildTree(float64(objsize), float64(*heapsize), 0)
	fmt.Printf("*** built heap: %.0f MB; (%d objects * %d bytes)\n",
		float64(*heapsize)/1048576, numobjects, objsize)
}

func buildTree(objsize, size float64, depth int) (*Object, float64) {
	calls[depth]++
	x := &Object{}
	numobjects++
	subtreeSize := (size - objsize) / BranchingFactor
	alloc := objsize
	for i := 0; i < BranchingFactor && alloc < size; i++ {
		c, n := buildTree(objsize, subtreeSize, depth+1)
		x.child[i] = c
		alloc += n
	}
	return x, alloc
}

func gc() {
	runtime.GC()
	runtime.UpdateMemStats()
	pause := runtime.MemStats.PauseTotalNs
	inuse := runtime.MemStats.Alloc
	free := runtime.MemStats.TotalAlloc - inuse
	fmt.Printf("gc pause: %8.3f ms; collect: %8.0f MB; heapsize: %8.0f MB\n",
		float64(pause-lastPauseNs)/1e6,
		float64(free-lastFree)/1048576,
		float64(inuse)/1048576)
	lastPauseNs = pause
	lastFree = free
}

func main() {
	flag.Parse()
	buildHeap()
	runtime.GOMAXPROCS(*cpus)
	runtime.UpdateMemStats()
	lastPauseNs = runtime.MemStats.PauseTotalNs
	lastFree = runtime.MemStats.TotalAlloc - runtime.MemStats.Alloc
	if *cpuprofile != "" {
		f, err := os.Create(*cpuprofile)
		if err != nil {
			log.Fatal(err)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}
	for i := 0; i < 10; i++ {
		gc()
	}
}

コアとなるコードの解説

このコミットの核心は、Goランタイムのガベージコレクタにおけるオブジェクトサイズ特定ロジックの簡素化と、GCの並列処理能力の向上です。

src/pkg/runtime/mgc0.cscanblock 関数は、GCがヒープをスキャンする際に、各オブジェクトのサイズを決定するために使用されます。変更前は、この関数内でオブジェクトのアドレスからビットマップ情報を直接読み取り、複雑なビット操作とループを用いてオブジェクトの境界を推測しようとするロジックが存在しました。この推測ロジックは、最終的に MHeap_Lookup (スパン情報からサイズを取得する) にフォールバックするものでした。

このコミットでは、この複雑で冗長なビットマップベースの推測ロジックが完全に削除されました。これにより、scanblock は常に MHeap_Lookup の手動インラインコピー(スパン情報に基づくルックアップ)を使用してオブジェクトサイズを決定するようになりました。この「コードの削除」は、GCの実行パスから不要な計算と分岐を取り除くことで、オーバーヘッドを削減し、GCの処理速度を向上させる効果があります。コミットメッセージにある「5-20%の高速化」は、この簡素化によるものと考えられます。

また、src/pkg/runtime/malloc.hMaxGcproc2 から 4 に増加したことは、GCがより多くのCPUコアを並列に利用できるようになったことを意味します。これにより、特にマルチコア環境において、GCのマークフェーズやスイープフェーズなどの並列化可能な処理が効率的に実行され、GCの停止時間(STW時間)の短縮に貢献します。

ベンチマークの更新と新規追加は、これらのGC改善が実際にパフォーマンスに寄与していることを検証するための重要なステップです。特に tree2.go は、複雑なツリー構造を持つオブジェクトを大量に生成し、GCの負荷が高いシナリオをシミュレートすることで、GCの効率改善をより正確に測定できるように設計されています。既存のベンチマークのAPI更新は、Go言語自体の進化(time パッケージや標準ライブラリのパッケージパス変更など)に対応するためのものであり、GCの変更とは直接関係ありませんが、ベンチマークが最新のGo環境で正しく動作するために必要不可欠な変更です。

総じて、このコミットは、Goのガベージコレクタの内部実装をより効率的かつスケーラブルにするための、重要な最適化の一歩を示しています。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント (runtimeパッケージ、timeパッケージなど)
  • Goのガベージコレクションに関する技術記事やブログ (GoのGCの歴史と進化について理解を深めるため)
  • Goのベンチマークに関する公式ドキュメントやチュートリアル
  • Goのソースコード (特に src/runtime ディレクトリ内のファイル)
  • Goのコミット履歴とGerritレビューシステム (変更の背景や議論を理解するため)
  • Goの初期のGC実装に関する情報 (STW GCの課題と改善の歴史を理解するため)
  • Goのメモリ管理に関する技術解説 (スパン、ビットマップなどの概念を理解するため)
  • Goの unsafe パッケージに関するドキュメント (ベンチマークコードで unsafe.Sizeof が使用されているため)
  • Goの pprof ツールに関するドキュメント (プロファイリングの概念と使用方法を理解するため)
  • Goの os パッケージに関するドキュメント (os.FileInfo の変更を理解するため)
  • Goの go/ast パッケージに関するドキュメント (ASTの概念を理解するため)
  • Goのパッケージ構成の変更に関する情報 (パッケージパスの変更を理解するため)