RegexOptions.Compiled hoạt động như thế nào?


168

Điều gì đang xảy ra đằng sau hậu trường khi bạn đánh dấu một biểu thức chính quy là một biểu thức sẽ được biên dịch? Làm thế nào để so sánh này / khác với một biểu thức chính quy được lưu trữ?

Sử dụng thông tin này, làm thế nào để bạn xác định khi nào chi phí tính toán không đáng kể so với mức tăng hiệu suất?


tài nguyên tốt về các thực tiễn tốt nhất của Regex: docs.microsoft.com/en-us/dotnet/stiteria/base-types/iêu
CAD bloke

Câu trả lời:


302

RegexOptions.Compiledhướ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

  1. 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)
  2. 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 Regexlớp.

Ví dụ : Regex.Replace, Regex.Matchvv .. 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.Compiledtù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.


11
Câu trả lời chính xác. Đối với mục đích riêng của tôi, tôi thường sử dụng Compiledmã trang web nơi tôi thực sự đang lưu trữ một Regexđối tượng tĩnh (toàn ứng dụng) . Vì vậy, Regexchỉ được xây dựng một lần khi IIS khởi động ứng dụng và sau đó được sử dụng lại hàng ngàn lần. Điều này hoạt động tốt miễn là ứng dụng không khởi động lại thường xuyên.
Steve Wortham

W00! Thông tin này giúp tôi tăng tốc quá trình của mình từ 8-13 giờ, lên ~ 30 phút. Cảm ơn bạn!
Robert Christ

3
Sam trả lời tuyệt vời, bạn có thể cập nhật về những gì đã thay đổi trong phiên bản> 4.5 không? (Tôi biết bạn đã thay đổi ngăn xếp của mình một thời gian trước ...)
gdoron đang hỗ trợ Monica

@gdoronissupportingMonica Đã có một số cải tiến hiệu suất Regex trên NET 5.0. Tôi thấy một bài viết trên blog cho việc này. Bạn có thể kiểm tra nó ở đây
kapozade

42

Mục này trong Blog Nhóm BCL cung cấp một cái nhìn tổng quan thú vị: " Hiệu suất biểu hiện thường xuyên ".

Nói tóm lại, có ba loại regex (mỗi loại thực thi nhanh hơn loại trước):

  1. giải thích

    nhanh để tạo khi đang bay, chậm để thực hiện

  2. biên dịch (một trong những bạn dường như hỏi về)

    chậm hơn để tạo khi đang bay, nhanh để thực hiện (tốt cho việc thực hiện trong các vòng lặp)

  3. biên soạn trước

    tạo tại thời điểm biên dịch ứng dụng của bạn (không có hình phạt tạo thời gian chạy), thực hiện nhanh

Vì vậy, nếu bạn dự định chỉ thực hiện regex một lần hoặc trong phần không quan trọng về hiệu suất của ứng dụng của bạn (tức là xác thực nhập liệu của người dùng), bạn vẫn ổn với tùy chọn 1.

Nếu bạn có ý định chạy regex trong một vòng lặp (tức là phân tích cú pháp từng dòng của tệp), bạn nên đi với tùy chọn 2.

Nếu bạn có nhiều biểu thức sẽ không bao giờ thay đổi cho ứng dụng của bạn và được sử dụng mạnh mẽ, bạn có thể đi với tùy chọn 3.



9

Cần lưu ý rằng hiệu suất của các biểu thức chính quy kể từ .NET 2.0 đã được cải thiện với bộ đệm MRU của các biểu thức thông thường chưa được biên dịch. Mã thư viện Regex không còn diễn giải lại cùng một biểu thức chính quy chưa được biên dịch mỗi lần.

Vì vậy, có khả năng một hình phạt hiệu suất lớn hơn với một biểu thức thường xuyên được biên soạn và đang hoạt động. Ngoài thời gian tải chậm hơn, hệ thống cũng sử dụng nhiều bộ nhớ hơn để biên dịch biểu thức chính quy thành opcodes.

Về cơ bản, lời khuyên hiện tại là không biên dịch một biểu thức chính quy, hoặc biên dịch chúng trước cho một hội đồng riêng biệt.

Tham khảo: Blog nhóm BCL Hiệu suất biểu hiện thường xuyên [David Gutierrez]



0

Tôi hy vọng đoạn mã dưới đây sẽ giúp bạn hiểu khái niệm về hàm re.compile

import re

x="""101 COM    Computers
205 MAT   Mathematics
189 ENG   English
222 SCI Science
333 TA  Tamil
5555 KA  Kannada
6666  TL  Telugu
777777 FR French
"""

#compile reg expression / successfully compiled regex can be used in any regex 
#functions    
find_subject_code=re.compile("\d+",re.M)
#using compiled regex in regex function way - 1
out=find_subject_code.findall(x)
print(out)
#using compiled regex in regex function way - 2
out=re.findall(find_numbers,x)
print(out)

#few more eg:
#find subject name
find_subjectnames=re.compile("(\w+$)",re.M) 
out=find_subjectnames.findall(x)
print(out)


#find subject SHORT name
find_subject_short_names=re.compile("[A-Z]{2,3}",re.M) 
out=find_subject_short_names.findall(x)
print(out)

Cảm ơn câu trả lời của bạn nhưng mã của bạn bằng ngôn ngữ Python . Câu hỏi là về tùy chọn RegexOptions.Compiled của Microsoft .NET framework . Bạn có thể thấy thẻ [ .net ] được đính kèm bên dưới câu hỏi.
stomy

ồ vâng Cảm ơn stomy
Daniel Muthupandi
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.