🙌

Goでひらがなをカタカナに変換する処理を書いてベンチマークを取ってみた

に公開

GoにはUnicodeの特定の文字に対して、code pointを一定数ずらすことができるようになっています。
この機能を使ってひらがなをカタカナにするコードを書いてみました。

package jptokenizer

import (
	"strings"
	"unicode"
)

var (
	kanaConv = unicode.SpecialCase{
		unicode.CaseRange{
			Lo: 0x3040, // ぁ
			Hi: 0x309F, // ん
			Delta: [unicode.MaxCase]rune{
				0x30A0 - 0x3040,
				0,
				0,
			},
		},
	}
)

func ToKatakana(s string) string {
	return strings.ToUpperSpecial(kanaConv, s)
}

結構簡単にかけますよね。
ただ、これはひらがなとカタカナをマッピングしたMapから変換するのと比べると速いのでしょうか?
で、こんなコードを書いてbenckmarkを取ってみました

package jptokenizer

import (
	"strings"
	"unicode"
)

var (
	kanaConv = unicode.SpecialCase{
		// Hiragana: https://www.unicode.org/charts/PDF/U3040.pdf
		// Katakana: https://www.unicode.org/charts/PDF/U30A0.pdf
		unicode.CaseRange{
			Lo: 0x3040, // Lo: ぁ
			Hi: 0x309F, // Hi: ん
			Delta: [unicode.MaxCase]rune{
				0x30A0 - 0x3040,
				0,
				0,
			},
		},
	}
	hiraganaToKatakanaMap = map[string]string{
		"あ": "ア",
		"い": "イ",
		"う": "ウ",
		"え": "エ",
		"お": "オ",
		"か": "カ",
		"き": "キ",
		"く": "ク",
		"け": "ケ",
		"こ": "コ",
		"さ": "サ",
		"し": "シ",
		"す": "ス",
		"せ": "セ",
		"そ": "ソ",
		"た": "タ",
		"ち": "チ",
		"つ": "ツ",
		"て": "テ",
		"と": "ト",
		"な": "ナ",
		"に": "ニ",
		"ぬ": "ヌ",
		"ね": "ネ",
		"の": "ノ",
		"は": "ハ",
		"ひ": "ヒ",
		"ふ": "フ",
		"へ": "ヘ",
		"ほ": "ホ",
		"ま": "マ",
		"み": "ミ",
		"む": "ム",
		"め": "メ",
		"も": "モ",
		"や": "ヤ",
		"ゆ": "ユ",
		"よ": "ヨ",
		"ら": "ラ",
		"り": "リ",
		"る": "ル",
		"れ": "レ",
		"ろ": "ロ",
		"わ": "ワ",
		"を": "ヲ",
		"ん": "ン",
	}
)

func ToKatakana(s string) string {
	return strings.ToUpperSpecial(kanaConv, s)
}

func ToKatakanaFromMap(s string) string {
	var result strings.Builder
	result.Grow(len(s))
	for _, r := range s {
		char := string(r)
		if katakana, ok := hiraganaToKatakanaMap[char]; ok {
			result.WriteString(katakana)
		} else {
			result.WriteString(char)
		}
	}

	return result.String()
}

benchmarkコード

package jptokenizer

import "testing"

var (
	// ベンチマーク用のテストデータ
	shortText  = "あいうえお"
	mediumText = "これはテストです。ひらがなとカタカナの変換をテストします。"
	longText   = "これは非常に長いテストテキストです。ひらがなからカタカナへの変換のパフォーマンスをテストするために使用されます。たくさんのひらがなが含まれており、変換処理の性能を測定できます。実際のアプリケーションでは、このような長いテキストを処理することもあるでしょう。"
	mixedText  = "Hello世界!これはmixed textです。ひらがなとEnglishが混在しています。"
)

// ベンチマーク: ToKatakana関数のテスト
func BenchmarkToKatakana_Short(b *testing.B) {
	for i := 0; i < b.N; i++ {
		ToKatakana(shortText)
	}
}

func BenchmarkToKatakana_Medium(b *testing.B) {
	for i := 0; i < b.N; i++ {
		ToKatakana(mediumText)
	}
}

func BenchmarkToKatakana_Long(b *testing.B) {
	for i := 0; i < b.N; i++ {
		ToKatakana(longText)
	}
}

func BenchmarkToKatakana_Mixed(b *testing.B) {
	for i := 0; i < b.N; i++ {
		ToKatakana(mixedText)
	}
}

// ベンチマーク: ToKatakanaFromMap関数のテスト
func BenchmarkToKatakanaFromMap_Short(b *testing.B) {
	for i := 0; i < b.N; i++ {
		ToKatakanaFromMap(shortText)
	}
}

func BenchmarkToKatakanaFromMap_Medium(b *testing.B) {
	for i := 0; i < b.N; i++ {
		ToKatakanaFromMap(mediumText)
	}
}

func BenchmarkToKatakanaFromMap_Long(b *testing.B) {
	for i := 0; i < b.N; i++ {
		ToKatakanaFromMap(longText)
	}
}

func BenchmarkToKatakanaFromMap_Mixed(b *testing.B) {
	for i := 0; i < b.N; i++ {
		ToKatakanaFromMap(mixedText)
	}
}

結果

% go test -bench=. -benchmem
goos: darwin
goarch: arm64
pkg: github.com/SpringMT/jptokenizer
cpu: Apple M3 Pro
BenchmarkToKatakana_Short-12                    19980058                58.33 ns/op           24 B/op          1 allocs/op
BenchmarkToKatakana_Medium-12                    2502476               483.9 ns/op            96 B/op          1 allocs/op
BenchmarkToKatakana_Long-12                       579843              2065 ns/op             416 B/op          1 allocs/op
BenchmarkToKatakana_Mixed-12                     2528209               472.7 ns/op            96 B/op          1 allocs/op
BenchmarkToKatakanaFromMap_Short-12             15088131                80.50 ns/op           16 B/op          1 allocs/op
BenchmarkToKatakanaFromMap_Medium-12             2321415               521.3 ns/op            96 B/op          1 allocs/op
BenchmarkToKatakanaFromMap_Long-12                456230              2603 ns/op             384 B/op          1 allocs/op
BenchmarkToKatakanaFromMap_Mixed-12              1620055               716.9 ns/op            96 B/op          1 allocs/op

ToKatakana vs ToKatakanaFromMap 比較:

  1. 短いテキスト:
    ToKatakana: 58.33 ns/op vs ToKatakanaFromMap: 80.50 ns/op
    差: ToKatakanaが約1.4倍高速
  2. 中程度のテキスト:
    ToKatakana: 483.9 ns/op vs ToKatakanaFromMap: 521.3 ns/op
    差: ToKatakanaが約1.1倍高速
  3. 長いテキスト:
    ToKatakana: 2065 ns/op vs ToKatakanaFromMap: 2603 ns/op
    差: ToKatakanaが約1.3倍高速

ToKatakanaのほうが若干ではありますが速いですね。
これだとコードが短くかけるToUpperSpecialのほうを使ったほうがよさそうですね。

Discussion