Save/load 用 Rust 编译正则表达式?
Save/load compiled Regex in Rust?
我正在开发一些性能至关重要的应用程序,我正在寻找改进它的机会。该应用广泛使用正则表达式(regex crate),例如:
use regex::Regex;
...
let Regex = Regex::new(r###"[a-z0-9%]{3,}"###).unwrap();
是否有机会将预编译的 Regex
实例保存到缓冲区(想想 Vec<u8>
)并稍后加载预编译以避免多次编译(在 docs)?
PS。我已经懒洋洋地做了:
lazy_static! {
static ref REGEX2: Regex = Regex::new(r###"[a-z0-9%]{3,}"###).unwrap();
}
尝试在多次调用时节省一些 CPU 周期(但那是另一回事)。
PS。出于好奇,我对代码进行了基准测试:
fn bench_regex_candidates(b: &mut Bencher) {
b.iter(|| {
Regex::new(r###"[a-z0-9%]{3,}"###).unwrap();
});
}
fn bench_regex_content(b: &mut Bencher) {
b.iter(|| {
Regex::new(r###"^([^*|@"!]*?)#([@?$])?#(.+)$"###).unwrap()
});
}
并且编译速度很快(相对来说,满足我的需求):
regex/candidates time: [22.021 us 22.684 us 23.689 us]
Found 11 outliers among 100 measurements (11.00%)
2 (2.00%) high mild
9 (9.00%) high severe
regex/content time: [36.542 us 36.687 us 36.845 us]
Found 8 outliers among 100 measurements (8.00%)
3 (3.00%) high mild
5 (5.00%) high severe
简短的回答是不,你不能。 regex
箱子本身必须提供这样的东西。有一个未解决的问题跟踪它:https://github.com/rust-lang/regex/issues/258
短期内不太可能支持此类功能。一般来说,regex
crate 可以通过两种方式解决这个问题。第一种是使用 Serde 简单地序列化其内部数据结构。第二个是使内部实现与定制的序列化结构一致。
在第一种情况下,朴素方法的实施工作可能还不错,可以通过在几个地方散布一些 derive(serde::Serialize, serde::Deserialize)
来完成。这种方法有两个主要问题。首先,它需要一个显式的序列化步骤,这会产生自己的成本,并且不清楚在足够多的情况下它是否比正则表达式编译本身更快。其次,这会将内部数据结构公开为 public API 保证。所以实际上,实现这一点可能需要选择一种我们可以承诺的格式。这反过来可能需要在序列化之前和反序列化之后进行另一个翻译步骤,从而增加成本。所以总的来说,这种方法需要付出相当多的努力,而且尚不清楚它是否值得。
第二种方法将使序列化和反序列化有效地免费,因为内部表示将是序列化形式。这受到与第一个问题相同的“public API”格式保证的影响,因此必须小心完成以管理兼容性保证。这种方法的另一个主要问题是它会影响整个内部实现。实际上,在编写所有内容时都必须牢记这种约束。例如,不能再使用内部指针。因此,执行此操作所需的代码复杂性有很大的权衡。而且工作量也很大。
其他人提到了对您的代码进行基准测试。你应该这样做,如果你能仔细地证明为什么你需要 eliminate 编译成本,这个问题将是一个更好的问题。仅仅因为您正在编写高性能应用程序并且在运行时使用使用模式字符串编译的正则表达式并不意味着正则表达式编译本身就是瓶颈。绝对可以,但也很有可能不是。因此,检查这个假设非常重要。理想情况下,您将创建一个最小的基准程序来重现应用程序的相同使用模式,然后测量程序的哪些方面使用了大部分时间。
综上所述,regex-automata
crate does support (de)serializing regexes, and it does so using the second approach I outlined above. However, regex-automata
is not a general purpose regex engine and there are severe differences between it and the regex
crate。例如,使用 regex-automata
来编译带有从用户输入派生的模式字符串的正则表达式是不合适的,因为编译可能会花费模式大小的指数时间。
我正在开发一些性能至关重要的应用程序,我正在寻找改进它的机会。该应用广泛使用正则表达式(regex crate),例如:
use regex::Regex;
...
let Regex = Regex::new(r###"[a-z0-9%]{3,}"###).unwrap();
是否有机会将预编译的 Regex
实例保存到缓冲区(想想 Vec<u8>
)并稍后加载预编译以避免多次编译(在 docs)?
PS。我已经懒洋洋地做了:
lazy_static! {
static ref REGEX2: Regex = Regex::new(r###"[a-z0-9%]{3,}"###).unwrap();
}
尝试在多次调用时节省一些 CPU 周期(但那是另一回事)。
PS。出于好奇,我对代码进行了基准测试:
fn bench_regex_candidates(b: &mut Bencher) {
b.iter(|| {
Regex::new(r###"[a-z0-9%]{3,}"###).unwrap();
});
}
fn bench_regex_content(b: &mut Bencher) {
b.iter(|| {
Regex::new(r###"^([^*|@"!]*?)#([@?$])?#(.+)$"###).unwrap()
});
}
并且编译速度很快(相对来说,满足我的需求):
regex/candidates time: [22.021 us 22.684 us 23.689 us]
Found 11 outliers among 100 measurements (11.00%)
2 (2.00%) high mild
9 (9.00%) high severe
regex/content time: [36.542 us 36.687 us 36.845 us]
Found 8 outliers among 100 measurements (8.00%)
3 (3.00%) high mild
5 (5.00%) high severe
简短的回答是不,你不能。 regex
箱子本身必须提供这样的东西。有一个未解决的问题跟踪它:https://github.com/rust-lang/regex/issues/258
短期内不太可能支持此类功能。一般来说,regex
crate 可以通过两种方式解决这个问题。第一种是使用 Serde 简单地序列化其内部数据结构。第二个是使内部实现与定制的序列化结构一致。
在第一种情况下,朴素方法的实施工作可能还不错,可以通过在几个地方散布一些 derive(serde::Serialize, serde::Deserialize)
来完成。这种方法有两个主要问题。首先,它需要一个显式的序列化步骤,这会产生自己的成本,并且不清楚在足够多的情况下它是否比正则表达式编译本身更快。其次,这会将内部数据结构公开为 public API 保证。所以实际上,实现这一点可能需要选择一种我们可以承诺的格式。这反过来可能需要在序列化之前和反序列化之后进行另一个翻译步骤,从而增加成本。所以总的来说,这种方法需要付出相当多的努力,而且尚不清楚它是否值得。
第二种方法将使序列化和反序列化有效地免费,因为内部表示将是序列化形式。这受到与第一个问题相同的“public API”格式保证的影响,因此必须小心完成以管理兼容性保证。这种方法的另一个主要问题是它会影响整个内部实现。实际上,在编写所有内容时都必须牢记这种约束。例如,不能再使用内部指针。因此,执行此操作所需的代码复杂性有很大的权衡。而且工作量也很大。
其他人提到了对您的代码进行基准测试。你应该这样做,如果你能仔细地证明为什么你需要 eliminate 编译成本,这个问题将是一个更好的问题。仅仅因为您正在编写高性能应用程序并且在运行时使用使用模式字符串编译的正则表达式并不意味着正则表达式编译本身就是瓶颈。绝对可以,但也很有可能不是。因此,检查这个假设非常重要。理想情况下,您将创建一个最小的基准程序来重现应用程序的相同使用模式,然后测量程序的哪些方面使用了大部分时间。
综上所述,regex-automata
crate does support (de)serializing regexes, and it does so using the second approach I outlined above. However, regex-automata
is not a general purpose regex engine and there are severe differences between it and the regex
crate。例如,使用 regex-automata
来编译带有从用户输入派生的模式字符串的正则表达式是不合适的,因为编译可能会花费模式大小的指数时间。