[インデックス 10657] ファイルの概要
このコミットは、Go言語の標準ライブラリであるstrconv
パッケージにおけるドキュメンテーションの修正と、浮動小数点数フォーマット処理のパフォーマンス改善を目的としています。具体的には、ParseInt
関数のコメントがより正確になるように更新され、FormatFloat
関数における内部バッファの初期サイズ計算が最適化されています。
コミット
commit 2e3bd890c5942a00f6271ffe419c1e62ef5e2a73
Author: Robert Griesemer <gri@golang.org>
Date: Wed Dec 7 14:45:45 2011 -0800
strconv: fix documentation
Also: minor performance fix for large precision results.
benchmark old ns/op new ns/op delta
strconv_test.BenchmarkFormatFloatDecimal 2734 2734 +0.00%
strconv_test.BenchmarkFormatFloat 3141 3139 -0.06%
strconv_test.BenchmarkFormatFloatExp 8970 8989 +0.21%
strconv_test.BenchmarkFormatFloatBig 3228 3208 -0.62%
Fixes #2535.
R=rsc
CC=golang-dev
https://golang.org/cl/5435089
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/2e3bd890c5942a00f6271ffe419c1e62ef5e2a73
元コミット内容
strconv: fix documentation
Also: minor performance fix for large precision results.
benchmark old ns/op new ns/op delta strconv_test.BenchmarkFormatFloatDecimal 2734 2734 +0.00% strconv_test.BenchmarkFormatFloat 3141 3139 -0.06% strconv_test.BenchmarkFormatFloatExp 8970 8989 +0.21% strconv_test.BenchmarkFormatFloatBig 3228 3208 -0.62%
Fixes #2535.
R=rsc CC=golang-dev https://golang.org/cl/5435089
変更の背景
このコミットは主に二つの側面からGo言語のstrconv
パッケージを改善しています。
- ドキュメンテーションの明確化:
strconv.ParseInt
関数のドキュメンテーションが、引数名と説明の整合性を高めるために修正されました。これは、コードの可読性と利用者が関数を正しく理解し使用するための重要な改善です。特に、base
引数がb
からbase
に、戻り値がn
からi
に変更されたことに合わせて、説明も更新されています。 - パフォーマンス最適化:
strconv.FormatFloat
関数において、浮動小数点数を文字列に変換する際の内部バッファの初期サイズ計算が最適化されました。これにより、特に高い精度で浮動小数点数をフォーマットする際に、不要なメモリ再割り当てが減少し、パフォーマンスが向上します。コミットメッセージに含まれるベンチマーク結果は、この変更が特にBenchmarkFormatFloatBig
のようなシナリオでわずかながらもパフォーマンス改善に寄与していることを示しています。 - バグ修正: このコミットはGoのIssue #2535を修正しています。このIssueは、
strconv.ParseInt
のドキュメンテーションが不正確であるという報告でした。
前提知識の解説
Go言語のstrconv
パッケージ
strconv
パッケージは、Go言語の標準ライブラリの一部であり、基本的なデータ型(文字列、整数、浮動小数点数、真偽値など)間の変換機能を提供します。例えば、文字列を整数に変換したり(Atoi
, ParseInt
)、浮動小数点数を文字列に変換したり(FormatFloat
)する際に使用されます。これらの変換は、ユーザー入力の処理、設定ファイルの解析、データシリアライズなど、多くのアプリケーションで不可欠です。
ParseInt
関数
func ParseInt(s string, base int, bitSize int) (i int64, err error)
ParseInt
関数は、指定された文字列s
を、与えられたbase
(基数、2から36まで)で解釈し、対応するint64
型の整数値i
を返します。
s
: 変換対象の文字列。base
: 基数。2から36までの範囲で指定します。base
が0
の場合、文字列のプレフィックスに基づいて基数が自動的に決定されます。"0x"
または"0X"
プレフィックスがあれば16進数(基数16)。"0"
プレフィックスがあれば8進数(基数8)。- それ以外は10進数(基数10)。
bitSize
: 結果が収まるべき整数型のビットサイズを指定します。0
,8
,16
,32
,64
のいずれかを指定でき、それぞれint
,int8
,int16
,int32
,int64
に対応します。
FormatFloat
関数
func FormatFloat(f float64, fmt byte, prec, bitSize int) string
FormatFloat
関数は、float64
型の浮動小数点数f
を文字列に変換します。
f
: 変換対象の浮動小数点数。fmt
: フォーマットの種類を指定する文字(例:'f'
は-ddd.dddd
形式、'e'
は-d.ddddede±dd
形式、'g'
は'e'
または'f'
のより簡潔な方)。prec
: 精度。fmt
の種類によって意味が異なります。'f'
,'e'
,'E'
の場合、小数点以下の桁数を指定します。'g'
,'G'
の場合、有効数字の桁数を指定します。-1
を指定すると、必要な最小限の桁数でフォーマットされます。
bitSize
:f
がfloat32
かfloat64
かを示します(32
または64
)。
メモリ割り当てとパフォーマンス
Go言語では、文字列やスライス(可変長配列)を扱う際に、内部的にメモリが割り当てられます。特に、文字列の構築やスライスの拡張では、既存の容量が不足した場合に新しい、より大きなメモリ領域が割り当てられ、古い内容がコピーされる「再割り当て」が発生します。この再割り当ては、頻繁に発生するとパフォーマンスのオーバーヘッドになります。
make([]byte, 0, capacity)
のように、スライスを初期化する際にcapacity
(容量)を事前に指定することで、将来の要素追加に備えて十分なメモリを確保し、不必要な再割り当てを減らすことができます。これにより、特に大量のデータを扱う場合や、ループ内で繰り返しスライスを構築する場合にパフォーマンスが向上します。
技術的詳細
strconv.ParseInt
のドキュメンテーション修正
変更前:
// ParseInt interprets a string s in an arbitrary base b (2 to 36)
// and returns the corresponding value n. If b == 0, the base
// is taken from the string prefix: base 16 for "0x", base 8 for "0",
// and base 10 otherwise.
変更後:
// ParseInt interprets a string s in the given base (2 to 36) and
// returns the corresponding value i. If base == 0, the base is
// implied by the string's prefix: base 16 for "0x", base 8 for
// "0", and base 10 otherwise.
この変更は、関数の引数名と戻り値の変数名に合わせてドキュメンテーションを修正しています。具体的には、base b
をgiven base
に、value n
をvalue i
に変更しています。これにより、コードとドキュメンテーションの整合性が保たれ、開発者が関数シグネチャとドキュメンテーションを照らし合わせる際に混乱が生じにくくなります。これは、Go言語のドキュメンテーション規約に沿った、品質向上のための典型的な修正です。
strconv.FormatFloat
のパフォーマンス最適化
変更前:
return string(genericFtoa(make([]byte, 0, 16), f, fmt, prec, bitSize))
変更後:
return string(genericFtoa(make([]byte, 0, max(prec+4, 24)), f, fmt, prec, bitSize))
この変更は、FormatFloat
関数が内部的に使用するgenericFtoa
関数に渡すバイトスライスの初期容量を調整しています。
- 変更前:
make([]byte, 0, 16)
は、初期容量を16バイトに固定していました。これは、多くの一般的な浮動小数点数フォーマットには十分かもしれませんが、非常に高い精度(prec
が大きい場合)を要求される場合には、変換結果の文字列が16バイトを超えることが頻繁に発生し、そのたびに内部でスライスの再割り当てとデータコピーが発生していました。 - 変更後:
make([]byte, 0, max(prec+4, 24))
は、初期容量をprec + 4
と24
の大きい方で決定するように変更しています。prec + 4
:prec
は小数点以下の桁数または有効数字の桁数を指定します。浮動小数点数を文字列に変換する際、符号、整数部、小数点、指数部などのオーバーヘッドを考慮すると、prec
に数バイトを加えた値が、最終的な文字列長の良い見積もりになります。+4
は、これらのオーバーヘッド(例: 符号、小数点、指数表記のe
、指数部の符号と数字)を考慮した経験的な値と考えられます。24
: これは、一般的な浮動小数点数の文字列表現が少なくともこの程度の長さになることが多いという、最小限の安全な初期容量を示している可能性があります。例えば、float64
の最大精度で表現した場合、約17桁の数字が必要となり、それに符号や小数点などが加わると20バイトを超えることがあります。max(...)
:prec + 4
と24
の大きい方を取ることで、prec
が小さい場合でも最低限の容量を確保しつつ、prec
が大きい場合には十分な容量を事前に確保し、再割り当ての頻度を減らすことを目指しています。
この最適化により、特にprec
が大きい場合に、genericFtoa
関数内でバイトスライスが拡張される回数が減り、結果としてFormatFloat
の実行速度が向上します。コミットメッセージのベンチマーク結果では、BenchmarkFormatFloatBig
で-0.62%
の改善が見られており、これは大きな精度での処理において効果があったことを示唆しています。
コアとなるコードの変更箇所
src/pkg/strconv/atoi.go
--- a/src/pkg/strconv/atoi.go
+++ b/src/pkg/strconv/atoi.go
@@ -119,10 +119,10 @@ Error:
return n, &NumError{s0, err}
}
-// ParseInt interprets a string s in an arbitrary base b (2 to 36)
-// and returns the corresponding value n. If b == 0, the base
-// is taken from the string prefix: base 16 for "0x", base 8 for "0",
-// and base 10 otherwise.
+// ParseInt interprets a string s in the given base (2 to 36) and
+// returns the corresponding value i. If base == 0, the base is
+// implied by the string's prefix: base 16 for "0x", base 8 for
+// "0", and base 10 otherwise.
//
// The bitSize argument specifies the integer type
// that the result must fit into. Bit sizes 0, 8, 16, 32, and 64
src/pkg/strconv/ftoa.go
--- a/src/pkg/strconv/ftoa.go
+++ b/src/pkg/strconv/ftoa.go
@@ -46,7 +46,7 @@ var float64info = floatInfo{52, 11, -1023}
// because correct rounding and the number of digits
// needed to identify f depend on the precision of the representation.
func FormatFloat(f float64, fmt byte, prec, bitSize int) string {
- return string(genericFtoa(make([]byte, 0, 16), f, fmt, prec, bitSize))
+ return string(genericFtoa(make([]byte, 0, max(prec+4, 24)), f, fmt, prec, bitSize))
}
// AppendFloat appends the string form of the floating-point number f,
コアとなるコードの解説
src/pkg/strconv/atoi.go
の変更
この変更は、ParseInt
関数のドキュメンテーションコメントを更新しています。
- 変更前は、引数
base
をb
と、戻り値int64
をn
と記述していましたが、実際の関数シグネチャに合わせてbase
とi
に修正されました。 - これにより、ドキュメンテーションがより正確になり、関数の利用者が引数や戻り値の意味を誤解する可能性が低減されます。これは、コードの保守性と利用者の体験を向上させるための、小さなしかし重要な改善です。
src/pkg/strconv/ftoa.go
の変更
この変更は、FormatFloat
関数内でgenericFtoa
関数を呼び出す際に、バイトスライスを初期化するmake
関数の容量引数を変更しています。
make([]byte, 0, 16)
からmake([]byte, 0, max(prec+4, 24))
への変更は、genericFtoa
が浮動小数点数を文字列に変換する際に使用する内部バッファの初期容量を動的に調整することを意味します。max(prec+4, 24)
は、要求される精度prec
に基づいて、変換後の文字列が格納されるのに十分な初期容量を計算します。prec+4
は、精度に加えて、符号、小数点、指数表記のための文字など、追加で必要となる文字数を考慮したものです。24
は、prec
が小さい場合でも、ある程度の最小限のバッファサイズを保証するためのものです。
- この変更により、特に高い精度で浮動小数点数をフォーマットする際に、Goランタイムが内部的にバイトスライスの容量を拡張するために行うメモリ再割り当ての回数が減少します。メモリ再割り当てはコストの高い操作であるため、これを減らすことで
FormatFloat
関数の全体的なパフォーマンスが向上します。
関連リンク
- Go言語の
strconv
パッケージのドキュメンテーション: https://pkg.go.dev/strconv - Go Issue #2535:
strconv.ParseInt
documentation is confusing (このコミットによって修正されたIssue) - 検索しても直接的なリンクは見つかりませんでしたが、コミットメッセージに記載されています。
参考にした情報源リンク
- Go言語の公式ドキュメンテーション
- Go言語のソースコード(
src/pkg/strconv/
ディレクトリ) - Go言語のIssueトラッカー (Issue #2535に関する情報)
- Go言語におけるスライスとメモリ割り当てに関する一般的な知識
- 浮動小数点数の表現と文字列変換に関する一般的な知識