[インデックス 11558] ファイルの概要
このコミットは、Go言語の実験的なUnicode正規化パッケージ exp/norm
において、特定の正規化形式間の変換性能を測定するためのベンチマークを追加するものです。これにより、異なる正規化形式(NFC, NFD, NFKC, NFKD)間の変換効率や、特定の文字セット(特にハングル文字)における性能特性を詳細に分析することが可能になります。
コミット
commit d673c95d6c60a287d3fbb865c8885f635505903f
Author: Marcel van Lohuizen <mpvl@golang.org>
Date: Thu Feb 2 13:19:12 2012 +0100
exp/norm: Added some benchmarks for form-specific performance measurements.
R=r
CC=golang-dev
https://golang.org/cl/5605051
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/d673c95d6c60a287d3fbb865c8885f635505903f
元コミット内容
exp/norm: Added some benchmarks for form-specific performance measurements.
このコミットは、exp/norm
パッケージに、特定の正規化形式(Form)に特化したパフォーマンス測定のためのベンチマークを追加したことを示しています。
変更の背景
Unicodeの正規化は、異なる方法で表現されうる同じ文字シーケンスを統一的な形式に変換するプロセスです。例えば、「é」という文字は、単一のコードポイント(U+00E9)で表現することもできますし、「e」と結合文字の「´」(U+0065 U+0301)の組み合わせで表現することもできます。これらの異なる表現は、文字列の比較や検索において問題を引き起こす可能性があります。
Go言語の exp/norm
パッケージは、このようなUnicode正規化を扱うための実験的なライブラリです。このコミットが作成された背景には、おそらく exp/norm
パッケージの性能特性をより深く理解し、最適化の機会を特定するという目的があったと考えられます。特に、異なる正規化形式(NFC, NFD, NFKC, NFKD)間の変換性能や、特定の複雑な文字セット(例: ハングル文字)における性能が、アプリケーションのパフォーマンスに与える影響を評価する必要があったのでしょう。
既存のベンチマークでは、ASCII文字列に対する正規化性能は測定されていましたが、より現実的な、既に正規化された、あるいは異なる正規化形式の入力データに対する変換性能は十分に評価されていなかった可能性があります。このコミットは、そのギャップを埋めるために、より多様な入力と変換シナリオをカバーするベンチマークを追加しています。
前提知識の解説
Unicode正規化 (Unicode Normalization)
Unicode正規化は、Unicode文字列の異なる表現を標準的な形式に変換するプロセスです。これにより、文字列の比較やソートが正しく行われるようになります。Unicodeには主に以下の4つの正規化形式があります。
-
NFC (Normalization Form Canonical Composition):
- 結合文字を可能な限り合成して、最も短い形式にします。
- 例:
e
+´
(U+0065 U+0301) →é
(U+00E9) - Webページやファイルシステムで一般的に使用されます。
-
NFD (Normalization Form Canonical Decomposition):
- 合成された文字を可能な限り分解して、結合文字のシーケンスにします。
- 例:
é
(U+00E9) →e
+´
(U+0065 U+0301) - 文字のプロパティに基づいてソートを行う場合などに有用です。
-
NFKC (Normalization Form Compatibility Composition):
- NFCと同様に結合文字を合成しますが、互換性分解(Compatibility Decomposition)も行います。互換性分解とは、見た目は似ているが意味が異なる文字(例: 全角数字と半角数字、リガチャー)を分解することです。
- 例:
①
(U+2460) →1
(U+0031) - 検索やマッチングで、互換性のある文字を同じものとして扱いたい場合に有用ですが、情報が失われる可能性があります。
-
NFKD (Normalization Form Compatibility Decomposition):
- NFDと同様に文字を分解しますが、NFKCと同様に互換性分解も行います。
- 例:
①
(U+2460) →1
(U+0031) - NFKCと同様に情報が失われる可能性があります。
Go言語の exp/norm
パッケージ
exp/norm
は、Go言語の標準ライブラリの一部として提供されている golang.org/x/text/unicode/norm
パッケージの前身、または実験的なバージョンであると考えられます。このパッケージは、Unicode正規化形式間の変換機能を提供します。
Form
インターフェース: 正規化形式(NFC, NFDなど)を表す型。Bytes([]byte)
メソッド: 指定されたバイトスライスをその形式に正規化して返します。Append([]byte, ...byte)
メソッド: 既存のバイトスライスに正規化されたバイトを追加します。ベンチマークではこのメソッドが使用されています。*testing.B
: Go言語のベンチマークテストで使用される構造体。b.StopTimer()
: タイマーを一時停止します。セットアップコードの時間を測定から除外するために使用されます。b.StartTimer()
: タイマーを再開します。b.SetBytes(int64)
: 1回の操作で処理されるバイト数を設定します。これにより、ベンチマーク結果が「ops/sec」ではなく「MB/sec」のような形式で表示されるようになります。b.N
: ベンチマーク関数が実行される回数。
技術的詳細
このコミットの主要な技術的変更点は、ベンチマーク関数 doFormBenchmark
の拡張と、それを利用した新しいベンチマークの追加です。
doFormBenchmark
関数の変更
元の doFormBenchmark
関数は、単一の正規化形式 f
と入力文字列 s
を受け取っていました。変更後、inf Form
という新しい引数が追加されました。この inf
は「入力形式(input form)」を意味し、ベンチマークの対象となる文字列 s
が、ベンチマーク実行前にどの正規化形式に変換されるべきかを指定します。
- 変更前:
func doFormBenchmark(b *testing.B, f Form, s string)
- 変更後:
func doFormBenchmark(b *testing.B, inf, f Form, s string)
この変更により、ベンチマークの柔軟性が大幅に向上しました。例えば、「NFC形式の文字列をNFD形式に変換する」といった、特定の入力形式から特定の出力形式への変換性能を正確に測定できるようになります。
また、入力データの準備方法も変更されました。
- 変更前:
in := []byte(s)
(入力文字列をそのままバイトスライスに変換) - 変更後:
in := inf.Bytes([]byte(s))
(入力文字列をまずinf
で指定された形式に正規化してからバイトスライスに変換)
これにより、ベンチマークの対象となるデータが、常に指定された入力形式 inf
に準拠していることが保証されます。b.SetBytes
の引数も len(s)
から len(in)
に変更され、実際に処理されるバイト数に基づいて性能が測定されるようになりました。
新しいベンチマークの追加
追加されたベンチマークは、主に以下の2つのカテゴリに分けられます。
-
異なる正規化形式間の変換ベンチマーク:
BenchmarkNormalizeNFC2NFC
: NFC入力からNFC出力への変換BenchmarkNormalizeNFC2NFD
: NFC入力からNFD出力への変換BenchmarkNormalizeNFD2NFC
: NFD入力からNFC出力への変換BenchmarkNormalizeNFD2NFD
: NFD入力からNFD出力への変換 これらのベンチマークは、txt_all
という多様なUnicode文字を含む文字列を使用しており、一般的なテキストデータに対する変換性能を評価します。
-
ハングル文字に特化したベンチマーク:
BenchmarkNormalizeHangulNFC2NFC
BenchmarkNormalizeHangulNFC2NFD
BenchmarkNormalizeHangulNFD2NFC
BenchmarkNormalizeHangulNFD2NFD
ハングル文字は、Unicodeの結合文字の規則が複雑であり、正規化処理において特殊なケースとなることが多いため、個別に性能を測定することが重要です。これらのベンチマークはtxt_kr
(韓国語テキスト) を使用しています。
新しいテストデータの追加
コミットの最後で、txt_cjk
と txt_all
という新しい定数が定義されています。
txt_cjk
: 中国語 (txt_cn
)、日本語 (txt_jp
)、韓国語 (txt_kr
) のテキストを結合したもの。CJK (Chinese, Japanese, Korean) 文字は、Unicodeの範囲が広く、正規化処理に影響を与える可能性があるため、これらの文字を含むデータセットは重要です。txt_all
: ベトナム語 (txt_vn
)、2バイトUTF-8文字 (twoByteUtf8
)、3バイトUTF-8文字 (threeByteUtf8
)、そしてtxt_cjk
を結合したもの。これは、より広範なUnicode文字セットをカバーする包括的なテストデータとして機能します。
これらの新しいテストデータは、ベンチマークがより現実的で多様な入力シナリオをカバーできるようにするために不可欠です。
コアとなるコードの変更箇所
変更は src/pkg/exp/norm/normalize_test.go
ファイルに集中しています。
--- a/src/pkg/exp/norm/normalize_test.go
+++ b/src/pkg/exp/norm/normalize_test.go
@@ -495,11 +495,11 @@ func TestAppend(t *testing.T) {
runAppendTests(t, "TestString", NFKC, stringF, appendTests)
}
-func doFormBenchmark(b *testing.B, f Form, s string) {
+func doFormBenchmark(b *testing.B, inf, f Form, s string) {
b.StopTimer()
- in := []byte(s)
+ in := inf.Bytes([]byte(s))
buf := make([]byte, 2*len(in))
- b.SetBytes(int64(len(s)))
+ b.SetBytes(int64(len(in)))
b.StartTimer()
for i := 0; i < b.N; i++ {
buf = f.Append(buf[0:0], in...)
@@ -510,16 +510,43 @@ func doFormBenchmark(b *testing.B, f Form, s string) {
var ascii = strings.Repeat("There is nothing to change here! ", 500)
func BenchmarkNormalizeAsciiNFC(b *testing.B) {
- doFormBenchmark(b, NFC, ascii)
+ doFormBenchmark(b, NFC, NFC, ascii)
}
func BenchmarkNormalizeAsciiNFD(b *testing.B) {
- doFormBenchmark(b, NFD, ascii)
+ doFormBenchmark(b, NFC, NFD, ascii)
}
func BenchmarkNormalizeAsciiNFKC(b *testing.B) {
- doFormBenchmark(b, NFKC, ascii)
+ doFormBenchmark(b, NFC, NFKC, ascii)
}
func BenchmarkNormalizeAsciiNFKD(b *testing.B) {
- doFormBenchmark(b, NFKD, ascii)
+ doFormBenchmark(b, NFC, NFKD, ascii)
+}
+
+func BenchmarkNormalizeNFC2NFC(b *testing.B) {
+ doFormBenchmark(b, NFC, NFC, txt_all)
+}
+func BenchmarkNormalizeNFC2NFD(b *testing.B) {
+ doFormBenchmark(b, NFC, NFD, txt_all)
+}
+func BenchmarkNormalizeNFD2NFC(b *testing.B) {
+ doFormBenchmark(b, NFD, NFC, txt_all)
+}
+func BenchmarkNormalizeNFD2NFD(b *testing.B) {
+ doFormBenchmark(b, NFD, NFD, txt_all)
+}
+
+// Hangul is often special-cased, so we test it separately.
+func BenchmarkNormalizeHangulNFC2NFC(b *testing.B) {
+ doFormBenchmark(b, NFC, NFC, txt_kr)
+}
+func BenchmarkNormalizeHangulNFC2NFD(b *testing.B) {
+ doFormBenchmark(b, NFC, NFD, txt_kr)
+}
+func BenchmarkNormalizeHangulNFD2NFC(b *testing.B) {
+ doFormBenchmark(b, NFD, NFC, txt_kr)
+}
+func BenchmarkNormalizeHangulNFD2NFD(b *testing.B) {
+ doFormBenchmark(b, NFD, NFD, txt_kr)
}
func doTextBenchmark(b *testing.B, s string) {
@@ -657,3 +684,6 @@ const txt_cn = `您可以自由: 复制、发行、展览、表演、放映、
署名 — 您必须按照作者或者许可人指定的方式对作品进行署名。
相同方式共享 — 如果您改变、转换本作品或者以本作品为基础进行创作,
您只能采用与本协议相同的许可协议发布基于本作品的演绎作品。`
+
+const txt_cjk = txt_cn + txt_jp + txt_kr
+const txt_all = txt_vn + twoByteUtf8 + threeByteUtf8 + txt_cjk
コアとなるコードの解説
このコミットの核心は、doFormBenchmark
関数のシグネチャ変更と、それを利用した新しいベンチマークの追加です。
-
doFormBenchmark
関数の拡張:func doFormBenchmark(b *testing.B, inf, f Form, s string)
: この関数は、ベンチマークの汎用性を高めるために変更されました。inf Form
: 新しく追加された引数で、入力文字列s
がベンチマークの対象となる正規化処理の前に、どの正規化形式に変換されるべきかを指定します。これにより、例えば「NFC形式の文字列をNFDに変換する際の性能」といった、より具体的なシナリオをテストできるようになります。in := inf.Bytes([]byte(s))
: ベンチマークの計測開始前に、入力文字列s
をinf
で指定された形式に正規化します。これにより、ベンチマークは常に、特定の入力形式を持つデータに対して実行されることになります。b.SetBytes(int64(len(in)))
: ベンチマークが処理するバイト数を、正規化後の入力データのサイズに基づいて設定します。これにより、ベンチマーク結果がより正確なスループット(例: MB/s)として報告されます。
-
既存のASCIIベンチマークの更新:
BenchmarkNormalizeAsciiNFC
などの既存のASCII文字列に対するベンチマークも、doFormBenchmark
の新しいシグネチャに合わせて引数が追加されました。例えば、doFormBenchmark(b, NFC, NFC, ascii)
のように、入力形式も明示的に指定されるようになりました。ASCII文字列の場合、どの正規化形式に変換しても内容は変わらないため、入力形式と出力形式を同じNFC
に設定しています。
-
新しい正規化形式間変換ベンチマークの追加:
BenchmarkNormalizeNFC2NFC
,BenchmarkNormalizeNFC2NFD
,BenchmarkNormalizeNFD2NFC
,BenchmarkNormalizeNFD2NFD
: これらのベンチマークは、txt_all
という多様なUnicode文字を含む文字列を使用して、異なる正規化形式間の変換性能を測定します。例えば、BenchmarkNormalizeNFC2NFD
は、NFC形式の入力文字列をNFD形式に変換する際の性能を評価します。これは、実際のアプリケーションで異なる正規化形式のデータが混在する場合の性能特性を理解する上で非常に重要です。
-
ハングル文字に特化したベンチマークの追加:
BenchmarkNormalizeHangulNFC2NFC
など: ハングル文字は、Unicodeの結合文字の規則が複雑であり、正規化処理において性能ボトルネックとなる可能性があるため、txt_kr
(韓国語テキスト) を使用して個別にベンチマークが追加されました。これにより、ハングル文字の正規化性能を詳細に分析し、必要に応じて最適化を行うためのデータが得られます。
-
新しいテストデータの定義:
const txt_cjk = txt_cn + txt_jp + txt_kr
: 中国語、日本語、韓国語のテキストを結合した新しい定数。const txt_all = txt_vn + twoByteUtf8 + threeByteUtf8 + txt_cjk
: ベトナム語、2バイトUTF-8、3バイトUTF-8、そしてCJKテキストを結合した、より包括的なテストデータ。 これらの新しいデータセットは、ベンチマークがより広範なUnicode文字と実際のテキストデータに対して実行されることを保証し、より現実的な性能評価を可能にします。
これらの変更により、exp/norm
パッケージの正規化機能のパフォーマンス特性を、より詳細かつ多角的に分析できるようになり、将来的な最適化や改善のための貴重な情報が提供されます。
関連リンク
- Go言語の
x/text/unicode/norm
パッケージ: https://pkg.go.dev/golang.org/x/text/unicode/norm - Unicode正規化の概要 (Wikipedia): https://ja.wikipedia.org/wiki/Unicode%E6%AD%A3%E8%A6%8F%E5%8C%96
参考にした情報源リンク
- Unicode Standard Annex #15: Unicode Normalization Forms: https://www.unicode.org/reports/tr15/
- Go言語の
testing
パッケージドキュメント: https://pkg.go.dev/testing - Go言語のベンチマークの書き方に関する記事 (例: Goの公式ブログやGoDocなど)
- Go言語のベンチマークに関する公式ドキュメント: https://go.dev/doc/articles/go_benchmarking
- Go言語のテストとベンチマーク: https://go.dev/blog/testing