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

[インデックス 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パッケージを改善しています。

  1. ドキュメンテーションの明確化: strconv.ParseInt関数のドキュメンテーションが、引数名と説明の整合性を高めるために修正されました。これは、コードの可読性と利用者が関数を正しく理解し使用するための重要な改善です。特に、base引数がbからbaseに、戻り値がnからiに変更されたことに合わせて、説明も更新されています。
  2. パフォーマンス最適化: strconv.FormatFloat関数において、浮動小数点数を文字列に変換する際の内部バッファの初期サイズ計算が最適化されました。これにより、特に高い精度で浮動小数点数をフォーマットする際に、不要なメモリ再割り当てが減少し、パフォーマンスが向上します。コミットメッセージに含まれるベンチマーク結果は、この変更が特にBenchmarkFormatFloatBigのようなシナリオでわずかながらもパフォーマンス改善に寄与していることを示しています。
  3. バグ修正: このコミットは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までの範囲で指定します。base0の場合、文字列のプレフィックスに基づいて基数が自動的に決定されます。
    • "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: ffloat32float64かを示します(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 bgiven baseに、value nvalue 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 + 424の大きい方で決定するように変更しています。
    • prec + 4: precは小数点以下の桁数または有効数字の桁数を指定します。浮動小数点数を文字列に変換する際、符号、整数部、小数点、指数部などのオーバーヘッドを考慮すると、precに数バイトを加えた値が、最終的な文字列長の良い見積もりになります。+4は、これらのオーバーヘッド(例: 符号、小数点、指数表記のe、指数部の符号と数字)を考慮した経験的な値と考えられます。
    • 24: これは、一般的な浮動小数点数の文字列表現が少なくともこの程度の長さになることが多いという、最小限の安全な初期容量を示している可能性があります。例えば、float64の最大精度で表現した場合、約17桁の数字が必要となり、それに符号や小数点などが加わると20バイトを超えることがあります。
    • max(...): prec + 424の大きい方を取ることで、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関数のドキュメンテーションコメントを更新しています。

  • 変更前は、引数basebと、戻り値int64nと記述していましたが、実際の関数シグネチャに合わせてbaseiに修正されました。
  • これにより、ドキュメンテーションがより正確になり、関数の利用者が引数や戻り値の意味を誤解する可能性が低減されます。これは、コードの保守性と利用者の体験を向上させるための、小さなしかし重要な改善です。

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言語におけるスライスとメモリ割り当てに関する一般的な知識
  • 浮動小数点数の表現と文字列変換に関する一般的な知識