RegexOptions.Compiled는 어떻게 작동합니까?


169

정규식을 컴파일 할 것으로 표시하면 장면 뒤에서 무슨 일이 일어나고 있습니까? 이것은 캐시 된 정규 표현식과 어떻게 비교 / 차이됩니까?

이 정보를 사용하여 성능 향상에 비해 계산 비용이 무시할 수있는 시점을 어떻게 결정합니까?


Regex 모범 사례에 대한 유용한 리소스 : docs.microsoft.com/en-us/dotnet/standard/base-types/…
CAD

답변:


302

RegexOptions.CompiledLCG (Lightweight Code Generation)를 사용하여 정규식 표현식을 IL로 컴파일하도록 정규식 엔진에 지시합니다 . 이 편집은 객체를 구성하는 동안 발생하며 속도가 크게 느려집니다. 결과적으로 정규 표현식을 사용한 일치가 더 빠릅니다.

이 플래그를 지정하지 않으면 정규식은 "통역 됨"으로 간주됩니다.

이 예제를 보자 :

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]);
        });

    }
}

3 가지 정규 표현식에 대해 4 가지 테스트를 수행합니다. 먼저 한 번의 오프 매치 (컴파일 대 비 컴파일)를 테스트합니다 . 둘째, 동일한 정규 표현식을 재사용하는 반복 일치를 테스트합니다.

내 컴퓨터의 결과 (릴리스로 컴파일되고 디버거가 연결되지 않음)

단일 일치 1000 회

타입 | 플랫폼 | 사소한 번호 | 간단한 이메일 확인 | 내선 이메일 확인
-------------------------------------------------- ----------------------------
해석 | x86 | 4ms | 26ms | 31ms
해석 | x64 | 5ms | 29ms | 35ms
컴파일 | x86 | 913ms | 3775ms | 4487ms
컴파일 | x64 | 3300ms | 21985 ms | 22793ms

1,000,000 개의 일치 항목-Regex 객체 재사용

타입 | 플랫폼 | 사소한 번호 | 간단한 이메일 확인 | 내선 이메일 확인
-------------------------------------------------- ----------------------------
해석 | x86 | 422ms | 461 ms | 2122ms
해석 | x64 | 436ms | 463 ms | 2167ms
컴파일 | x86 | 279ms | 166ms | 1268ms
컴파일 | x64 | 281ms | 176ms | 1180ms

이 결과 는 객체 를 재사용하는 경우 컴파일 된 정규식이 최대 60 % 더 빠를 수 있음을 보여줍니다 Regex. 그러나 어떤 경우에는 구성이 3 배 이상 느릴 수 있습니다 .

또한 정규식을 컴파일 할 때 x64 버전 의 .NET이 5-6 배 느려질 수 있음을 보여줍니다 .


다음 과 같은 경우 컴파일 된 버전사용하는 것이 좋습니다.

  1. 객체 초기화 비용에 신경 쓰지 않고 추가 성능 향상이 필요합니다. (여기서 우리는 밀리 초의 분수를 말하고 있습니다)
  2. 초기화 비용에 대해서는 약간의 관심이 있지만 Regex 객체를 너무 많이 재사용하여 응용 프로그램 수명주기 동안 보상합니다.

작품의 스패너, 정규식 캐시

정규 표현식 엔진에는 Regex클래스 의 정적 메소드를 사용하여 테스트 한 마지막 15 개의 정규 표현식을 보유하는 LRU 캐시가 포함되어 있습니다 .

예를 들어 Regex.Replace, Regex.Match등 모든 사용 정규식 캐시.

캐시 크기를 설정하여 늘릴 수 있습니다 Regex.CacheSize. 응용 프로그램 수명주기 동안 언제든지 크기 변경을 허용합니다.

새로운 정규 표현식은 Regex 클래스 의 정적 도우미 에 의해서만 캐시 됩니다 . 그러나 객체를 생성하면 캐시가 점검되고 (재사용 및 범프) 생성하는 정규식 은 캐시에 추가되지 않습니다 .

이 캐시는 간단한 LRU 캐시이며 간단한 이중 링크 목록을 사용하여 구현됩니다. 5000으로 늘리고 정적 도우미에서 5000 개의 다른 호출을 사용하는 경우 모든 정규식 구성은 5000 개의 항목을 크롤링하여 이전에 캐시되었는지 확인합니다. 검사 주위 에 잠금 장치 가 있으므로 검사를 통해 병렬 처리가 줄어들고 스레드 차단이 발생할 수 있습니다.

숫자는 이와 같은 경우로부터 자신을 보호하기 위해 상당히 낮게 설정되어 있지만 경우에 따라 숫자를 늘리는 것 외에는 선택의 여지가 없습니다.

강력한 권장 사항 은 옵션을 정적 도우미에게 전달 하지 않습니다RegexOptions.Compiled .

예를 들면 다음과 같습니다.

\\ WARNING: bad code
Regex.IsMatch("10000", @"\\d+", RegexOptions.Compiled)

그 이유는 LRU 캐시를 놓칠 위험이 너무 커서 컴파일 비용매우 많이 듭니다 . 또한 의존하는 라이브러리가 무엇을하는지 모를 수 있으므로 캐시의 가능한 최대 크기 를 제어하거나 예측하는 능력이 거의 없습니다 .

참조 : BCL 팀 블로그


참고 : 이것은 .NET 2.0 및 .NET 4.0과 관련이 있습니다. 4.5에는 일부 변경 사항이있을 수 있으며 이로 인해 수정 될 수 있습니다.


11
좋은 대답입니다. 내 목적을 위해 Compiled웹 사이트 코드에서 종종 정적 (응용 프로그램 전체) Regex객체를 저장하는 곳에서 사용 합니다. 따라서 RegexIIS는 응용 프로그램을 시작할 때 한 번만 구성한 다음 수천 번 재사용해야합니다. 응용 프로그램이 자주 다시 시작되지 않는 한 잘 작동합니다.
Steve Wortham

W00! 이 정보는 8-13 시간에서 ~ 30 분으로 프로세스 속도를 높이는 데 도움이되었습니다. 감사합니다!
Robert Christ

3
훌륭한 답변 Sam, 버전> 4.5에서 변경된 사항을 업데이트 할 수 있습니까? (나는 ... 당신이 당신의 스택 한참을 변경 알고)
gdoron 모니카 지원하고있다

@gdoronissupportingMonica NET 5.0에서 일부 Regex 성능이 향상되었습니다. 나는 이것에 대한 블로그 게시물을 보았다. 여기에서
kapozade

42

BCL 팀 블로그의이 항목은 " 정규 표현식 성능 "에 대한 개요를 제공합니다 .

간단히 말해 세 가지 유형의 정규 표현식이 있습니다 (각각 이전 유형보다 빠르게 실행 됨).

  1. 해석

    빠르게 생성하고 실행 속도가 느립니다.

  2. 컴파일 (당신이 묻는 것)

    실행 속도가 느리고 실행 속도가 빠릅니다 (루프 실행에 적합)

  3. 사전 컴파일

    앱 컴파일 타임에 생성 (런타임 생성 페널티 없음), 빠른 실행

따라서 정규 표현식을 한 번만 실행하거나 앱의 성능이 중요하지 않은 섹션 (예 : 사용자 입력 유효성 검사)에서 실행하려면 옵션 1을 사용하는 것이 좋습니다.

루프에서 정규식을 실행하려면 (예 : 파일의 라인 별 구문 분석) 옵션 2를 사용해야합니다.

앱에 변경되지 않고 많이 사용되는 정규 표현식이 많은 경우 옵션 3을 사용할 수 있습니다.


1
사용자 지정 roslyn을 통해 3 번을 쉽게 만들 수 있습니다CompileModule . 젠장, 나는 새로운 플랫폼을 더 깊이 들여다 볼 필요가있다.
Christian Gollhardt

9

.NET 2.0 이후 정규 표현식의 성능은 컴파일되지 않은 정규 표현식의 MRU 캐시로 향상되었습니다. 정규식 라이브러리 코드는 더 이상 매번 컴파일되지 않은 동일한 정규식을 더 이상 해석하지 않습니다.

따라서 잠재적으로 더 큰 성과가 페널티 컴파일하고 즉석에서 정규 표현식으로. 로드 시간이 느려지는 것 외에도 시스템은 더 많은 메모리를 사용하여 정규식을 opcode로 컴파일합니다.

기본적으로 현재 조언은 정규식을 컴파일하지 않거나 별도의 어셈블리로 미리 컴파일하는 것입니다.

Ref : BCL 팀 블로그 정규 표현식 성능 [David Gutierrez]



0

아래 코드가 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)

답변 주셔서 감사하지만 코드는 Python 언어입니다. 문제는 Microsoft .NET 프레임 워크 RegexOptions.Compiled 옵션에 관한 것입니다. 당신은 볼 수 있습니다 [ .NET ] 태그 질문 아래에 첨부합니다.
Stomy

오 예! 감사합니다 Stomy
Daniel Muthupandi
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.