読者です 読者をやめる 読者になる 読者になる

golangの正規表現は遅いのか?

golang

golang正規表現は遅いって聞くけど、実際どーなんだろ?
って思ったよっていう記事です。
おそらく、この記事を読んでも何も得られないと思います。

-----追記 2016/11/26 -------
書籍「みんなのGO言語」で少し言及されていました。
golang正規表現は場合によっては perl に負けるほど遅く、
コンパイル言語としては遅い」ということみたいです。
とはいえ、ちゃんとベンチ取らないとダメですね。
-------------------------------

http://qiita.com/naoina/items/d71ddfab31f4b29f6693#正規表現はスイスアーミーナイフではない

ここにあるとおり、正規表現は遅いらしい。
正規表現のベンチを見ると、 643 ns/op。
これは正規表現を1回評価するのに643ナノ秒かかってるってこと。

正規表現を使わない場合は 55.0 ns/op になっているので、
たしかに正規表現を利用しない場合の方がパフォーマンスは良い。

でも、ナノ秒レベルで遅いって、「遅くて困る」っていうレベルの遅さなのか・・・?

仮に 1000回評価したとしたら・・・
643ナノ秒 x 1000 = 0.643ミリ秒 = 0.000643秒

これ遅いのかな・・・?

自分の環境でも上記の記事のベンチマークコードを実行してみる。

func BenchmarkStringMatchWithRegexp(b *testing.B) {
	s := "0xDeadBeef"
	re := regexp.MustCompile(`^0[xX][0-9A-Fa-f]+$`)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		re.MatchString(s)
	}
}

複数回実行した結果、 611 ~ 714 ns/op だった。
上記の記事と大体同じくらいかな。(雑

複数パターンをチェックしたい場合は毎回コンパイルする必要あると思うので、
regexp.MustCompile() をループに入れてみる。

func BenchmarkStringMatchWithRegexp(b *testing.B) {
	s := "0xDeadBeef"
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		re := regexp.MustCompile(`^0[xX][0-9A-Fa-f]+$`) //毎回コンパイルされる
		re.MatchString(s)
	}
}

複数回実行した結果、大体 11700 ns/op だった。
当然ながらコンパイルすると遅い。
これを仮に1000回評価したとしたら・・・ 0.0117 秒


次にパターンマッチに時間がかかりそうな正規表現でやってみる。
以下のサイボウズさんの記事にあったものを利用する。
http://blog.cybozu.io/entry/8757

func BenchmarkStringMatchWithRegexp2(b *testing.B) {
	s := "______________________________"
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		re := regexp.MustCompile(`"(\\w|_){1,64}@"`)
		re.MatchString(s)
	}
}

複数回実行した結果、大体 86000 ns/op だった。
これを仮に1000回評価したとしたら・・・ 0.086 秒
さすがに 0.086 秒 とかだと遅いかな。
これ java だと70秒かかるって書いてあったけど、golang だと全然早いんだけどw
なんか間違ってるのかなw

ということで、ものによっては遅いっちゃ遅いのかな・・・って思うけど、
「ものによっては」とか言ったら、
全部ものによっては遅いってなるから、なんかどーなんでしょうね。

golang にしては遅い処理ってことになるのかな。
たしか、今年の isucon で golang正規表現使ってて、
それを改善するとパフォーマンス出る的な話があった気がする。