Скорость сортировки .NET - PullRequest
       13

Скорость сортировки .NET

2 голосов
/ 19 января 2009

У меня есть подпись метода C ++, которая выглядит следующим образом:

    static extern void ImageProcessing(
        [MarshalAs(UnmanagedType.LPArray)]ushort[] inImage,
        [MarshalAs(UnmanagedType.LPArray)]ushort[] outImage,
        int inYSize, int inXSize);

Я обернул функцию в методы синхронизации, как внутренние, так и внешние. Внутренне, функция работает на 0,24 с. Внешне функция работает за 2,8 с, или примерно в 12 раз медленнее. В чем дело? Маршаллинг замедляет меня так сильно? Если это так, как я могу обойти это? Должен ли я перейти на небезопасный код и использовать указатели или что-то? Я в некотором замешательстве относительно того, откуда взялись дополнительные затраты времени.

Ответы [ 3 ]

3 голосов
/ 19 января 2009

Взгляните на эту статью . Хотя основное внимание уделяется Compact Framework, общие принципы применимы и к рабочему столу. Соответствующая цитата из раздела анализа выглядит следующим образом:

Управляемый вызов напрямую не вызывает собственный метод. Вместо этого он вызывает метод-заглушку JITted, который должен выполнять некоторые служебные процедуры, такие как вызовы, чтобы определить статус выгрузки GC (чтобы определить, ожидает ли GC ожидание, и нам нужно подождать). Также возможно, что некоторый код маршаллинга также получит JITted в заглушку. Все это требует времени.

Редактировать : Также стоит прочитать эту статью в блоге о совершенствовании кода JITted - опять же, специфично для CF, но все еще актуально. Существует также статья, в которой рассматривается глубина стека вызовов и ее влияние на производительность , хотя, вероятно, эта статья специфична для CF (не тестировалась на настольном компьютере).

2 голосов
/ 20 января 2009

Вы пробовали переключить два параметра массива на IntPtr? PInvoke работает максимально быстро, когда все типы в маршалинговой сигнатуре являются легковесными. Это означает, что Pinvoke сводится к простой memcpy для получения данных взад и вперед.

В моей команде мы нашли самый эффективный способ управления нашим слоем PInvoke -

  1. Гарантия того, что все, что есть у Маршалла, является легкомысленным
  2. Платите цену маршалам вручную, таким как массивы, манипулируя классом IntPtr по мере необходимости. Это очень тривиально, так как у нас есть много методов / классов-оболочек.

Как и в случае любого ответа «это будет быстрее», вам нужно будет профилировать это свою собственную кодовую базу. Мы пришли к этому решению только после того, как несколько методов были рассмотрены и представлены.

0 голосов
/ 20 января 2009

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

Временной код, который я использовал, был таким:

Ipp32s timer;
ippGetCpuFreqMhz(&timer);
Ipp64u globalStart = ippGetCpuClocks();
globalStart = ippGetCpuClocks() *2 - globalStart; //use this method to get rid of the overhead of getting clock ticks

      //do some stuff

Ipp64u globalEnd = ippGetCpuClocks(); 
globalEnd = ippGetCpuClocks() *2 - globalEnd;
std::cout << "total runtime: " << ((Ipp64f)globalEnd - (Ipp64f)globalStart)/((Ipp64f)timer *1000000.0f) << " seconds" << std::endl;

Этот код специфичен для компилятора Intel и предназначен для обеспечения чрезвычайно точных измерений времени. К сожалению, такая предельная точность означает стоимость примерно 2,5 секунды на цикл. Удаление временного кода сняло это временное ограничение.

Тем не менее, кажется, что время выполнения все еще задерживается - код сообщит о 0,24 с при включенном временном коде, и теперь сообщает о времени примерно 0,35 с, что означает, что скорость составляет около 50%.

Изменение кода на это:

  static extern void ImageProcessing(
     IntPtr inImage, //[MarshalAs(UnmanagedType.LPArray)]ushort[] inImage,
     IntPtr outImage, //[MarshalAs(UnmanagedType.LPArray)]ushort[] outImage,
     int inYSize, int inXSize);

и называется как:

        unsafe {
            fixed (ushort* inImagePtr = theInputImage.DataArray){
                fixed (ushort* outImagePtr = theResult){
                    ImageProcessing((IntPtr)inImagePtr,//theInputImage.DataArray,
                        (IntPtr)outImagePtr,//theResult,
                        ysize,
                        xsize);
                }
            }
        }

уменьшает время выполнения до 0,3 с (в среднем три запуска). Все еще слишком медленный для моих вкусов, но повышение скорости в 10 раз, безусловно, в рамках приемлемости для моего босса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...