我想比较使用非贪婪匹配器.*?
和负查找(例如[^/]+/
)之间的正则表达式性能,并编写了以下node.js
脚本:
const URL = "https://this-is-some-hostname-test/login?ReturnUrl=/"
const regex1 = new RegExp("^https?://.*?/(.*)");
const regex2 = new RegExp("^https?://[^/]+/(.*)$");
const iterations = 10000000;
let start = new Date();
for (let i = 0; i < iterations; i++) {
regex1.test(URL);
}
console.log("Time taken for non-greedy match: %s", (new Date()).getTime() - start);
start = new Date();
for (let i = 0; i < iterations; i++) {
regex2.test(URL);
}
console.log("Time taken for negative match: %s", (new Date()).getTime() - start);
这将报告以下结果:
node regexTest.js
Time taken for non-greedy match: 386
Time taken for negative match: 305
然后我想,让我们检查一下,仅仅是为了它,在golang
中也是一样的。我期待golang
比node.js快得多,但令我惊讶的是,它实际上慢了很多-事实上慢了2个数量级!
下面是我的golang
实现:
package main
import (
"fmt"
"regexp"
"time"
)
func main() {
URL := "https://this-is-some-hostname-test/login?ReturnUrl=/"
URLBytes := []byte(URL)
regex1 := regexp.MustCompile("^https?://.*?/(.*)")
regex2 := regexp.MustCompile("^https?://[^/]+/(.*)$")
iterations := 10000000
start := time.Now()
for i := 0; i < iterations; i++ {
regex1.Match(URLBytes)
}
fmt.Printf("Time taken for non-greedy match: %s\n", time.Since(start))
start = time.Now()
for i := 0; i < iterations; i++ {
regex2.Match(URLBytes)
}
fmt.Printf("Time taken for negative match: %s\n", time.Since(start))
}
结果是:
go run main.go
Time taken for non-greedy match: 8.041351917s
Time taken for negative match: 7.905017458s
我一开始尝试了regex.Compile
和regex.MatchString
,但后来决定使用MustCompile
和Match([]byte)
来消除任何错误检查和类型转换。
2条答案
按热度按时间h22fl7wq1#
在Go中,使用Go标准库测试包进行基准测试(ns是纳秒)。
与Node.js的时间相当。
rx_test.go:
e5nqia272#
Go正则表达式很慢,但并没有那么慢。
你的基准测试有很多问题可以解释这一点。主要的一个是确保编译器不会编译掉你的代码!
因为
regex1
、regex2
和URL
都是Node.js中的常量...。。。可以优化为。。
而且因为没有对匹配做任何事情,所以整个循环可以被优化掉。
它们在Go基准测试中不是常量。
为了避免这些问题。
我使用this list of URLs作为输入。
这使得150 ms比950 ms更合理。仍然令人惊讶,但在置信范围内。正则表达式引擎通常是用C编写和优化的,所以这与语言无关,而与正则表达式引擎的优化程度有关。
此外,“挂钟”时间不适合用于基准测试。使用单时钟。您的Go基准测试使用单时钟,而您的Node使用挂钟。我在这里没有解决这个问题。