[インデックス 15798] ファイルの概要
このコミットは、Go言語のネットワーク(net)パッケージとランタイム(runtime)において、Linux/ARMアーキテクチャ向けにランタイム統合されたポーリング機構(pollster)を有効にする変更です。これにより、特にTCPネットワーク操作のパフォーマンスが大幅に向上しています。
コミット
commit 1d64d04da5f54f2fc0c9801c908380b633ea67d9
Author: Shenghou Ma <minux.ma@gmail.com>
Date: Sat Mar 16 04:01:56 2013 +0800
net, runtime: enable runtime-integrated pollster on Linux/ARM.
Results from linux/arm on a Samsung Chromebook (from dfc):
localhost(~/go/src/pkg/net) % ~/go/misc/benchcmp {old,new}.txt
benchmark old ns/op new ns/op delta
BenchmarkTCP4OneShot 568840 350526 -38.38%
BenchmarkTCP4OneShot-2 359054 206708 -42.43%
BenchmarkTCP4OneShotTimeout 637464 363550 -42.97%
BenchmarkTCP4OneShotTimeout-2 374255 216695 -42.10%
BenchmarkTCP4Persistent 184974 64984 -64.87%
BenchmarkTCP4Persistent-2 109902 47195 -57.06%
BenchmarkTCP4PersistentTimeout 210039 64789 -69.15%
BenchmarkTCP4PersistentTimeout-2 124284 43374 -65.10%
BenchmarkTCP6OneShot 672278 362116 -46.14%
BenchmarkTCP6OneShot-2 383631 216400 -43.59%
BenchmarkTCP6OneShotTimeout 680740 378306 -44.43%
BenchmarkTCP6OneShotTimeout-2 397524 230152 -42.10%
BenchmarkTCP6Persistent 172346 65292 -62.12%
BenchmarkTCP6Persistent-2 106229 42096 -60.37%
BenchmarkTCP6PersistentTimeout 161149 65138 -59.58%
BenchmarkTCP6PersistentTimeout-2 152276 44548 -70.75%
R=golang-dev, dave, bradfitz, dvyukov, rsc
CC=golang-dev
https://golang.org/cl/7820045
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/1d64d04da5f54f2fc0c9801c908380b633ea67d9
元コミット内容
このコミットの主な内容は、Go言語のネットワークI/O処理において、Linux/ARM環境でのファイルディスクリプタ(FD)のポーリングメカニズムを、Goランタイムに統合されたepollベースの実装に切り替えることです。
具体的には、以下の変更が含まれています。
src/pkg/net/fd_linux.goファイルの削除。このファイルは、Go言語レベルでepollをラップしてFDのポーリングを行っていた実装を含んでいました。src/pkg/net/fd_poll_runtime.goおよびsrc/pkg/net/fd_poll_unix.goのビルドタグの変更。これにより、Linux/ARM環境がfd_poll_runtime.go(ランタイム統合ポーリング)を使用するように切り替わり、fd_poll_unix.go(従来のポーリング)からは除外されます。src/pkg/runtime/defs_linux_arm.hにepoll関連の定数(EPOLLIN,EPOLLOUTなど)とEpollEvent構造体の定義を追加。src/pkg/runtime/netpoll.gocおよびsrc/pkg/runtime/netpoll_epoll.cのビルドタグの変更。これにより、Linux/ARM環境がnetpoll_epoll.c(epollベースのネットワークポーリング)を使用するように切り替わります。src/pkg/runtime/netpoll_stub.cのビルドタグの変更。Linux/ARMがスタブ実装から除外されます。src/pkg/runtime/sys_linux_arm.sにepoll_create,epoll_ctl,epoll_wait,epoll_create1,fcntlといったepoll関連のシステムコールを呼び出すためのアセンブリコードを追加。
コミットメッセージには、Samsung Chromebook(Linux/ARM)上でのベンチマーク結果が示されており、TCPの各種ベンチマークにおいて、旧バージョンと比較して38%から70%以上という大幅なパフォーマンス改善が確認されています。これは、Go言語のネットワークI/Oが、より効率的なepollメカニズムを直接利用できるようになったことによるものです。
変更の背景
Go言語の初期のバージョンでは、異なるOSやアーキテクチャに対応するため、ネットワークI/Oのポーリングメカニズムが抽象化されていました。Linux環境では、epollが利用可能であるにもかかわらず、一部のアーキテクチャ(特にARM)では、Go言語のランタイムに直接統合されたepollベースのポーリングが有効になっていませんでした。
このコミットが行われた2013年当時、ARMアーキテクチャはモバイルデバイスや組み込みシステムだけでなく、サーバー分野でもその存在感を増し始めていました。Go言語がこれらのプラットフォームで高性能なネットワークアプリケーションを構築するためには、基盤となるI/Oメカニズムの最適化が不可欠でした。
従来のGo言語レベルでのepollラッパー(fd_linux.goのようなファイルで実装されていたもの)は、Goランタイムのスケジューラと直接連携していなかったため、コンテキストスイッチのオーバーヘッドや、I/Oブロッキング時のGoroutineのスケジューリング効率に課題がありました。
このコミットの目的は、Linux/ARM環境においても、他の主要なLinuxアーキテクチャ(x86, amd64)と同様に、Goランタイムが直接epollシステムコールを呼び出し、GoroutineのI/Oブロッキングとアンブロッキングを効率的に管理する「ランタイム統合ポーリング」を有効にすることでした。これにより、ネットワークI/Oのレイテンシが削減され、スループットが向上し、全体的なアプリケーションのパフォーマンスが大幅に改善されることが期待されました。
前提知識の解説
1. ファイルディスクリプタ (File Descriptor, FD)
Unix系OSにおいて、ファイルやソケット、パイプなどのI/Oリソースを識別するために使われる整数値です。プログラムはFDを通じてこれらのリソースにアクセスします。
2. I/O多重化 (I/O Multiplexing)
複数のI/O操作(ファイルディスクリプタ)を同時に監視し、いずれかのFDでI/O準備ができたときに通知を受け取るメカニズムです。これにより、単一のスレッドで多数のI/O操作を効率的に処理できます。主なI/O多重化メカニズムには以下があります。
select()/poll(): 伝統的なI/O多重化API。監視対象のFDが増えると、カーネルとユーザー空間間のデータコピー量が増え、パフォーマンスが低下する傾向があります(O(N)の複雑性)。epoll()(Linux): Linuxカーネルが提供する高性能なI/O多重化API。select()やpoll()と比較して、監視対象のFD数が増えてもパフォーマンスが劣化しにくい(O(1)の複雑性)という特徴があります。これは、カーネル内部でイベントキューを管理し、準備ができたFDのみを通知するためです。
3. Go言語のGoroutineとスケジューラ
- Goroutine: Go言語の軽量な並行処理単位です。OSのスレッドよりもはるかに軽量で、数百万のGoroutineを同時に実行することも可能です。
- Goスケジューラ: Goランタイムの一部であり、Goroutineの実行を管理します。GoroutineがI/O操作などでブロックされると、スケジューラは自動的にそのGoroutineを一時停止し、他の実行可能なGoroutineにCPUを割り当てます。I/O操作が完了すると、ブロックされていたGoroutineは再び実行可能状態になり、スケジューラによって再開されます。
4. Go言語のネットワークI/Oとポーリング
Go言語の標準ライブラリ(netパッケージなど)は、GoroutineとI/O多重化メカニズムを組み合わせて、ノンブロッキングI/Oを効率的に実現しています。
GoのネットワークI/Oは、内部的に「ネットワークポーラー(netpoller)」と呼ばれるコンポーネントを使用します。このポーラーは、OSの提供するI/O多重化API(Linuxではepoll、macOSではkqueueなど)を利用して、多数のソケットからのイベントを効率的に監視します。
Goroutineがソケットからの読み書きを試み、データが準備できていない場合、そのGoroutineはブロックされます。しかし、OSのスレッド全体がブロックされるのではなく、GoランタイムのスケジューラがそのGoroutineを「待機中」の状態にし、ポーラーにそのソケットのイベント監視を登録します。ポーラーがイベントを検知すると、スケジューラは待機中のGoroutineを「実行可能」状態に戻し、別のOSスレッドで実行を再開させます。このメカニズムにより、Goは多数の同時接続を効率的に処理できます。
5. ビルドタグ (Build Tags)
Go言語のソースコードでは、ファイルの先頭に// +build tagのようなコメントを記述することで、特定の環境(OS、アーキテクチャなど)でのみそのファイルをコンパイルするように制御できます。例えば、// +build linux,armはLinux/ARM環境でのみコンパイルされることを意味します。
技術的詳細
このコミットの核心は、Linux/ARM環境におけるGo言語のネットワークI/Oパスを、Goランタイムに深く統合されたepollベースのポーリングメカニズムに移行することです。
1. fd_linux.go の削除とランタイム統合
以前のGoバージョンでは、src/pkg/net/fd_linux.goのようなファイルが、Go言語のコード内でepollシステムコールを直接ラップし、ファイルディスクリプタのポーリングを行っていました。これはGoのnetパッケージの一部として機能していましたが、Goランタイムのスケジューラとは独立した形でポーリングロジックが実装されていました。
このコミットでは、fd_linux.goが削除されます。これは、Go言語レベルでのポーリング実装が不要になったことを意味します。代わりに、src/pkg/runtime/netpoll_epoll.cに実装されているC言語ベースのepollポーリングロジックが、Linux/ARM環境でも直接利用されるようになります。このnetpoll_epoll.cはGoランタイムの一部であり、Goroutineのスケジューリングと密接に連携しています。
2. ビルドタグによる制御
src/pkg/net/fd_poll_runtime.goのビルドタグが// +build darwin linux,386 linux,amd64から// +build darwin linuxに変更されました。これにより、Linux/ARMもこのファイルに含まれるランタイム統合ポーリングのインターフェースを使用するようになります。src/pkg/net/fd_poll_unix.goのビルドタグが// +build freebsd linux,arm netbsd openbsdから// +build freebsd netbsd openbsdに変更されました。これにより、Linux/ARMは従来のUnix系ポーリングメカニズムから除外されます。src/pkg/runtime/netpoll.gocのビルドタグも同様に// +build darwin linux,386 linux,amd64から// +build darwin linuxに変更され、Linux/ARMがランタイムのネットワークポーリングロジックを使用するようになります。src/pkg/runtime/netpoll_epoll.cのビルドタグが// +build linux,386 linux,amd64から// +build linuxに変更され、Linux/ARMもepollベースのポーリング実装を使用するようになります。src/pkg/runtime/netpoll_stub.cのビルドタグからlinux,armが削除され、Linux/ARMがスタブ実装ではなく実際のポーリングメカニズムを使用するようになります。
これらのビルドタグの変更により、GoコンパイラはLinux/ARM環境でビルドする際に、Goランタイムに統合されたepollポーリング関連のCコードとアセンブリコードをリンクするようになります。
3. epollシステムコールの追加
src/pkg/runtime/defs_linux_arm.h には、epoll関連の定数(EPOLLIN, EPOLLOUT, EPOLL_CTL_ADDなど)と、epoll_waitシステムコールが返すイベント情報を格納するEpollEvent構造体が追加されました。これらはCコードからepollシステムコールを呼び出す際に必要となる定義です。
src/pkg/runtime/sys_linux_arm.s には、ARMアーキテクチャのシステムコール呼び出し規約に従って、以下のepoll関連システムコールを呼び出すためのアセンブリコードが追加されました。
SYS_epoll_createSYS_epoll_ctlSYS_epoll_waitSYS_epoll_create1SYS_fcntl(ファイルディスクリプタのフラグ設定、特にFD_CLOEXECのため)
これらのアセンブリ関数は、GoランタイムのCコード(netpoll_epoll.cなど)から呼び出され、カーネルのepoll機能と直接やり取りします。
4. パフォーマンス向上
コミットメッセージに示されているベンチマーク結果は、この変更がもたらすパフォーマンス上の大きなメリットを明確に示しています。TCPの各種ベンチマークにおいて、処理時間が大幅に短縮されています。これは、GoランタイムがI/Oイベントをより効率的に検知し、Goroutineのブロック/アンブロックを最適化できるようになったためです。特に、多数の同時接続や高頻度なI/O操作を伴うアプリケーションにおいて、この改善は顕著な効果を発揮します。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更は、主に以下のファイルに集中しています。
-
src/pkg/net/fd_linux.goの削除:- このファイル全体が削除されました。これは、Go言語レベルで実装されていた
epollベースのポーリングロジックが、ランタイム統合されたC言語実装に置き換えられたことを示します。
- このファイル全体が削除されました。これは、Go言語レベルで実装されていた
-
src/pkg/net/fd_poll_runtime.goのビルドタグ変更:--- a/src/pkg/net/fd_poll_runtime.go +++ b/src/pkg/net/fd_poll_runtime.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin linux,386 linux,amd64 +// +build darwin linux package netlinux,armが含まれるように、linuxのみが指定されるようになりました。これにより、Linux/ARM環境でもこのファイルがコンパイルされ、ランタイム統合ポーリングのインターフェースが使用されます。
-
src/pkg/net/fd_poll_unix.goのビルドタグ変更:--- a/src/pkg/net/fd_poll_unix.go +++ b/src/pkg/net/fd_poll_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license can be found in the LICENSE file. -// +build freebsd linux,arm netbsd openbsd +// +build freebsd netbsd openbsd package netlinux,armがビルドタグから削除されました。これにより、Linux/ARMは従来のUnix系ポーリングメカニズム(select/pollベースの可能性が高い)から除外されます。
-
src/pkg/runtime/defs_linux_arm.hへのepoll関連定義の追加:--- a/src/pkg/runtime/defs_linux_arm.h +++ b/src/pkg/runtime/defs_linux_arm.h @@ -2,6 +2,7 @@ // Constants enum { +\tEINTR = 0x4,\ \tENOMEM = 0xc,\ \tEAGAIN = 0xb,\ @@ -65,6 +66,17 @@ enum { \tITIMER_VIRTUAL = 0x1,\ \tO_RDONLY = 0,\ \tO_CLOEXEC = 02000000,\ +\ +\tEPOLLIN\t\t= 0x1,\ +\tEPOLLOUT\t= 0x4,\ +\tEPOLLERR\t= 0x8,\ +\tEPOLLHUP\t= 0x10,\ +\tEPOLLRDHUP\t= 0x2000,\ +\tEPOLLET\t\t= -0x80000000,\ +\tEPOLL_CLOEXEC\t= 0x80000,\ +\tEPOLL_CTL_ADD\t= 0x1,\ +\tEPOLL_CTL_DEL\t= 0x2,\ +\tEPOLL_CTL_MOD\t= 0x3,\ }; // Types @@ -146,4 +158,11 @@ struct Sigaction {\ \tvoid *sa_restorer;\ \tuint64 sa_mask;\ };\ +\ +typedef struct EpollEvent EpollEvent;\ +struct EpollEvent {\ +\tuint32\tevents;\ +\tuint32\t_pad;\ +\tuint64\tdata;\ +};\ #pragma pack offepoll関連の定数(イベントフラグ、操作コード)と、epoll_waitから返されるイベント情報を格納するEpollEvent構造体が追加されました。これらはC言語でepollシステムコールを扱うために必要です。
-
src/pkg/runtime/netpoll.gocのビルドタグ変更:--- a/src/pkg/runtime/netpoll.goc +++ b/src/pkg/runtime/netpoll.goc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license can be found in the LICENSE file. -// +build darwin linux,386 linux,amd64 +// +build darwin linux package netlinux,armが含まれるように、linuxのみが指定されるようになりました。これにより、Linux/ARM環境でもこのファイルがコンパイルされ、ランタイムのネットワークポーリングロジックが使用されます。
-
src/pkg/runtime/netpoll_epoll.cのビルドタグ変更:--- a/src/pkg/runtime/netpoll_epoll.c +++ b/src/pkg/runtime/netpoll_epoll.c @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license can be found in the LICENSE file. -// +build linux,386 linux,amd64 +// +build linux #include "runtime.h" #include "defs_GOOS_GOARCH.h"linux,armが含まれるように、linuxのみが指定されるようになりました。これにより、Linux/ARM環境でもこのファイルがコンパイルされ、epollベースのポーリング実装が使用されます。
-
src/pkg/runtime/netpoll_stub.cのビルドタグ変更:--- a/src/pkg/runtime/netpoll_stub.c +++ b/src/pkg/runtime/netpoll_stub.c @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license can be found in the LICENSE file. -// +build freebsd linux,arm netbsd openbsd plan9 windows +// +build freebsd netbsd openbsd plan9 windows #include "runtime.h"linux,armがビルドタグから削除されました。これにより、Linux/ARMはネットワークポーリングのスタブ実装ではなく、実際のepollベースの実装を使用するようになります。
-
src/pkg/runtime/sys_linux_arm.sへのシステムコール呼び出しの追加:--- a/src/pkg/runtime/sys_linux_arm.s +++ b/src/pkg/runtime/sys_linux_arm.s @@ -36,6 +36,11 @@ #define SYS_ugetrlimit (SYS_BASE + 191) #define SYS_sched_getaffinity (SYS_BASE + 242) #define SYS_clock_gettime (SYS_BASE + 263) +#define SYS_epoll_create (SYS_BASE + 250) +#define SYS_epoll_ctl (SYS_BASE + 251) +#define SYS_epoll_wait (SYS_BASE + 252) +#define SYS_epoll_create1 (SYS_BASE + 357) +#define SYS_fcntl (SYS_BASE + 55) #define ARM_BASE (SYS_BASE + 0x0f0000) @@ -371,7 +376,6 @@ cascheck: MOVW $0, R0 RET -\ TEXT runtime·casp(SB),7,$0 B runtime·cas(SB) @@ -387,3 +391,46 @@ TEXT runtime·sched_getaffinity(SB),7,$0 MOVW $SYS_sched_getaffinity, R7 SWI $0 RET +\ +// int32 runtime·epollcreate(int32 size)\ +TEXT runtime·epollcreate(SB),7,$0\ +\tMOVW\t0(FP), R0\ +\tMOVW\t$SYS_epoll_create, R7\ +\tSWI\t$0\ +\tRET\ +\ +// int32 runtime·epollcreate1(int32 flags)\ +TEXT runtime·epollcreate1(SB),7,$0\ +\tMOVW\t0(FP), R0\ +\tMOVW\t$SYS_epoll_create1, R7\ +\tSWI\t$0\ +\tRET\ +\ +// int32 runtime·epollctl(int32 epfd, int32 op, int32 fd, EpollEvent *ev)\ +TEXT runtime·epollctl(SB),7,$0\ +\tMOVW\t0(FP), R0\ +\tMOVW\t4(FP), R1\ +\tMOVW\t8(FP), R2\ +\tMOVW\t12(FP), R3\ +\tMOVW\t$SYS_epoll_ctl, R7\ +\tSWI\t$0\ +\tRET\ +\ +// int32 runtime·epollwait(int32 epfd, EpollEvent *ev, int32 nev, int32 timeout)\ +TEXT runtime·epollwait(SB),7,$0\ +\tMOVW\t0(FP), R0\ +\tMOVW\t4(FP), R1\ +\tMOVW\t8(FP), R2\ +\tMOVW\t12(FP), R3\ +\tMOVW\t$SYS_epoll_wait, R7\ +\tSWI\t$0\ +\tRET\ +\ +// void runtime·closeonexec(int32 fd)\ +TEXT runtime·closeonexec(SB),7,$0\ +\tMOVW\t0(FP), R0\t// fd\ +\tMOVW\t$2, R1\t// F_SETFD\ +\tMOVW\t$1, R2\t// FD_CLOEXEC\ +\tMOVW\t$SYS_fcntl, R7\ +\tSWI $0\ +\tRETepoll_create,epoll_ctl,epoll_wait,epoll_create1,fcntlといったシステムコールの番号定義が追加されました。- これらのシステムコールを呼び出すためのアセンブリ関数(
runtime·epollcreate,runtime·epollcreate1,runtime·epollctl,runtime·epollwait,runtime·closeonexec)が追加されました。これらの関数は、GoランタイムのCコードから呼び出され、カーネルのepoll機能と直接連携します。
コアとなるコードの解説
このコミットの主要な変更は、Go言語のネットワークI/O処理におけるLinux/ARMアーキテクチャのポーリングメカニズムを、Goランタイムに直接統合されたepollベースの実装に切り替えることです。
-
fd_linux.goの削除:- このファイルは、Go言語の
netパッケージ内でepollシステムコールをラップし、ファイルディスクリプタのイベントを監視する役割を担っていました。しかし、これはGoランタイムのスケジューラとは独立したGoコードとして実装されていたため、GoroutineのI/Oブロッキングとスケジューリングの連携において、オーバーヘッドや非効率性が存在しました。このファイルの削除は、Go言語レベルでのポーリング実装が不要になり、より低レベルで効率的なランタイム統合ポーリングに移行したことを意味します。
- このファイルは、Go言語の
-
ビルドタグの変更 (
fd_poll_runtime.go,fd_poll_unix.go,netpoll.goc,netpoll_epoll.c,netpoll_stub.c):- これらの変更は、Goコンパイラに対して、Linux/ARM環境でビルドする際にどのポーリング実装を使用すべきかを指示します。
fd_poll_runtime.goとnetpoll.gocのビルドタグにlinuxが含まれるようになったことで、Linux/ARMもこれらのファイルに含まれるランタイム統合ポーリングのインターフェースとロジックを使用するようになります。fd_poll_unix.goとnetpoll_stub.cからlinux,armが削除されたことで、Linux/ARMは従来の汎用Unixポーリングやスタブ実装ではなく、専用のepoll実装を使用するようになります。netpoll_epoll.cのビルドタグにlinuxが含まれるようになったことで、Linux/ARMはepollベースのネットワークポーリング実装を直接利用するようになります。このCファイルは、Goランタイムの一部としてコンパイルされ、GoのGoroutineスケジューラと密接に連携して動作します。
-
defs_linux_arm.hとsys_linux_arm.sにおけるepollシステムコールの追加:defs_linux_arm.hに追加されたepoll関連の定数とEpollEvent構造体は、C言語でepollシステムコールを呼び出す際に必要な定義を提供します。これにより、Cコードがepollの各種フラグやイベント情報を正しく解釈できるようになります。sys_linux_arm.sに追加されたアセンブリコードは、GoランタイムのCコード(例:netpoll_epoll.c)から、Linuxカーネルのepoll_create,epoll_ctl,epoll_waitなどのシステムコールを直接呼び出すためのゲートウェイを提供します。ARMアーキテクチャのシステムコール呼び出し規約に従って、レジスタに引数をセットし、SWI(Software Interrupt) 命令でカーネルに処理を委譲します。- 特に
runtime·epollwaitのような関数は、GoroutineがI/Oでブロックされた際に、Goスケジューラがこの関数を呼び出し、カーネルにI/Oイベントの発生を待機させます。イベントが発生すると、カーネルはepoll_waitから戻り、Goランタイムは対応するGoroutineを再開させることができます。この直接的な連携により、I/Oブロッキング時のコンテキストスイッチのオーバーヘッドが最小限に抑えられ、Goroutineのスケジューリング効率が大幅に向上します。
これらの変更により、Linux/ARM環境におけるGo言語のネットワークI/Oは、Goランタイムのスケジューラと密接に連携した、より効率的なepollベースのポーリングメカニズムを利用するようになり、結果としてコミットメッセージに示されたような顕著なパフォーマンス改善が実現されました。
関連リンク
- Go言語のネットワークポーラーに関する公式ドキュメントやブログ記事(当時のものがあれば)
- Linux
epollシステムコールのmanページ - Go言語のランタイムとスケジューラに関する解説記事
参考にした情報源リンク
- Go の netpoller の仕組み (Goのnetpollerの一般的な仕組みを理解するために参照)
- Go の netpoller の仕組み (2) - epoll (Goのepoll実装の一般的な仕組みを理解するために参照)
- epoll(7) - Linux man page (epollシステムコールの詳細を理解するために参照)
- Goのビルドタグについて (Goのビルドタグの仕組みを理解するために参照)
- Goのシステムコール呼び出しについて (Goのruntimeにおけるシステムコール呼び出しのアセンブリコードの例を理解するために参照)
- Goのランタイムとスケジューラ (GoのGoroutineとスケジューラの基本的な概念を理解するために参照)
- golang/go GitHubリポジトリ (コミットのコンテキストと関連ファイルを確認するために参照)
- Go CL 7820045 (元の変更リストを確認するために参照)