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

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

このコミットは、Goランタイムにおけるfloat64のルーチンをインライン化し、complex128の除算性能を向上させることを目的としています。具体的には、浮動小数点数の特殊値(NaN, Inf)の判定や生成に関する関数呼び出しを削減し、直接的な比較やマクロに置き換えることで、オーバーヘッドを低減しています。

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

  • src/pkg/runtime/complex.c: complex128の除算ロジックにおけるNaN/Inf判定の変更。
  • src/pkg/runtime/float.c: 多数のfloat64関連ユーティリティ関数(float64tobits, float64frombits, isInf, isNaN, NaN, Inf, frexp, ldexp, modf)の削除と、NaN/Inf定数の定義。
  • src/pkg/runtime/print.c: 浮動小数点数の出力におけるNaN/Inf判定の変更。
  • src/pkg/runtime/runtime.h: 新しいNaN/Inf定数の宣言と、ISNANマクロの追加。

コミット

commit 0157c72d133471631c13419f61117b75dcd7c255
Author: Shenghou Ma <minux.ma@gmail.com>
Date:   Tue Aug 7 23:45:50 2012 +0800

    runtime: inline several float64 routines to speed up complex128 division
    Depends on CL 6197045.
    
    Result obtained on Core i7 620M, Darwin/amd64:
    benchmark                       old ns/op    new ns/op    delta
    BenchmarkComplex128DivNormal           57           28  -50.78%
    BenchmarkComplex128DivNisNaN           49           15  -68.90%
    BenchmarkComplex128DivDisNaN           49           15  -67.88%
    BenchmarkComplex128DivNisInf           40           12  -68.50%
    BenchmarkComplex128DivDisInf           33           13  -61.06%
    
    Result obtained on Core i7 620M, Darwin/386:
    benchmark                       old ns/op    new ns/op    delta
    BenchmarkComplex128DivNormal           89           50  -44.05%
    BenchmarkComplex128DivNisNaN          307          802  +161.24%
    BenchmarkComplex128DivDisNaN          309          788  +155.02%
    BenchmarkComplex128DivNisInf          278          237  -14.75%
    BenchmarkComplex128DivDisInf           46           22  -52.46%
    
    Result obtained on 700MHz OMAP4460, Linux/ARM:
    benchmark                       old ns/op    new ns/op    delta
    BenchmarkComplex128DivNormal         1557          465  -70.13%
    BenchmarkComplex128DivNisNaN         1443          220  -84.75%
    BenchmarkComplex128DivDisNaN         1481          218  -85.28%
    BenchmarkComplex128DivNisInf          952          216  -77.31%
    BenchmarkComplex128DivDisInf          861          231  -73.17%
    
    The 386 version has a performance regression, but as we have
    decided to use SSE2 instead of x87 FPU for 386 too (issue 3912),
    I won't address this issue.
    
    R=dsymonds, mchaten, iant, dave, mtj, rsc, r
    CC=golang-dev
    https://golang.org/cl/6024045

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

https://github.com/golang/go/commit/0157c72d133471631c13419f61117b75dcd7c255

元コミット内容

Goランタイムにおいて、いくつかのfloat64ルーチンをインライン化することで、complex128の除算を高速化する。この変更はCL 6197045に依存する。

ベンチマーク結果(Core i7 620M, Darwin/amd64, Darwin/386, 700MHz OMAP4460, Linux/ARM)が示されており、多くのケースで大幅な性能向上が見られる。ただし、Darwin/386環境ではBenchmarkComplex128DivNisNaNBenchmarkComplex128DivDisNaNで性能劣化が発生している。しかし、386環境でもx87 FPUの代わりにSSE2を使用する決定がなされているため(issue 3912)、この性能劣化は対処しない方針である。

変更の背景

このコミットの主な背景は、Go言語のランタイムにおけるcomplex128(複素数)の除算処理の性能改善です。特に、浮動小数点数の特殊な値(非数: NaN, 無限大: Inf)の扱いが、関数呼び出しのオーバーヘッドによってパフォーマンスボトルネックとなっていたと考えられます。

Goのランタイムは、言語の基本的な操作を効率的に実行するためにC言語で記述された部分が多く含まれています。浮動小数点演算は、その性質上、NaNやInfといった特殊なケースを適切に処理する必要があります。従来のGoランタイムでは、これらの特殊値の判定や生成に専用の関数(例: runtime·isInf, runtime·isNaN, runtime·NaN, runtime·Inf)を使用していました。

しかし、これらの関数呼び出しは、特に頻繁に実行されるcomplex128の除算のような演算において、無視できないオーバーヘッドを生じさせます。関数呼び出しには、スタックフレームの作成、引数の渡し、戻り値の処理など、一定のコストが伴います。このコストが積み重なることで、全体のパフォーマンスが低下します。

このコミットでは、これらの関数呼び出しを削減し、より直接的な方法(インライン化、マクロ、直接比較)で特殊値の処理を行うことで、パフォーマンスの向上を図っています。コミットメッセージに示されているベンチマーク結果は、特にamd64ARMアーキテクチャにおいて、complex128の除算が大幅に高速化されたことを明確に示しています。

また、386アーキテクチャにおける一部のベンチマークで性能劣化が見られますが、これは当時のGo開発チームが386環境でもx87 FPUではなくSSE2命令セットを使用する方針に転換したため、一時的なものとして許容された経緯があります(issue 3912)。これは、より新しい命令セットを活用することで、長期的なパフォーマンスとコードの簡潔性を追求するGoの設計思想を反映しています。

前提知識の解説

浮動小数点数 (float64)

float64は、IEEE 754倍精度浮動小数点数形式で表現される数値です。Go言語ではfloat64型として扱われます。これは、実数をコンピュータで近似的に表現するための標準的な方法であり、非常に広い範囲の数値を表現できますが、精度には限界があります。

複素数 (complex128)

complex128は、Go言語における倍精度複素数型です。実部と虚部がそれぞれfloat64で構成されます。数学的な複素数演算(加算、減算、乗算、除算など)をサポートします。

NaN (Not a Number)

「非数」と訳され、浮動小数点演算の結果が未定義または表現不能な場合に生成される特殊な値です。例えば、0/0Inf - Infのような演算結果がNaNになります。IEEE 754規格では、NaNは自身との比較で常に偽となるという特性があります(NaN == NaNは偽)。

Inf (Infinity)

「無限大」と訳され、浮動小数点演算の結果が表現可能な最大値を超えた場合に生成される特殊な値です。正の無限大(+Inf)と負の無限大(-Inf)があります。例えば、1/0+Inf-1/0-Infになります。

インライン化 (Inlining)

プログラミングにおける最適化手法の一つで、関数呼び出しのオーバーヘッドを削減するために、呼び出される関数のコードを呼び出し元に直接埋め込むことです。これにより、関数呼び出しに伴うスタック操作やジャンプ命令が不要になり、実行速度が向上する可能性があります。ただし、コードサイズが増加する、キャッシュ効率が悪化するなどのデメリットもあります。コンパイラが自動的に行う場合と、プログラマが明示的に指示する場合があります。このコミットでは、GoランタイムのCコードにおいて、手動で関数呼び出しを削除し、そのロジックを呼び出し元に直接記述することでインライン化に相当する効果を得ています。

SSE2 (Streaming SIMD Extensions 2)

Intelが開発したSIMD(Single Instruction, Multiple Data)命令セットの一つです。主にPentium 4プロセッサで導入され、浮動小数点演算や整数演算を高速化するための命令を含んでいます。特に、複数のデータを同時に処理できるため、メディア処理や科学技術計算などで高いパフォーマンスを発揮します。x87 FPUに比べて、より効率的な浮動小数点演算が可能です。

x87 FPU (Floating-Point Unit)

Intel 8087コプロセッサに由来する、x86アーキテクチャにおける浮動小数点演算ユニットの古い実装です。スタックベースのレジスタモデルを使用し、倍精度浮動小数点演算をサポートします。SSEなどの新しい命令セットが登場するまで、x86プロセッサの主要な浮動小数点演算機構でした。しかし、SSEに比べて効率が劣り、特に現代の高性能計算ではボトルネックとなることがあります。

技術的詳細

このコミットの核心は、Goランタイムにおける浮動小数点数の特殊値(NaN, Inf)の処理方法を、関数呼び出しベースから直接的な比較やマクロベースへと変更することで、complex128の除算性能を向上させる点にあります。

1. src/pkg/runtime/float.c の大幅な変更

このファイルは、浮動小数点数に関する低レベルのユーティリティ関数を多数提供していました。このコミットでは、以下の関数が削除されています。

  • runtime·float32tobits, runtime·float64tobits: 浮動小数点数をビット表現に変換する関数。
  • runtime·float64frombits, runtime·float32frombits: ビット表現から浮動小数点数に変換する関数。
  • runtime·isInf, runtime·isNaN: それぞれ無限大、非数であるかを判定する関数。
  • runtime·NaN, runtime·Inf: それぞれNaN、無限大の値を生成する関数。
  • runtime·frexp, runtime·ldexp, runtime·modf: 浮動小数点数の指数部と仮数部を操作する関数(C標準ライブラリの同名関数に相当)。

これらの関数は、Goランタイム内部で浮動小数点数のビットパターンを直接操作したり、特殊値を生成したりするために使用されていました。しかし、関数呼び出しのオーバーヘッドが性能に影響を与えていたため、これらを削除し、より直接的な方法で同等の機能を実現しています。

代わりに、src/pkg/runtime/float.cには、NaN、正の無限大、負の無限大を表すuint64定数が直接定義されています。

+uint64	·nan		= 0x7FF8000000000001ULL;
+uint64	·posinf	= 0x7FF0000000000000ULL;
+uint64	·neginf	= 0xFFF0000000000000ULL;

これらの定数は、runtime·nan, runtime·posinf, runtime·neginfという名前でfloat64として外部に公開されます。これにより、特殊値を生成する際にruntime·float64frombitsのような関数を介さず、直接これらの定数を使用できるようになります。

2. src/pkg/runtime/runtime.h における変更

runtime.hには、上記の新しいfloat64定数(runtime·nan, runtime·posinf, runtime·neginf)のextern宣言が追加されています。

さらに重要な変更として、ISNANマクロが追加されました。

+#define ISNAN(f) ((f) != (f))

このマクロは、IEEE 754規格におけるNaNの特性(NaNは自身との比較で常に偽となる)を利用して、浮動小数点数がNaNであるかを判定します。これにより、runtime·isNaN(f)という関数呼び出しが不要になり、コンパイラが直接比較命令を生成できるようになります。これは、特にパフォーマンスが要求される場面で非常に効果的です。

3. src/pkg/runtime/complex.c における変更

complex128の除算を行うruntime·complex128div関数内で、NaNやInfの判定ロジックが変更されています。

  • Inf判定:
    • 変更前: runtime·isInf(n.real, 0) のような関数呼び出しを使用。
    • 変更後: n.real == runtime·posinf || n.real == runtime·neginf のような直接比較を使用。
  • NaN判定:
    • 変更前: runtime·isNaN(n.real) のような関数呼び出しを使用。
    • 変更後: ISNAN(n.real) マクロを使用。
  • NaN/Inf値の生成:
    • 変更前: runtime·NaN(), runtime·Inf(0) のような関数呼び出しを使用。
    • 変更後: runtime·nan, runtime·posinf のような直接的な定数を使用。

これらの変更により、complex128の除算処理における浮動小数点数の特殊値のハンドリングが、関数呼び出しのオーバーヘッドなしに、より効率的に行われるようになりました。

4. src/pkg/runtime/print.c における変更

浮動小数点数を文字列として出力するruntime·printfloat関数でも、NaNやInfの判定ロジックが変更されています。

  • NaN判定:
    • 変更前: runtime·isNaN(v)
    • 変更後: ISNAN(v)
  • Inf判定:
    • 変更前: runtime·isInf(v, 1), runtime·isInf(v, -1)
    • 変更後: v == runtime·posinf, v == runtime·neginf

これにより、出力ルーチンも高速化され、ランタイム全体のパフォーマンスに寄与します。

まとめ

このコミットは、Goランタイムの低レベルな部分において、浮動小数点数の特殊値処理に関する関数呼び出しを徹底的に排除し、直接的なビットパターン比較、マクロ、および定数によるアクセスに置き換えることで、complex128の除算という計算負荷の高い操作の性能を劇的に向上させています。これは、Goがパフォーマンスを重視する言語であることを示す典型的な最適化の例と言えます。

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

src/pkg/runtime/complex.c

--- a/src/pkg/runtime/complex.c
+++ b/src/pkg/runtime/complex.c
@@ -13,28 +13,30 @@ runtime·complex128div(Complex128 n, Complex128 d, Complex128 q)
 	float64 a, b, ratio, denom;
 
 	// Special cases as in C99.
-	ninf = runtime·isInf(n.real, 0) || runtime·isInf(n.imag, 0);
-	dinf = runtime·isInf(d.real, 0) || runtime·isInf(d.imag, 0);
+	ninf = n.real == runtime·posinf || n.real == runtime·neginf ||
+	       n.imag == runtime·posinf || n.imag == runtime·neginf;
+	dinf = d.real == runtime·posinf || d.real == runtime·neginf ||
+	       d.imag == runtime·posinf || d.imag == runtime·neginf;
 
-	nnan = !ninf && (runtime·isNaN(n.real) || runtime·isNaN(n.imag));
-	dnan = !dinf && (runtime·isNaN(d.real) || runtime·isNaN(d.imag));
+	nnan = !ninf && (ISNAN(n.real) || ISNAN(n.imag));
+	dnan = !dinf && (ISNAN(d.real) || ISNAN(d.imag));
 
 	if(nnan || dnan) {
-		q.real = runtime·NaN();
-		q.imag = runtime·NaN();
-	} else if(ninf && !dinf && !dnan) {
-		q.real = runtime·Inf(0);
-		q.imag = runtime·Inf(0);
-	} else if(!ninf && !nnan && dinf) {
+		q.real = runtime·nan;
+		q.imag = runtime·nan;
+	} else if(ninf && !dinf) {
+		q.real = runtime·posinf;
+		q.imag = runtime·posinf;
+	} else if(!ninf && dinf) {
 		q.real = 0;
 		q.imag = 0;
 	} else if(d.real == 0 && d.imag == 0) {
 		if(n.real == 0 && n.imag == 0) {
-			q.real = runtime·NaN();
-			q.imag = runtime·NaN();
+			q.real = runtime·nan;
+			q.imag = runtime·nan;
 		} else {
-			q.real = runtime·Inf(0);
-			q.imag = runtime·Inf(0);
+			q.real = runtime·posinf;
+			q.imag = runtime·posinf;
 		}
 	} else {
 		// Standard complex arithmetic, factored to avoid unnecessary overflow.

src/pkg/runtime/float.c

--- a/src/pkg/runtime/float.c
+++ b/src/pkg/runtime/float.c
@@ -4,170 +4,7 @@
 
 #include "runtime.h"
 
-static	uint64	uvnan		= 0x7FF8000000000001ULL;
-static	uint64	uvinf		= 0x7FF0000000000000ULL;
-static	uint64	uvneginf	= 0xFFF0000000000000ULL;
-
-uint32
-runtime·float32tobits(float32 f)
-{
-	// The obvious cast-and-pointer code is technically
-	// not valid, and gcc miscompiles it.  Use a union instead.
-	union {
-		float32 f;
-		uint32 i;
-	} u;
-	u.f = f;
-	return u.i;
-}
-
-uint64
-runtime·float64tobits(float64 f)
-{
-	// The obvious cast-and-pointer code is technically
-	// not valid, and gcc miscompiles it.  Use a union instead.
-	union {
-		float64 f;
-		uint64 i;
-	} u;
-	u.f = f;
-	return u.i;
-}
-
-float64
-runtime·float64frombits(uint64 i)
-{
-	// The obvious cast-and-pointer code is technically
-	// not valid, and gcc miscompiles it.  Use a union instead.
-	union {
-		float64 f;
-		uint64 i;
-	} u;
-	u.i = i;
-	return u.f;
-}
-
-float32
-runtime·float32frombits(uint32 i)
-{
-	// The obvious cast-and-pointer code is technically
-	// not valid, and gcc miscompiles it.  Use a union instead.
-	union {
-		float32 f;
-		uint32 i;
-	} u;
-	u.i = i;
-	return u.f;
-}
-
-bool
-runtime·isInf(float64 f, int32 sign)
-{
-	uint64 x;
-
-	x = runtime·float64tobits(f);
-	if(sign == 0)
-		return x == uvinf || x == uvneginf;
-	if(sign > 0)
-		return x == uvinf;
-	return x == uvneginf;
-}
-
-float64
-runtime·NaN(void)
-{
-	return runtime·float64frombits(uvnan);
-}
-
-bool
-runtime·isNaN(float64 f)
-{
-	uint64 x;
-
-	x = runtime·float64tobits(f);
-	return ((uint32)(x>>52) & 0x7FF) == 0x7FF && !runtime·isInf(f, 0);
-}
-
-float64
-runtime·Inf(int32 sign)
-{
-	if(sign >= 0)
-		return runtime·float64frombits(uvinf);
-	else
-		return runtime·float64frombits(uvneginf);
-}
-
-enum
-{
-	MASK	= 0x7ffL,
-	SHIFT	= 64-11-1,
-	BIAS	= 1022L,
-};
-
-float64
-runtime·frexp(float64 d, int32 *ep)
-{
-	uint64 x;
-
-	if(d == 0) {
-		*ep = 0;
-		return 0;
-	}
-	x = runtime·float64tobits(d);
-	*ep = (int32)((x >> SHIFT) & MASK) - BIAS;
-	x &= ~((uint64)MASK << SHIFT);
-	x |= (uint64)BIAS << SHIFT;
-	return runtime·float64frombits(x);
-}
-
-float64
-runtime·ldexp(float64 d, int32 e)
-{
-	uint64 x;
-
-	if(d == 0)
-		return 0;
-	x = runtime·float64tobits(d);
-	e += (int32)(x >> SHIFT) & MASK;
-	if(e <= 0)
-		return 0;	/* underflow */
-	if(e >= MASK){		/* overflow */
-		if(d < 0)
-			return runtime·Inf(-1);
-		return runtime·Inf(1);
-	}
-	x &= ~((uint64)MASK << SHIFT);
-	x |= (uint64)e << SHIFT;
-	return runtime·float64frombits(x);
-}
-
-float64
-runtime·modf(float64 d, float64 *ip)
-{
-	float64 dd;
-	uint64 x;
-	int32 e;
-
-	if(d < 1) {
-		if(d < 0) {
-			d = runtime·modf(-d, ip);
-			*ip = -*ip;
-			return -d;
-		}
-		*ip = 0;
-		return d;
-	}
-
-	x = runtime·float64tobits(d);
-	e = (int32)((x >> SHIFT) & MASK) - BIAS;
-
-	/*
-	 * Keep the top 11+e bits; clear the rest.
-	 */
-	if(e <= 64-11)
-		x &= ~(((uint64)1 << (64LL-11LL-e))-1);
-	dd = runtime·float64frombits(x);
-	*ip = dd;
-	return d - dd;
-}
-
 // used as float64 via runtime· names
+uint64	·nan		= 0x7FF8000000000001ULL;
+uint64	·posinf	= 0x7FF0000000000000ULL;
+uint64	·neginf	= 0xFFF0000000000000ULL;

src/pkg/runtime/print.c

--- a/src/pkg/runtime/print.c
+++ b/src/pkg/runtime/print.c
@@ -209,15 +209,15 @@ runtime·printfloat(float64 v)
 	int32 e, s, i, n;
 	float64 h;
 
-	if(runtime·isNaN(v)) {
+	if(ISNAN(v)) {
 		gwrite("NaN", 3);
 		return;
 	}
-	if(runtime·isInf(v, 1)) {
+	if(v == runtime·posinf) {
 		gwrite("+Inf", 4);
 		return;
 	}
-	if(runtime·isInf(v, -1)) {
+	if(v == runtime·neginf) {
 		gwrite("-Inf", 4);
 		return;
 	}

src/pkg/runtime/runtime.h

--- a/src/pkg/runtime/runtime.h
+++ b/src/pkg/runtime/runtime.h
@@ -815,3 +815,12 @@ uintptr	runtime·memlimit(void);
 // is forced to deliver the signal to a thread that's actually running.
 // This is a no-op on other systems.
 void	runtime·setprof(bool);
+
+// float.c
+extern float64 runtime·nan;
+extern float64 runtime·posinf;
+extern float64 runtime·neginf;
+extern uint64 ·nan;
+extern uint64 ·posinf;
+extern uint64 ·neginf;
+#define ISNAN(f) ((f) != (f))

コアとなるコードの解説

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

このファイルでは、runtime·complex128div関数内の浮動小数点数の特殊値(NaN, Inf)の判定と生成方法が変更されています。

  • Inf判定の変更:
    • 変更前はruntime·isInf(n.real, 0)のように関数を呼び出して、実部や虚部が無限大であるかを判定していました。
    • 変更後はn.real == runtime·posinf || n.real == runtime·neginfのように、直接定義された無限大の定数と比較することで、関数呼び出しのオーバーヘッドをなくしています。
  • NaN判定の変更:
    • 変更前はruntime·isNaN(n.real)のように関数を呼び出して、実部や虚部がNaNであるかを判定していました。
    • 変更後はISNAN(n.real)というマクロを使用しています。このマクロは((f) != (f))というIEEE 754のNaN特性を利用した比較に展開されるため、関数呼び出しが不要になります。
  • NaN/Inf値の生成の変更:
    • 変更前はruntime·NaN()runtime·Inf(0)のように関数を呼び出してNaNやInfを生成していました。
    • 変更後はruntime·nanruntime·posinfといった、float64型のグローバル定数を直接使用しています。これにより、値の生成も関数呼び出しなしで行えるようになります。

これらの変更により、complex128の除算における特殊値のハンドリングが、より効率的かつ高速に行われるようになりました。

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

このファイルは、浮動小数点数に関する多くのユーティリティ関数を削除し、代わりにNaN、正の無限大、負の無限大を表すuint64定数を直接定義しています。

  • 関数群の削除: runtime·float32tobits, runtime·float64tobits, runtime·float64frombits, runtime·float32frombits, runtime·isInf, runtime·isNaN, runtime·NaN, runtime·Inf, runtime·frexp, runtime·ldexp, runtime·modfといった、浮動小数点数のビット操作や特殊値の判定・生成、数学的な操作を行う関数がすべて削除されました。これは、これらの関数が呼び出しオーバーヘッドの原因となっていたため、その機能を他の場所でインライン化するか、より直接的な方法で代替するためです。
  • 特殊値の定数定義:
    uint64	·nan		= 0x7FF8000000000001ULL;
    uint64	·posinf	= 0x7FF0000000000000ULL;
    uint64	·neginf	= 0xFFF0000000000000ULL;
    
    これらのuint64定数は、IEEE 754倍精度浮動小数点数形式におけるNaN、正の無限大、負の無限大のビットパターンを直接表しています。これらはruntime·nan, runtime·posinf, runtime·neginfとしてfloat64型で外部に公開され、特殊値の生成に直接使用されます。

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

このファイルでは、runtime·printfloat関数内の浮動小数点数の出力ロジックが変更されています。

  • NaN判定: runtime·isNaN(v)からISNAN(v)マクロへの変更。
  • Inf判定: runtime·isInf(v, 1)runtime·isInf(v, -1)から、v == runtime·posinfv == runtime·neginfといった直接比較への変更。

これにより、浮動小数点数の文字列変換処理も高速化され、ランタイム全体のパフォーマンス向上に貢献します。

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

このヘッダーファイルでは、新しいグローバル定数とマクロが宣言されています。

  • extern float64 runtime·nan; など: src/pkg/runtime/float.cで定義されたuint64定数を、float64型として他のファイルからアクセスできるようにするためのextern宣言です。
  • #define ISNAN(f) ((f) != (f)): NaN判定のためのマクロです。このマクロは、IEEE 754規格のNaNの特性(NaNは自身と比較すると常に偽になる)を利用しており、コンパイラが効率的なコードを生成できるようにします。

これらの変更は、Goランタイムの浮動小数点数処理の基盤を、より低レベルで効率的な実装へと移行させるものであり、特に計算負荷の高い数値演算のパフォーマンス向上に大きく寄与しています。

関連リンク

  • Go issue 3912: https://github.com/golang/go/issues/3912 (386環境でのSSE2使用に関する議論)
  • Go CL 6197045: このコミットが依存している変更リスト。直接的なリンクは提供されていないが、Goのコードレビューシステム(Gerrit)の古いCL番号。
  • Go CL 6024045: このコミットのGerrit上の変更リスト。 https://golang.org/cl/6024045

参考にした情報源リンク