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

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

このコミットは、Go言語のruntimeパッケージにcomplex128型(倍精度浮動小数点数で構成される複素数)の除算性能を測定するためのベンチマークテストを追加するものです。特に、通常の数値、NaN (Not a Number)、およびInf (Infinity) を含む様々なケースでの除算性能を評価することを目的としています。

コミット

commit 32c3a626da60210c85dc004ead609941dfff73bc
Author: Michael Chaten <mchaten@gmail.com>
Date:   Sat Apr 21 13:24:41 2012 +1000

    runtime: add benchmark for complex128 division
    
    R=golang-dev, dave, rsc
    CC=golang-dev, minux.ma
    https://golang.org/cl/6070043

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

https://github.com/golang/go/commit/32c3a626da60210c85dc004ead609941dfff73bc

元コミット内容

runtime: add benchmark for complex128 division

R=golang-dev, dave, rsc
CC=golang-dev, minux.ma
https://golang.org/cl/6070043

変更の背景

Go言語は、科学技術計算や数値解析など、複素数演算が頻繁に用いられる分野でも利用されることを想定しています。complex128型は、倍精度浮動小数点数(float64)を実部と虚部に持つ複素数型であり、その演算性能はアプリケーション全体のパフォーマンスに直結します。

このコミットが作成された2012年当時、Go言語のruntimeパッケージにおける複素数演算、特に除算の性能特性は十分にベンチマークされていなかった可能性があります。数値計算において、通常の有限な数値だけでなく、非数(NaN)や無限大(Inf)といった特殊な浮動小数点値が入力として与えられるケースも考慮する必要があります。これらの特殊な値に対する演算がどのように振る舞い、どの程度の性能を持つかは、言語の堅牢性と実用性にとって重要です。

このベンチマークの追加は、complex128除算のパフォーマンスを継続的に監視し、将来的な最適化の基盤を築くことを目的としています。また、特殊な浮動小数点値が関与する除算の挙動と性能を明確にすることで、潜在的なバグや性能ボトルネックを早期に発見し、修正するための手助けとなります。

前提知識の解説

1. Go言語のcomplex128

Go言語には、複素数を扱うための組み込み型としてcomplex64complex128があります。

  • complex64: float32を実部と虚部に持つ複素数。
  • complex128: float64を実部と虚部に持つ複素数。 複素数はreal + imag iの形式で表現され、Goでは3 + 2iのように記述します。

2. 浮動小数点数の特殊な値 (NaN, Inf)

IEEE 754浮動小数点数標準では、通常の有限な数値の他に、以下の特殊な値を定義しています。

  • NaN (Not a Number): 不定形な演算結果(例: 0/0Inf - Inf)を表します。
  • Inf (Infinity): オーバーフローやゼロ除算(例: 1/0)の結果として生じる無限大を表します。正の無限大 (+Inf) と負の無限大 (-Inf) があります。

これらの特殊な値が演算に含まれる場合、その結果も特殊な値になることが多く、その挙動は厳密に定義されています。

3. Go言語のベンチマークテスト

Go言語には、標準でベンチマークテストを記述・実行するためのフレームワークが組み込まれています。

  • testingパッケージ: テストとベンチマークの機能を提供します。
  • go test -bench=.: カレントディレクトリ内のベンチマークテストを実行するコマンドです。
  • ベンチマーク関数はBenchmarkXxx(*testing.B)というシグネチャを持ちます。
  • b.N: ベンチマーク関数内でループを回す回数を示します。testingパッケージが自動的に適切なb.Nの値を調整し、安定した測定結果が得られるようにします。
  • b.StopTimer() / b.StartTimer(): 測定対象外の処理がある場合に、タイマーを一時停止・再開するために使用します。
  • b.SetBytes(n): 1回の操作で処理されるバイト数を設定し、スループット(bytes/op)を計算するために使用します。
  • b.ReportAllocs(): メモリ割り当ての回数を報告するように設定します。

4. math/cmplxパッケージ

Goの標準ライブラリであるmath/cmplxパッケージは、複素数に関する数学関数を提供します。このコミットでは、cmplx.NaN()cmplx.Inf()を使用して、NaNやInfを含む複素数を生成しています。

技術的詳細

このコミットは、src/pkg/runtime/complex_test.goという新しいファイルを追加し、complex128型の除算に関する5つのベンチマーク関数を定義しています。これらのベンチマークは、complex128の除算が様々な入力値(通常の数値、NaN、Inf)に対してどの程度の性能を発揮するかを測定します。

各ベンチマーク関数は、b.N回ループを実行し、その中で複素数除算n / dを繰り返し行っています。結果はresultというグローバル変数に代入されており、これはコンパイラが最適化によって除算処理を削除してしまうのを防ぐための一般的な手法です(結果が使用されないと判断されると、コンパイラが処理を最適化で消してしまう可能性があるため)。

具体的には、以下の5つのベンチマークが追加されています。

  1. BenchmarkComplex128DivNormal:

    • 通常の有限な複素数同士の除算性能を測定します。
    • d = 15 + 2i, n = 32 + 3iという具体的な値を使用しています。
  2. BenchmarkComplex128DivNisNaN:

    • 被除数(分子)nがNaNである場合の除算性能を測定します。
    • d = cmplx.NaN()を使用し、nは通常の有限な値です。
  3. BenchmarkComplex128DivDisNaN:

    • 除数(分母)dがNaNである場合の除算性能を測定します。
    • n = cmplx.NaN()を使用し、dは通常の有限な値です。
  4. BenchmarkComplex128DivNisInf:

    • 被除数(分子)nが無限大(Inf)である場合の除算性能を測定します。
    • n = cmplx.Inf()を使用し、dは通常の有限な値です。
  5. BenchmarkComplex128DivDisInf:

    • 除数(分母)dが無限大(Inf)である場合の除算性能を測定します。
    • d = cmplx.Inf()を使用し、nは通常の有限な値です。

これらのベンチマークは、Goのruntimeパッケージの内部実装におけるcomplex128除算の効率性、特に特殊な浮動小数点値のハンドリングが性能に与える影響を評価するために重要です。

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

src/pkg/runtime/complex_test.go が新規追加されています。

--- /dev/null
+++ b/src/pkg/runtime/complex_test.go
@@ -0,0 +1,57 @@
+// 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 runtime_test
+
+import (
+	"math/cmplx"
+	"testing"
+)
+
+var result complex128
+
+func BenchmarkComplex128DivNormal(b *testing.B) {
+	d := 15 + 2i
+	n := 32 + 3i
+	for i := 0; i < b.N; i++ {
+		n += n / d
+	}
+	result = n
+}
+
+func BenchmarkComplex128DivNisNaN(b *testing.B) {
+	d := cmplx.NaN()
+	n := 32 + 3i
+	for i := 0; i < b.N; i++ {
+		n += n / d
+	}
+	result = n
+}
+
+func BenchmarkComplex128DivDisNaN(b *testing.B) {
+	d := 15 + 2i
+	n := cmplx.NaN()
+	for i := 0; i < b.N; i++ {
+		n += n / d
+	}
+	result = n
+}
+
+func BenchmarkComplex128DivNisInf(b *testing.B) {
+	d := 15 + 2i
+	n := cmplx.Inf()
+	for i := 0; i < b.N; i++ {
+		n += n / d
+	}
+	result = n
+}
+
+func BenchmarkComplex128DivDisInf(b *testing.B) {
+	d := cmplx.Inf()
+	n := 32 + 3i
+	for i := 0; i < b.N; i++ {
+		n += n / d
+	}
+	result = n
+}

コアとなるコードの解説

追加されたcomplex_test.goファイルは、runtime_testパッケージに属しています。これは、runtimeパッケージの内部実装をテスト・ベンチマークするための慣例的な方法です。

  • import ("math/cmplx", "testing"):

    • math/cmplxパッケージは、複素数のNaNやInfを生成するために使用されます。
    • testingパッケージは、Goのベンチマーク機能を提供します。
  • var result complex128:

    • グローバル変数resultは、ベンチマークの計算結果を保持するために宣言されています。これにより、コンパイラがループ内の除算演算をデッドコードとして最適化で削除してしまうのを防ぎます。ベンチマークの正確な測定には、実際に計算が行われることが不可欠です。
  • func BenchmarkComplex128DivNormal(b *testing.B) などのベンチマーク関数:

    • 各関数はtesting.B型の引数bを受け取ります。
    • b.Nは、ベンチマーク実行時にGoのテストフレームワークによって自動的に調整されるループ回数です。これにより、統計的に有意な測定結果が得られるように、十分な回数の操作が実行されます。
    • n += n / dという演算は、除算の結果をnに加算することで、除算が実際に実行され、その結果が後続の計算に影響を与えることを保証しています。これにより、コンパイラが除算を最適化でスキップするのを防ぎます。

これらのベンチマークは、Goのruntimecomplex128の除算をどのように処理するか、特にエッジケース(NaNやInf)において、その性能特性を詳細に把握するための重要なツールとなります。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • testingパッケージのドキュメント
  • math/cmplxパッケージのドキュメント
  • 浮動小数点数に関する一般的な知識
  • Go言語のベンチマークに関する一般的なプラクティス