2017-02-09 9 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 обычно занимает примерно в два раза до тех пор, как регулярная для. Согласно профилировщику, 62% -65% времени выполнения тратится в FreeHGlobal для ParallelFor. Только 52% -53% расходуется внутри FreeHGlobal для регулярного использования.

Я предполагал, что с современными системами оперативной памяти это не будет иметь особого значения. Есть ли способ обрабатывать большие куски не управляемой памяти в нескольких процессах? Есть ли способ изменить это, чтобы он был многопоточным?

Если я не утилизирую ОЗУ, используемую в каждом процессе (плохая идея, но только для проверки), Parallel For в два раза быстрее, но тогда я могу открыть только 4-5 из них (они большие суммы данных изображения) одновременно до того, как приложение выйдет из строя (с учетом, как вы догадались, исключения из ОЗУ).

Почему несколько объектов Dispose на отдельных объектах замедляют работу?

Я могу оставить их однопоточными, если это единственный вариант, но я надеялся ускорить это.

спасибо.

+0

Если вы сделаете 'неуправляемый'' закрытым' класс, написание 'Dispose()' проще, так как нет необходимости в 'virtual Dispose (bool)'. –

+1

В AllocHGlobal() встроен замок, он сохраняет защиту от кучи. Так что вы измеряете, сколько времени удерживается замок, это неизбежно занимает больше времени, в то время как другой поток также занят распределением памяти. –

+0

Если вы имеете дело с множеством неуправляемых ресурсов ('10 * 1024 * 1024' может быть прокси для этого), вы можете рассмотреть C++/CLI, а не P/Invoke; C++ также может предоставить вам еще несколько инструментов для управления памятью. –

ответ

0

FreeHGlobal почти наверняка блокирует. Это означает, что только один поток в вашем процессе может запускать его за раз. Они встают в очередь и ждут. Для этого есть накладные расходы, поэтому он медленнее.

Вы можете сделать это быстрее, создав единый большой блок неуправляемой памяти и включив в него блокировочный распределитель.

:
+1

Я не понимал, что есть внутренний замок. Это заставляет меня подойти к проблеме по-другому. Мне нравится ваша идея создать блок памяти, достаточно большой, чтобы разместить около четырех из них в ОЗУ сразу. Затем я могу настроить задания, которые мне нужно обрабатывать в очереди, и иметь контроллер, разделяющий процессы на разные части рабочей области ОЗУ. Я могу очистить раздел, используемый в предыдущем процессе, прежде чем запускать очередную задачу из очереди. Это немного больше накладных расходов на кодирование, но это должно сэкономить часы на объем обработки, который необходимо выполнить с ними. –

+0

Вы, безусловно, следуете правильному пути в моем опыте. Попытайтесь подумать о том, как вы могли бы сделать это, не блокируя, или поочередно, изучать чудесные пулы памяти. – hoodaticus