Help us understand the problem. What is going on with this article?

Goの正規表現の文字置換のベンチマーク比較

More than 1 year has passed since last update.

Golangで正規表現を使おうとなると、何も考えないとregexpなどを使ってしまいたくなりますが、他の方の記事(参考文献に記載)にもあるように速度がかなり遅くなってしまいます。

そこで、今回はGolangの正規表現のパターンとその速度を検証してみたいと思います。

比較の前提

まず用いるアルゴリズムですが、regexpによる正規表現、strings.Replacerによる置換を用います。また呼び出し方もregexpとstrings.Replacerはそれぞれ正規表現の宣言(それぞれMustCompileとNewReplacer)をループごとに呼び出すかという話があるので、

  • regexpによる正規表現(MustCompile中/外)
  • strings.Replacerによる正規表現(NewReplacer中/外)

の計5パターンを試してみたいと思います。

比較の方法については簡単な正規表現を用います。用途によっても色々あると思うのですが、今回は短文ですが以下の文章を使います。イメージとしてはメールの文章の個人名を正規表現で伏せるという用途を考えます。

田中さん、お元気ですか。今度の火曜日に鈴木さんと打ち合わせがあるのですがご都合いかがでしょうか。

これを

XXさん、お元気ですか。今度の火曜日にXXさんと打ち合わせがあるのですがご都合いかがでしょうか。

比較結果

package main

import (
    "regexp"
    "strings"
    "testing"
)

// Compile抜きReplacer
func BenchmarkReplace(b *testing.B) {
    str := "田中さん、お元気ですか。今度の火曜日に鈴木さんと打ち合わせがあるのですがご都合いかがでしょうか。"
    rep := strings.NewReplacer("田中", "XX", "鈴木", "XX")
    for n := 0; n < b.N; n++ {
        _ = rep.Replace(str)
    }
}

// Compile込みReplacer
func BenchmarkReplaceIn(b *testing.B) {
    str := "田中さん、お元気ですか。今度の火曜日に鈴木さんと打ち合わせがあるのですがご都合いかがでしょうか。"
    for n := 0; n < b.N; n++ {
        rep := strings.NewReplacer("田中", "XX", "鈴木", "XX")
        _ = rep.Replace(str)
    }
}

// コンパイル抜きReg
func BenchmarkReplacereg(b *testing.B) {
    str := "田中さん、お元気ですか。今度の火曜日に鈴木さんと打ち合わせがあるのですがご都合いかがでしょうか。"
    rep := regexp.MustCompile("田中|鈴木")
    for n := 0; n < b.N; n++ {
        _ = rep.ReplaceAllString(str, "")
    }
}

// コンパイル込みReg
func BenchmarkReplaceregIn(b *testing.B) {
    str := "田中さん、お元気ですか。今度の火曜日に鈴木さんと打ち合わせがあるのですがご都合いかがでしょうか。"
    for n := 0; n < b.N; n++ {
        rep := regexp.MustCompile("田中|鈴木")
        _ = rep.ReplaceAllString(str, "")
    }
}
BenchmarkReplace-4               5000000               397 ns/op             320 B/op          3 allocs/op
BenchmarkReplaceIn-4             1000000              1229 ns/op            1104 B/op         10 allocs/op
BenchmarkReplacereg-4             500000              2446 ns/op             368 B/op          4 allocs/op
BenchmarkReplaceregIn-4           200000              8887 ns/op           39392 B/op         31 allocs/op

だいたい、コンパイルがありとなしでns/ops換算でそれぞれReplacerは3.1倍・Regexpでは3.6倍の差が出ました。
また、ReplacerとRegexpでは7倍程度の差が出ました。

この辺はたしかにReplacerの方が早いという結果なのですが、Replacerの定義をfor文の中で行っている場合でもコンパイルを外においているRegexpより倍近く早い結果となりました。

まとめ

今回はReplacerとRegexpの文字列置換についてまとめました。他の方々がやられているように、基本的にReplacerを使うのがベストとは思いますが、一方でstrings.NewReplacerの宣言部分をfor文の外に出すだけでも3倍近く早くなるという結果が得られました。

実際にコードに落とし込む際に、あまりに正規表現の宣言と利用部分がバラバラになっても管理しづらいケースがあるかもしれませんが、うまく使っていけばより早い速度向上に繋がるのかなと思ったりします。

参考文献

Golangの正規表現がどれぐらいおそいのかをPythonと比較してみた話

regexpとの付き合い方 〜 Go言語標準の正規表現ライブラリのパフォーマンスとアルゴリズム〜

Goの正規表現とReplacerを比較した

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away