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

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

このコミットは、Go言語のベンチマークスイートである test/bench/shootout ディレクトリ内の timing.sh スクリプトに対する変更です。具体的には、Goコンパイラ(8g または 6g)のアーキテクチャ(32-bit または 64-bit)に合わせて、GCCコンパイラに渡す -m32 または -m64 オプションを動的に設定するように修正されています。これにより、GoとC/C++のベンチマークが同じアーキテクチャで実行されるようになり、より公平で正確な比較が可能になります。

コミット

commit c4c4b3b46729d8df3a5ee7bd5f0e463290e206da
Author: Russ Cox <rsc@golang.org>
Date:   Sun Oct 7 15:49:56 2012 -0400

    test/bench/shootout: match gcc architecture to GOARCH
    
    If we're benchmarking 8g, use gcc -m32.
    If we're benchmarking 6g, use gcc -m64.
    
    R=golang-dev, bradfitz, minux.ma, remyoudompheng
    CC=golang-dev
    https://golang.org/cl/6625061

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

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

元コミット内容

test/bench/shootout: match gcc architecture to GOARCH

このコミットの目的は、Goのベンチマークスクリプトにおいて、GCCコンパイラのアーキテクチャをGoコンパイラのアーキテクチャ(GOARCH)に合わせることです。具体的には、8g(32-bit Goコンパイラ)を使用している場合はGCCに-m32オプションを、6g(64-bit Goコンパイラ)を使用している場合はGCCに-m64オプションを渡すように変更します。

変更の背景

Go言語のパフォーマンスを評価する際、CやC++などの他の言語と比較ベンチマークを行うことは非常に重要です。test/bench/shootout ディレクトリは、The Computer Language Benchmarks Game (旧称: The Great Computer Language Shootout) のベンチマークをGo言語で実装し、他の言語の実装と比較するためのものです。

このベンチマークでは、Goで書かれたプログラムと、C/C++で書かれた同等のプログラムの実行時間を比較します。しかし、Goコンパイラが32-bitモード(8g)で動作しているにもかかわらず、GCCがデフォルトで64-bitバイナリを生成してしまうと、両者の実行環境が異なり、公平な比較ができませんでした。特に、メモリのアライメント、ポインタのサイズ、レジスタの使用方法などが32-bitと64-bitで異なるため、パフォーマンスに影響を与える可能性があります。

このコミットは、このようなアーキテクチャの不一致によるベンチマーク結果の歪みを解消し、より正確で信頼性の高い比較を可能にすることを目的としています。

前提知識の解説

Goコンパイラのアーキテクチャと命名規則

Go言語の初期のコンパイラは、ターゲットアーキテクチャに基づいて命名されていました。

  • 8g: go tool 8g は、GOARCH=386 (Intel 32-bit x86) 向けのGoプログラムをコンパイルするためのコンパイラです。
  • 6g: go tool 6g は、GOARCH=amd64 (Intel 64-bit x86-64) 向けのGoプログラムをコンパイルするためのコンパイラです。
  • 5g: go tool 5g は、GOARCH=arm (ARM) 向けのGoプログラムをコンパイルするためのコンパイラです。

これらの命名規則は、Go 1.0リリース以前の時期に広く使われていました。現在のGoでは、go build コマンドが GOARCH 環境変数に基づいて適切なコンパイラを自動的に選択するため、ユーザーが直接 8g6g を指定することは稀です。しかし、このコミットが作成された2012年当時は、これらのコンパイラ名がベンチマークスクリプト内で直接参照されることが一般的でした。

GCCコンパイラのアーキテクチャ指定オプション

GCC (GNU Compiler Collection) は、C、C++、Goなど様々なプログラミング言語をコンパイルできるコンパイラ群です。GCCには、生成するバイナリのターゲットアーキテクチャを指定するためのオプションがあります。

  • -m32: 32-bit x86アーキテクチャ向けのコードを生成するようGCCに指示します。これにより、32-bitのレジスタ、ポインタサイズ、命令セットが使用されます。
  • -m64: 64-bit x86-64アーキテクチャ向けのコードを生成するようGCCに指示します。これにより、64-bitのレジスタ、ポインタサイズ、命令セットが使用されます。

これらのオプションは、特にクロスコンパイル環境や、特定のアーキテクチャに最適化されたバイナリを生成したい場合に重要となります。

The Computer Language Benchmarks Game

The Computer Language Benchmarks Game (旧称: The Great Computer Language Shootout) は、様々なプログラミング言語で同じアルゴリズムを実装し、その実行速度、メモリ使用量、コードサイズなどを比較するプロジェクトです。これは、言語のパフォーマンス特性を理解するための貴重な情報源となります。Go言語もこのベンチマークゲームに参加しており、その結果はGoのパフォーマンス改善の指針の一つとなっています。

技術的詳細

このコミットの技術的な核心は、シェルスクリプト timing.sh 内でGoコンパイラの選択(8g または 6g)に基づいて、GCCのコンパイルオプションを動的に調整するロジックを追加した点にあります。

スクリプトの冒頭で、GOCHAR 環境変数からGoコンパイラのアーキテクチャを示す文字(8 または 6)を取得し、それを変数 O に格納しています。

O=$GOCHAR
GC="go tool ${O}g"
LD="go tool ${O}l"

この O の値を利用して、GCCに渡すアーキテクチャオプション gccm を設定する case 文が追加されました。

gccm=""
case "$O" in
8)
	gccm=-m32;;
6)
	gccm=-m64;;
esac
  • O8 の場合(8g コンパイラを使用している場合)、gccm-m32 に設定されます。
  • O6 の場合(6g コンパイラを使用している場合)、gccm-m64 に設定されます。

その後、timing.sh スクリプト内でC/C++のベンチマークプログラムをコンパイルする際に使用される gcc コマンドの呼び出し箇所がすべて変更され、新しく定義された gccm 変数が gcc のオプションとして追加されました。

例えば、fasta ベンチマークのコンパイル部分では、元の

run 'gcc -O2 fasta.c' a.out 25000000

が、以下のように変更されています。

run "gcc $gccm -O2 fasta.c" a.out 25000000

これにより、gcc はGoコンパイラと同じアーキテクチャ(32-bitまたは64-bit)でC/C++プログラムをコンパイルするようになります。この変更は、fasta, reverse-complement, nbody, binarytree, fannkuch, regexdna, spectralnorm, mandelbrot, meteor, pidigits, threadring, chameneos など、timing.sh で実行されるすべてのC/C++ベンチマークに適用されています。

この修正により、GoとC/C++のベンチマークが同じCPUアーキテクチャ(32-bitまたは64-bit)上で実行されることが保証され、より正確なパフォーマンス比較が可能になります。これは、ベンチマーク結果の信頼性を高め、Go言語のパフォーマンス特性をより正確に評価するために不可欠な変更です。

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

変更は test/bench/shootout/timing.sh ファイルのみです。

--- a/test/bench/shootout/timing.sh
+++ b/test/bench/shootout/timing.sh
@@ -10,6 +10,14 @@ O=$GOCHAR
 GC="go tool ${O}g"
 LD="go tool ${O}l"
 
+gccm=""
+case "$O" in
+8)
+	gccm=-m32;;\n
+6)
+	gccm=-m64;;\n
+esac
+
 PATH=.:$PATH
 
 havegccgo=false
@@ -78,7 +86,7 @@ run() {\n 
 fasta() {\n 
 	runonly echo 'fasta -n 25000000'\n
-\trun 'gcc -O2 fasta.c' a.out 25000000\n
+\trun "gcc $gccm -O2 fasta.c" a.out 25000000\n
 \trun 'gccgo -O2 fasta.go' a.out -n 25000000\t#commented out until WriteString is in bufio\n
 \trun 'gc fasta' $O.out -n 25000000\n
 \trun 'gc_B fasta' $O.out -n 25000000\n
@@ -88,7 +96,7 @@ revcomp() {\n 
 	runonly gcc -O2 fasta.c\n
 	runonly a.out 25000000 > x\n
 	runonly echo 'reverse-complement < output-of-fasta-25000000'\n
-\trun 'gcc -O2 reverse-complement.c' a.out < x\n
+\trun "gcc $gccm -O2 reverse-complement.c" a.out < x\n
 \trun 'gccgo -O2 reverse-complement.go' a.out < x\n
 \trun 'gc reverse-complement' $O.out < x\n
 \trun 'gc_B reverse-complement' $O.out < x\n
@@ -97,7 +105,7 @@ revcomp() {\n 
 nbody() {\n 
 	runonly echo 'nbody -n 50000000'\n
-\trun 'gcc -O2 nbody.c -lm' a.out 50000000\n
+\trun "gcc $gccm -O2 nbody.c -lm" a.out 50000000\n
 \trun 'gccgo -O2 nbody.go' a.out -n 50000000\n
 \trun 'gc nbody' $O.out -n 50000000\n
 \trun 'gc_B nbody' $O.out -n 50000000\n
@@ -105,7 +113,7 @@ nbody() {\n 
 binarytree() {\n 
 	runonly echo 'binary-tree 15 # too slow to use 20'\n
-\trun 'gcc -O2 binary-tree.c -lm' a.out 15\n
+\trun "gcc $gccm -O2 binary-tree.c -lm" a.out 15\n
 \trun 'gccgo -O2 binary-tree.go' a.out -n 15\n
 \trun 'gccgo -O2 binary-tree-freelist.go' a.out -n 15\n
 \trun 'gc binary-tree' $O.out -n 15\n
@@ -114,7 +122,7 @@ binarytree() {\n 
 fannkuch() {\n 
 	runonly echo 'fannkuch 12'\n
-\trun 'gcc -O2 fannkuch.c' a.out 12\n
+\trun "gcc $gccm -O2 fannkuch.c" a.out 12\n
 \trun 'gccgo -O2 fannkuch.go' a.out -n 12\n
 \trun 'gccgo -O2 fannkuch-parallel.go' a.out -n 12\n
 \trun 'gc fannkuch' $O.out -n 12\n
@@ -126,7 +134,7 @@ regexdna() {\n 
 	runonly gcc -O2 fasta.c\n
 	runonly a.out 100000 > x\n
 	runonly echo 'regex-dna 100000'\n
-\trun 'gcc -O2 regex-dna.c -lpcre' a.out <x\n
+\trun "gcc $gccm -O2 regex-dna.c -lpcre" a.out <x\n
 \trun 'gccgo -O2 regex-dna.go' a.out <x\n
 \trun 'gccgo -O2 regex-dna-parallel.go' a.out <x\n
 \trun 'gc regex-dna' $O.out <x\n
@@ -137,7 +145,7 @@ regexdna() {\n 
 spectralnorm() {\n 
 	runonly echo 'spectral-norm 5500'\n
-\trun 'gcc -O2 spectral-norm.c -lm' a.out 5500\n
+\trun "gcc $gccm -O2 spectral-norm.c -lm" a.out 5500\n
 \trun 'gccgo -O2 spectral-norm.go' a.out -n 5500\n
 \trun 'gc spectral-norm' $O.out -n 5500\n
 \trun 'gc_B spectral-norm' $O.out -n 5500\n
@@ -160,7 +168,7 @@ knucleotide() {\n 
 mandelbrot() {\n 
 	runonly echo 'mandelbrot 16000'\n
-\trun 'gcc -O2 mandelbrot.c' a.out 16000\n
+\trun "gcc $gccm -O2 mandelbrot.c" a.out 16000\n
 \trun 'gccgo -O2 mandelbrot.go' a.out -n 16000\n
 \trun 'gc mandelbrot' $O.out -n 16000\n
 \trun 'gc_B mandelbrot' $O.out -n 16000\n
@@ -168,7 +176,7 @@ mandelbrot() {\n 
 meteor() {\n 
 	runonly echo 'meteor 2098'\n
-\trun 'gcc -O2 meteor-contest.c' a.out 2098\n
+\trun "gcc $gccm -O2 meteor-contest.c" a.out 2098\n
 \trun 'gccgo -O2 meteor-contest.go' a.out -n 2098\n
 \trun 'gc meteor-contest' $O.out -n 2098\n
 \trun 'gc_B  meteor-contest' $O.out -n 2098\n
@@ -176,7 +184,7 @@ meteor() {\n 
 pidigits() {\n 
 	runonly echo 'pidigits 10000'\n
-\trun 'gcc -O2 pidigits.c -lgmp' a.out 10000\n
+\trun "gcc $gccm -O2 pidigits.c -lgmp" a.out 10000\n
 \trun 'gccgo -O2 pidigits.go' a.out -n 10000\n
 \trun 'gc pidigits' $O.out -n 10000\n
 \trun 'gc_B  pidigits' $O.out -n 10000\n
@@ -184,14 +192,14 @@ pidigits() {\n 
 threadring() {\n 
 	runonly echo 'threadring 50000000'\n
-\trun 'gcc -O2 threadring.c -lpthread' a.out 50000000\n
+\trun "gcc $gccm -O2 threadring.c -lpthread" a.out 50000000\n
 \trun 'gccgo -O2 threadring.go' a.out -n 50000000\n
 \trun 'gc threadring' $O.out -n 50000000\n
 }\n
 \n chameneos() {\n 
 	runonly echo 'chameneos 6000000'\n
-\trun 'gcc -O2 chameneosredux.c -lpthread' a.out 6000000\n
+\trun "gcc $gccm -O2 chameneosredux.c -lpthread" a.out 6000000\n
 \trun 'gccgo -O2 chameneosredux.go' a.out 6000000\n
 \trun 'gc chameneosredux' $O.out 6000000\n
 }\n

コアとなるコードの解説

このコミットの主要な変更点は、timing.sh スクリプトの冒頭に gccm 変数を定義し、その値を GOCHAR に応じて設定する case 文を追加したことです。

  1. gccm 変数の導入と case:

    gccm=""
    case "$O" in
    8)
    	gccm=-m32;;
    6)
    	gccm=-m64;;
    esac
    

    O 変数には、Goコンパイラのアーキテクチャを示す文字(8 または 6)が格納されています。

    • O8 の場合(32-bit Goコンパイラ 8g を使用)、gccm-m32 に設定されます。
    • O6 の場合(64-bit Goコンパイラ 6g を使用)、gccm-m64 に設定されます。 この gccm 変数が、GCCコンパイルコマンドに渡すアーキテクチャ指定オプションとなります。
  2. gcc コマンドの変更: スクリプト内のすべての gcc コマンドの呼び出し箇所が、以下のように変更されています。

    • 変更前: run 'gcc -O2 fasta.c' ...
    • 変更後: run "gcc $gccm -O2 fasta.c" ... これにより、gcc コマンドが実行される際に、gccm 変数に格納された -m32 または -m64 オプションが自動的に追加されます。例えば、8g が使用されている場合は gcc -m32 -O2 fasta.c となり、6g が使用されている場合は gcc -m64 -O2 fasta.c となります。

この変更により、Go言語のベンチマークとC/C++言語のベンチマークが、常に同じCPUアーキテクチャ(32-bitまたは64-bit)でコンパイルおよび実行されることが保証されます。これにより、ベンチマーク結果の公平性と正確性が大幅に向上し、Go言語のパフォーマンス特性をより信頼性高く評価できるようになります。

関連リンク

参考にした情報源リンク

  • Go言語のコミット履歴 (GitHub): https://github.com/golang/go/commits/master
  • Go言語のIssue Tracker (Go CL): https://golang.org/cl/6625061 (コミットメッセージに記載されているCLへのリンク)
  • GCCのmanページやドキュメント (特に -m32, -m64 オプションについて)
  • Go言語の古いコンパイラに関する情報 (例: 8g, 6g の役割)