strings.Replace と strings.Replacer はどっちが早い


概要

複数の文字を置換するときは strings.Replacer でまとめて置換した方が早そうな印象がありました。
しかし strings.Replace のコードの方が -bench の結果がよい状況に出会したので改めて確認しました。

用語

やりたいこと

ABCDEA1BC23 に置き換えたいとき

用語

(用語に自信がないので以下の使い方で書きます)

  • 「文字」
    • 上記やりたいことでいうところの、A
  • 「文字列」
    • 上記やりたいことでいうところの、BC
  • 「置き換えたい文字(列)」
    • 上記やりたいことでいうところの、ABC
  • 「置換対象文字列」
    • 上記やりたいことでいうところの、ABCDE

さっそくまとめ

以下の場合は strings.Replace を複数回実行した方が早そうです。

  • 「置き換えたい文字(列)」が少ないとき
  • 「文字列」を置き換えたいとき
  • 「置換対象文字列」に、「置き換えたい文字(列)」が含まれない場合があるとき

ベンチマーク

以下の通り確認しましたが、よくないところがあれば教えてもらえるとうれしいです。

strings.Replaceのベンチマーク.go
const str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ"
func BenchmarkStringsReplace(b *testing.B) {
    b.Run("2 strings", func(b *testing.B) {
        b.Run("Replace all", func(b *testing.B) {
            b.ResetTimer()
            for i := 0; i < b.N; i++ {
                s := strings.Replace(str, "ABC", "123", -1)
                strings.Replace(s, "DEF", "456", -1)
            }
        })

        b.Run("Replace some", func(b *testing.B) {
            b.ResetTimer()
            for i := 0; i < b.N; i++ {
                s := strings.Replace(str, "ABC", "123", -1)
                strings.Replace(s, "456", "___", -1)
            }
        })
    })
}
strings.Replacerのベンチマーク.go
const str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ"
func BenchmarkStringsReplacer(b *testing.B) {
    b.Run("2 strings", func(b *testing.B) {
        b.Run("Replace all.", func(b *testing.B) {
            oldnew := []string{
                "ABC", "123",
                "DEF", "456",
            }
            r := strings.NewReplacer(oldnew...)
            b.ResetTimer()
            for i := 0; i < b.N; i++ {
                r.Replace(str)
            }
        })

        b.Run("Replace some.", func(b *testing.B) {
            oldnew := []string{
                "ABC", "123",
                "456", "___",
            }
            r := strings.NewReplacer(oldnew...)
            b.ResetTimer()
            for i := 0; i < b.N; i++ {
                r.Replace(str)
            }
        })
    })
}
BenchmarkStringsReplace/2_strings/Replace_all-8          3911292           305 ns/op         320 B/op          4 allocs/op
BenchmarkStringsReplace/2_strings/Replace_some-8         7506202           164 ns/op         160 B/op          2 allocs/op
BenchmarkStringsReplacer/2_strings/Replace_all.-8        3955276           305 ns/op         192 B/op          3 allocs/op
BenchmarkStringsReplacer/2_strings/Replace_some.-8       4732401           254 ns/op         192 B/op          3 allocs/op

各環境、各バージョンの結果:
https://github.com/abetomo/strings-replace-bench/actions/runs/559930095