2017-02-09 7 views
2

본인의 원래 문제를이 테스트에 단순화했습니다.Parallel 내부를 일반 for 루프보다 느리게 처리하십시오. 왜?

이 클래스 사용 :

public class Unmanaged : IDisposable 
{ 
    private IntPtr unmanagedResource; 

    public Unmanaged() 
    { 
     this.unmanagedResource = Marshal.AllocHGlobal(10 * 1024 * 1024); 
    } 
    public void DoSomethingWithThisClass() 
    { 
     Console.WriteLine($"{DateTime.Now} - {this.unmanagedResource.ToInt64()}"); 
    } 

    private bool disposedValue = false; // To detect redundant calls 

    protected virtual void Dispose(bool disposing) 
    { 
     if (!disposedValue) 
     { 
      Marshal.FreeHGlobal(unmanagedResource); 
      disposedValue = true; 
     } 
    } 

    ~Unmanaged() { 
     Dispose(false); 
    } 

    void IDisposable.Dispose() 
    { 
     Dispose(true); 
     GC.SuppressFinalize(this); 
    } 
} 

을 나는이 두 가지 검사를 :

public class UnitTest1 
{ 
    const int Runs = 100000; 

    [TestMethod] 
    public void UsingFor() 
    { 
     for (var i = 0; i <= Runs; i++) 
     { 
      using (var unman = new Unmanaged()) 
      { 
       unman.DoSomethingWithThisClass(); 
      } 
     } 
    } 

    [TestMethod] 
    public void UsingParallelFor() 
    { 
     Parallel.For(0, Runs, new ParallelOptions() { MaxDegreeOfParallelism = 10}, 
      index => { 
       using (var unman = new Unmanaged()) 
       { 
        unman.DoSomethingWithThisClass(); 
       } 
      }); 
    } 
} 

ParallelFor는 일반적으로 약 두 배 길이에 대한 정기적으로합니다. 프로파일 러에 따르면 ParallelFor의 경우 실행 시간의 62 % -65 %가 FreeHGlobal 내부에 소비됩니다. FreeHGlobal 내부에서는 일반용으로 52 % -53 % 만 사용됩니다.

현대의 RAM 시스템에서는이 점이 너무 큰 차이를 만들지 않을 것이라고 생각했습니다. 여러 프로세스에서 많은 양의 관리되지 않는 메모리를 처리 할 수있는 방법이 있습니까? 이 스레드를 멀티 스레드로 변경하는 방법이 있습니까?

각 프로세스에 사용 된 RAM을 폐기하지 말고 테스트하기 만하면 Parallel For는 두 배 빠르지 만 그 중 약 4-5 개만 열 수 있습니다 (대량입니다). 이미지 데이터)를 응용 프로그램이 충돌하기 전과 동시에 표시합니다 (예상치 못한 것처럼 RAM 예외가 있음).

왜 개별 개체에 대해 하나 이상의 Dispose 작업을 수행하면 작업 속도가 느려 집니까?

유일한 옵션 인 경우 단일 스레드로 남겨 둘 수 있지만 속도를 높이기 위해 노력하고 있습니다.

감사합니다.

+0

'관리되지 않는'클래스를'sealed' 클래스로 만들면'Dispose()'를 쓰는 것이'virtual Dispose (bool)'이 필요 없기 때문에 더 쉽습니다. –

+1

AllocHGlobal()에 내장 된 잠금 장치가있어 힙을 스레드로부터 안전하게 보호합니다. 그래서 당신이 측정하고있는 것은 자물쇠가 보관되는 시간입니다. 다른 스레드가 또한 메모리를 할당하는 동안 바쁜 동안에 불가피하게 더 오래 걸립니다. –

+0

많은 관리되지 않는 리소스 ('10 * 1024 * 1024')가 P/Invoke가 아닌 C++/CLI를 고려할 수도 있습니다. C++은 메모리 관리를위한 몇 가지 도구를 제공 할 수도 있습니다. –

답변

0

FreeHGlobal은 거의 확실하게 차단합니다. 즉, 한 번에 하나의 스레드 만 실행할 수 있습니다. 그들은 줄을 서서 기다린다. 오버 헤드가 있으므로 속도가 느립니다.

하나의 큰 관리되지 않는 메모리 블록을 만들고 잠금 해제 할당자를 실행하면 더 빨리 수행 할 수 있습니다.

+1

내부 잠금 장치가 있다는 것을 깨닫지 못했습니다. 그것은 내가이 문제에 다르게 접근해야만한다. 저는이 중 약 4 개를 RAM에 한 번에 넣을 수있을 정도로 큰 메모리 블록을 만드는 것에 대해 생각합니다. 그런 다음 대기열에서 처리해야하는 작업을 설정하고 컨트롤러가 RAM 작업 영역의 다른 청크로 프로세스를 분할하도록 할 수 있습니다. 큐에서 다른 작업을 시작하기 전에 이전 프로세스에서 사용 된 섹션을 지울 수 있습니다. 코딩 오버 헤드가 조금 더 많지만 이러한 작업을 처리하는 데 필요한 시간이 절약됩니다. –

+0

당신은 분명히 내 경험에 맞는 경로를 따라 가고 있습니다. 잠그지 않고도 할 수있는 방법에 대해 생각해보십시오. 또는 교대로 멋진 메모리 풀을 살펴보십시오. – hoodaticus