programing

컴파일러/J에 제공할 수 있는 최적화 힌트는 무엇입니까?IT?

newstyles 2023. 5. 29. 09:57

컴파일러/J에 제공할 수 있는 최적화 힌트는 무엇입니까?IT?

저는 이미 프로파일링을 마쳤으며, 이제 핫스팟에서 가능한 모든 성능을 짜내고자 합니다.

[MethodImplOptions]에 대해 알고 있습니다.[Agressive Inline]프로파일 최적화 클래스를 선택합니다.다른 사람들도 있나요?


[편집] 나는 방금 발견했습니다. [TargetedPatchingOptOut]도 마찬가지입니다. 신경쓰지 마세요, 분명히 그것은 필요하지 않습니다.

네, 더 많은 속임수가 있습니다 :-)

저는 실제로 C# 코드 최적화에 대해 꽤 많은 연구를 해왔습니다.지금까지 가장 중요한 결과는 다음과 같습니다.

  1. 직접 전달되는 Func 및 Action은 종종 JIT'ter에 의해 인라인화됩니다.변수로 저장하면 안 됩니다. 변수는 대리인으로 호출되기 때문입니다.자세한 내용은 이 게시물을 참조하십시오.
  2. 과부하 조심하세요.를 사용하지 IEquatable<T>일반적으로 잘못된 계획입니다. 따라서 f.ex. 해시를 사용하는 경우 올바른 오버로드와 인터페이스를 구현해야 합니다. 이는 엄청난 성능을 안전하게 보호해 줄 것이기 때문입니다.
  3. 다른 클래스에서 호출된 제네릭은 인라인이 적용되지 않습니다.그 이유는 여기에 설명된 "마법" 때문입니다.
  4. 데이터 구조를 사용하는 경우 어레이를 대신 사용해 보십시오. :-) 정말로, 이런 것들은 제가 생각하는 것과 비교해 볼 때... 글쎄요, 거의 모든 것에 비해 매우 빠릅니다.저는 제 해시 테이블을 사용하고 목록 대신 어레이를 사용하여 많은 것을 최적화했습니다.
  5. 대부분의 경우, 테이블 룩업은 사물을 계산하거나 vtable 룩업, 스위치, 다중 if 문, 심지어 계산과 같은 구성을 사용하는 것보다 더 빠릅니다.분기가 있는 경우에도 좋은 방법입니다. 분기 예측이 실패하면 큰 문제가 될 수 있습니다. 게시물도 참조하십시오. 이것은 제가 C#에서 꽤 많이 사용하는 속임수이며 많은 경우에 잘 작동합니다.아, 그리고 룩업 테이블은 당연히 배열입니다.
  6. (작은) 클래스 구조를 만드는 실험을 합니다.값 유형의 특성 때문에 일부 최적화는 구조체와 클래스에 대해 다릅니다.예를 들어, 메소드 호출은 컴파일러가 호출할 메소드를 정확히 알고 있기 때문에 더 간단합니다.또한 구조체 배열은 일반적으로 클래스 배열보다 빠릅니다. 이는 배열 작업당 1개의 메모리 작업이 덜 필요하기 때문입니다.
  7. 다차원 배열을 사용하지 마십시오.내가 선호하는 동안Foo[],심지어.Foo[][]일반적으로 보다 빠릅니다.Foo[,].
  8. 데이터를 복사하는 경우 버퍼를 선택합니다.배열을 통한 복사 차단.요일을 복사합니다.또한 문자열에 대해서도 주의하십시오. 문자열 작업은 성능 저하 요인이 될 수 있습니다.

또한 "인텔 펜티엄 프로세서를 위한 최적화"라는 가이드가 있으며 많은 트릭(분할 대신 이동 또는 곱셈)이 있습니다.컴파일러가 요즘은 노력을 많이 하지만, 가끔은 도움이 되기도 합니다.

물론 이는 단순한 최적화에 불과합니다. 가장 큰 성능 향상은 대개 알고리즘 및/또는 데이터 구조를 변경한 결과입니다.어떤 옵션을 사용할 수 있는지 확인하고 .NET 프레임워크로 너무 제한하지 마십시오.또한 저는 자연스럽게 .NET 구현을 불신하는 경향이 있습니다. 제가 직접 디컴파일된 코드를 확인하기 전까지는...훨씬 더 빨리 구현될 수 있는 것들이 많이 있습니다(대부분의 경우 좋은 이유로).

HTH


알렉스가 나에게 지적한 것은Array.Copy어떤 사람들에 따르면 실제로 더 빠르다고 합니다.그리고 저는 지난 몇 년간 무엇이 바뀌었는지 정말로 모르기 때문에, 새로운 벤치마크를 만들어 테스트하는 것이 유일한 적절한 행동 방법이라고 생각했습니다.

결과에만 관심이 있다면 내려가세요.대부분의 경우 전화는 다음과 같습니다.Buffer.BlockCopy분명히 능가하는.Array.Copy.NET 4.5.2에서 16GB 메모리(사용 가능한 메모리가 10GB 이상임)가 있는 Intel Skylake에서 테스트되었습니다.

코드:

static void TestNonOverlapped1(int K)
{
    long total = 1000000000;
    long iter = total / K;
    byte[] tmp = new byte[K];
    byte[] tmp2 = new byte[K];
    for (long i = 0; i < iter; ++i)
    {
        Array.Copy(tmp, tmp2, K);
    }
}

static void TestNonOverlapped2(int K)
{
    long total = 1000000000;
    long iter = total / K;
    byte[] tmp = new byte[K];
    byte[] tmp2 = new byte[K];
    for (long i = 0; i < iter; ++i)
    {
        Buffer.BlockCopy(tmp, 0, tmp2, 0, K);
    }
}

static void TestOverlapped1(int K)
{
    long total = 1000000000;
    long iter = total / K;
    byte[] tmp = new byte[K + 16];
    for (long i = 0; i < iter; ++i)
    {
        Array.Copy(tmp, 0, tmp, 16, K);
    }
}

static void TestOverlapped2(int K)
{
    long total = 1000000000;
    long iter = total / K;
    byte[] tmp = new byte[K + 16];
    for (long i = 0; i < iter; ++i)
    {
        Buffer.BlockCopy(tmp, 0, tmp, 16, K);
    }
}

static void Main(string[] args)
{
    for (int i = 0; i < 10; ++i)
    {
        int N = 16 << i;

        Console.WriteLine("Block size: {0} bytes", N);

        Stopwatch sw = Stopwatch.StartNew();

        {
            sw.Restart();
            TestNonOverlapped1(N);

            Console.WriteLine("Non-overlapped Array.Copy: {0:0.00} ms", sw.Elapsed.TotalMilliseconds);
            GC.Collect(GC.MaxGeneration);
            GC.WaitForFullGCComplete();
        }

        {
            sw.Restart();
            TestNonOverlapped2(N);

            Console.WriteLine("Non-overlapped Buffer.BlockCopy: {0:0.00} ms", sw.Elapsed.TotalMilliseconds);
            GC.Collect(GC.MaxGeneration);
            GC.WaitForFullGCComplete();
        }

        {
            sw.Restart();
            TestOverlapped1(N);

            Console.WriteLine("Overlapped Array.Copy: {0:0.00} ms", sw.Elapsed.TotalMilliseconds);
            GC.Collect(GC.MaxGeneration);
            GC.WaitForFullGCComplete();
        }

        {
            sw.Restart();
            TestOverlapped2(N);

            Console.WriteLine("Overlapped Buffer.BlockCopy: {0:0.00} ms", sw.Elapsed.TotalMilliseconds);
            GC.Collect(GC.MaxGeneration);
            GC.WaitForFullGCComplete();
        }

        Console.WriteLine("-------------------------");
    }

    Console.ReadLine();
}

x86 JIT에 대한 결과:

Block size: 16 bytes
Non-overlapped Array.Copy: 4267.52 ms
Non-overlapped Buffer.BlockCopy: 2887.05 ms
Overlapped Array.Copy: 3305.01 ms
Overlapped Buffer.BlockCopy: 2670.18 ms
-------------------------
Block size: 32 bytes
Non-overlapped Array.Copy: 1327.55 ms
Non-overlapped Buffer.BlockCopy: 763.89 ms
Overlapped Array.Copy: 2334.91 ms
Overlapped Buffer.BlockCopy: 2158.49 ms
-------------------------
Block size: 64 bytes
Non-overlapped Array.Copy: 705.76 ms
Non-overlapped Buffer.BlockCopy: 390.63 ms
Overlapped Array.Copy: 1303.00 ms
Overlapped Buffer.BlockCopy: 1103.89 ms
-------------------------
Block size: 128 bytes
Non-overlapped Array.Copy: 361.18 ms
Non-overlapped Buffer.BlockCopy: 219.77 ms
Overlapped Array.Copy: 620.21 ms
Overlapped Buffer.BlockCopy: 577.20 ms
-------------------------
Block size: 256 bytes
Non-overlapped Array.Copy: 192.92 ms
Non-overlapped Buffer.BlockCopy: 108.71 ms
Overlapped Array.Copy: 347.63 ms
Overlapped Buffer.BlockCopy: 353.40 ms
-------------------------
Block size: 512 bytes
Non-overlapped Array.Copy: 104.69 ms
Non-overlapped Buffer.BlockCopy: 65.65 ms
Overlapped Array.Copy: 211.77 ms
Overlapped Buffer.BlockCopy: 202.94 ms
-------------------------
Block size: 1024 bytes
Non-overlapped Array.Copy: 52.93 ms
Non-overlapped Buffer.BlockCopy: 38.84 ms
Overlapped Array.Copy: 144.39 ms
Overlapped Buffer.BlockCopy: 154.09 ms
-------------------------
Block size: 2048 bytes
Non-overlapped Array.Copy: 45.64 ms
Non-overlapped Buffer.BlockCopy: 30.11 ms
Overlapped Array.Copy: 118.33 ms
Overlapped Buffer.BlockCopy: 109.16 ms
-------------------------
Block size: 4096 bytes
Non-overlapped Array.Copy: 30.93 ms
Non-overlapped Buffer.BlockCopy: 30.72 ms
Overlapped Array.Copy: 119.73 ms
Overlapped Buffer.BlockCopy: 104.66 ms
-------------------------
Block size: 8192 bytes
Non-overlapped Array.Copy: 30.37 ms
Non-overlapped Buffer.BlockCopy: 26.63 ms
Overlapped Array.Copy: 90.46 ms
Overlapped Buffer.BlockCopy: 87.40 ms
-------------------------

x64 JIT에 대한 결과:

Block size: 16 bytes
Non-overlapped Array.Copy: 1252.71 ms
Non-overlapped Buffer.BlockCopy: 694.34 ms
Overlapped Array.Copy: 701.27 ms
Overlapped Buffer.BlockCopy: 573.34 ms
-------------------------
Block size: 32 bytes
Non-overlapped Array.Copy: 995.47 ms
Non-overlapped Buffer.BlockCopy: 654.70 ms
Overlapped Array.Copy: 398.48 ms
Overlapped Buffer.BlockCopy: 336.86 ms
-------------------------
Block size: 64 bytes
Non-overlapped Array.Copy: 498.86 ms
Non-overlapped Buffer.BlockCopy: 329.15 ms
Overlapped Array.Copy: 218.43 ms
Overlapped Buffer.BlockCopy: 179.95 ms
-------------------------
Block size: 128 bytes
Non-overlapped Array.Copy: 263.00 ms
Non-overlapped Buffer.BlockCopy: 196.71 ms
Overlapped Array.Copy: 137.21 ms
Overlapped Buffer.BlockCopy: 107.02 ms
-------------------------
Block size: 256 bytes
Non-overlapped Array.Copy: 144.31 ms
Non-overlapped Buffer.BlockCopy: 101.23 ms
Overlapped Array.Copy: 85.49 ms
Overlapped Buffer.BlockCopy: 69.30 ms
-------------------------
Block size: 512 bytes
Non-overlapped Array.Copy: 76.76 ms
Non-overlapped Buffer.BlockCopy: 55.31 ms
Overlapped Array.Copy: 61.99 ms
Overlapped Buffer.BlockCopy: 54.06 ms
-------------------------
Block size: 1024 bytes
Non-overlapped Array.Copy: 44.01 ms
Non-overlapped Buffer.BlockCopy: 33.30 ms
Overlapped Array.Copy: 53.13 ms
Overlapped Buffer.BlockCopy: 51.36 ms
-------------------------
Block size: 2048 bytes
Non-overlapped Array.Copy: 27.05 ms
Non-overlapped Buffer.BlockCopy: 25.57 ms
Overlapped Array.Copy: 46.86 ms
Overlapped Buffer.BlockCopy: 47.83 ms
-------------------------
Block size: 4096 bytes
Non-overlapped Array.Copy: 29.11 ms
Non-overlapped Buffer.BlockCopy: 25.12 ms
Overlapped Array.Copy: 45.05 ms
Overlapped Buffer.BlockCopy: 47.84 ms
-------------------------
Block size: 8192 bytes
Non-overlapped Array.Copy: 24.95 ms
Non-overlapped Buffer.BlockCopy: 21.52 ms
Overlapped Array.Copy: 43.81 ms
Overlapped Buffer.BlockCopy: 43.22 ms
-------------------------

.NET 4.5에 추가된 옵션을 모두 사용하여 지트 코드에 직접 영향을 미칩니다.다음 단계는 생성된 기계 코드를 검토하여 명백한 비효율성을 발견하는 것입니다.먼저 디버거를 사용하여 최적화 도구를 사용하지 않도록 설정합니다.도구 + 옵션, 디버깅, 일반, "모듈 로드 시 JIT 최적화 억제" 옵션을 선택 취소합니다.핫 코드의 중단점인 디버그 + 분해를 설정하여 확인합니다.

고려할 사항이 많지 않습니다. 일반적으로 지터 최적화 도구는 탁월한 작업을 수행합니다.한 가지 확인해야 할 것은 배열 경계 검사를 제거하려는 시도가 실패했다는 것입니다. 고정 키워드는 이에 대한 안전하지 않은 해결 방법입니다.코너 케이스는 메소드와 지터가 CPU 레지스터를 효과적으로 사용하지 않는 인라인화 시도가 실패한 경우로 x86 지터의 문제이며 MethodImpLoptions로 해결되었습니다.인라이닝 없음.옵티마이저는 불변 코드를 루프 밖으로 끌어올리는 데 매우 효율적이지는 않지만, C# 코드를 최적화할 방법을 찾을 때 거의 항상 먼저 고려해야 할 사항입니다.

알고 싶은 가장 중요한 것은 여러분이 언제 일을 끝마쳤는지이며, 더 빨리 일을 진행하기를 바랄 수 없다는 것입니다.사과와 오렌지를 비교하고 C++/CLI를 사용하여 핫 코드를 네이티브 코드로 작성해야 진정으로 도달할 수 있습니다.이 코드가 관리되지 않는 #pragma로 컴파일되어 있는지 확인하여 최적화 도구를 최대한 활용할 수 있도록 하십시오.관리 코드에서 네이티브 코드 실행으로 전환하는 데 비용이 소요되므로 네이티브 코드의 실행 시간이 충분한지 확인하십시오.그렇지 않으면 이것은 반드시 쉬운 일이 아니며 당신은 확실히 성공에 대한 보장을 받지 못할 것입니다.비록 여러분이 끝났다는 것을 아는 것은 여러분이 죽은 골목으로 비틀거리며 들어가는 많은 시간을 절약할 수 있습니다.

언급URL : https://stackoverflow.com/questions/16306206/what-optimization-hints-can-i-give-to-the-compiler-jit