RegexOptions.Compiled
hướng dẫn công cụ biểu thức chính quy biên dịch biểu thức biểu thức chính quy vào IL bằng cách sử dụng tạo mã nhẹ ( LCG ). Biên soạn Điều này xảy ra trong thời gian xây dựng của đối tượng và chủ yếu làm chậm nó xuống. Đổi lại, các trận đấu sử dụng biểu thức thông thường sẽ nhanh hơn.
Nếu bạn không chỉ định cờ này, biểu thức chính quy của bạn được coi là "diễn giải".
Lấy ví dụ này:
public static void TimeAction(string description, int times, Action func)
{
// warmup
func();
var watch = new Stopwatch();
watch.Start();
for (int i = 0; i < times; i++)
{
func();
}
watch.Stop();
Console.Write(description);
Console.WriteLine(" Time Elapsed {0} ms", watch.ElapsedMilliseconds);
}
static void Main(string[] args)
{
var simple = "^\\d+$";
var medium = @"^((to|from)\W)?(?<url>http://[\w\.:]+)/questions/(?<questionId>\d+)(/(\w|-)*)?(/(?<answerId>\d+))?";
var complex = @"^(([^<>()[\]\\.,;:\s@""]+"
+ @"(\.[^<>()[\]\\.,;:\s@""]+)*)|("".+""))@"
+ @"((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}"
+ @"\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+"
+ @"[a-zA-Z]{2,}))$";
string[] numbers = new string[] {"1","two", "8378373", "38737", "3873783z"};
string[] emails = new string[] { "sam@sam.com", "sss@s", "sjg@ddd.com.au.au", "onelongemail@oneverylongemail.com" };
foreach (var item in new[] {
new {Pattern = simple, Matches = numbers, Name = "Simple number match"},
new {Pattern = medium, Matches = emails, Name = "Simple email match"},
new {Pattern = complex, Matches = emails, Name = "Complex email match"}
})
{
int i = 0;
Regex regex;
TimeAction(item.Name + " interpreted uncached single match (x1000)", 1000, () =>
{
regex = new Regex(item.Pattern);
regex.Match(item.Matches[i++ % item.Matches.Length]);
});
i = 0;
TimeAction(item.Name + " compiled uncached single match (x1000)", 1000, () =>
{
regex = new Regex(item.Pattern, RegexOptions.Compiled);
regex.Match(item.Matches[i++ % item.Matches.Length]);
});
regex = new Regex(item.Pattern);
i = 0;
TimeAction(item.Name + " prepared interpreted match (x1000000)", 1000000, () =>
{
regex.Match(item.Matches[i++ % item.Matches.Length]);
});
regex = new Regex(item.Pattern, RegexOptions.Compiled);
i = 0;
TimeAction(item.Name + " prepared compiled match (x1000000)", 1000000, () =>
{
regex.Match(item.Matches[i++ % item.Matches.Length]);
});
}
}
Nó thực hiện 4 bài kiểm tra trên 3 biểu thức chính quy khác nhau. Đầu tiên nó kiểm tra một đơn một lần ra khỏi trận đấu (biên soạn vs phi biên soạn). Thứ hai, nó kiểm tra các kết quả khớp lặp lại sử dụng cùng một biểu thức chính quy.
Kết quả trên máy của tôi (được biên dịch trong bản phát hành, không có trình gỡ lỗi nào được đính kèm)
1000 trận đấu đơn (xây dựng Regex, trận đấu và xử lý)
Loại | Nền tảng | Số tầm thường | Kiểm tra email đơn giản | Kiểm tra email mở rộng
-------------------------------------------------- ----------------------------
Giải thích | x86 | 4 ms | 26 ms | 31 ms
Giải thích | x64 | 5 ms | 29 ms | 35 ms
Biên soạn | x86 | 913 ms | 3775 ms | 4487 ms
Biên soạn | x64 | 3300 ms | 21985 ms | 22793 ms
1.000.000 trận đấu - tái sử dụng đối tượng Regex
Loại | Nền tảng | Số tầm thường | Kiểm tra email đơn giản | Kiểm tra email mở rộng
-------------------------------------------------- ----------------------------
Giải thích | x86 | 422 ms | 461 ms | 2122 ms
Giải thích | x64 | 436 ms | 463 ms | 2167 ms
Biên soạn | x86 | 279 ms | 166 ms | 1268 ms
Biên soạn | x64 | 281 ms | 176 ms | 1180 ms
Các kết quả này cho thấy các biểu thức chính quy được biên dịch có thể nhanh hơn tới 60% cho các trường hợp bạn sử dụng lại Regex
đối tượng. Tuy nhiên, trong một số trường hợp có thể chậm hơn 3 bậc độ lớn để xây dựng.
Nó cũng cho thấy rằng phiên bản x64 của .NET có thể chậm hơn từ 5 đến 6 lần khi biên dịch các biểu thức thông thường.
Khuyến nghị là sử dụng phiên bản đã biên dịch trong trường hợp
- Bạn không quan tâm đến chi phí khởi tạo đối tượng và cần tăng hiệu suất bổ sung. (lưu ý chúng ta đang nói về phân số của một phần nghìn giây ở đây)
- Bạn quan tâm một chút về chi phí khởi tạo, nhưng đang sử dụng lại đối tượng Regex rất nhiều lần để nó bù cho nó trong vòng đời ứng dụng của bạn.
Spanner trong công việc, bộ đệm Regex
Công cụ biểu thức chính quy chứa bộ đệm LRU chứa 15 biểu thức chính quy cuối cùng đã được kiểm tra bằng các phương thức tĩnh trên Regex
lớp.
Ví dụ : Regex.Replace
, Regex.Match
vv .. tất cả đều sử dụng bộ đệm Regex.
Kích thước của bộ đệm có thể được tăng lên bằng cách cài đặt Regex.CacheSize
. Nó chấp nhận thay đổi kích thước bất cứ lúc nào trong vòng đời của ứng dụng của bạn.
Các biểu thức chính quy mới chỉ được lưu trữ bởi các trình trợ giúp tĩnh trên lớp Regex. Tuy nhiên, nếu bạn xây dựng các đối tượng của mình, bộ đệm được kiểm tra (để sử dụng lại và bị lỗi), tuy nhiên, biểu thức thông thường bạn xây dựng không được thêm vào bộ đệm .
Bộ đệm này là một bộ đệm LRU tầm thường , nó được thực hiện bằng cách sử dụng một danh sách liên kết đôi đơn giản. Nếu bạn tình cờ tăng nó lên 5000 và sử dụng 5000 cuộc gọi khác nhau trên các trình trợ giúp tĩnh, mọi cấu trúc biểu thức chính quy sẽ thu thập dữ liệu 5000 mục để xem liệu nó đã được lưu trong bộ nhớ cache trước đó chưa. Có một khóa xung quanh kiểm tra, vì vậy kiểm tra có thể giảm song song và giới thiệu chặn luồng.
Con số được đặt khá thấp để bảo vệ bạn khỏi những trường hợp như thế này, mặc dù trong một số trường hợp, bạn có thể không có lựa chọn nào khác ngoài việc tăng nó.
Khuyến nghị mạnh mẽ của tôi sẽ không bao giờ chuyển RegexOptions.Compiled
tùy chọn cho một người trợ giúp tĩnh.
Ví dụ:
\\ WARNING: bad code
Regex.IsMatch("10000", @"\\d+", RegexOptions.Compiled)
Lý do là bạn đang mạo hiểm rất nhiều khi bỏ lỡ bộ đệm của bộ nhớ cache, điều này sẽ kích hoạt một trình biên dịch siêu đắt . Ngoài ra, bạn không biết các thư viện mà bạn phụ thuộc đang làm gì, do đó có rất ít khả năng kiểm soát hoặc dự đoán kích thước tốt nhất có thể của bộ đệm.
Xem thêm: Blog nhóm BCL
Lưu ý : điều này có liên quan đến .NET 2.0 và .NET 4.0. Có một số thay đổi dự kiến trong 4.5 có thể khiến điều này được sửa đổi.