Небезопасный код на c # для ускорения выполнения

#c#

#c#

Вопрос:

У меня есть код, который выполняется медленно, и я хочу ускорить его с помощью указателя небезопасного байта, отображенного в long.

Код, который я хочу ускорить, читает:

 public ulong seek(ulong ul)
{
    ulong v = table[ul >> 56, ul >> 48 amp; 0xFF] ^ table2[ul >> 40 amp; 0xFF ul >> 32 amp;
    0xFF] ^ table[ul >> 24 amp; 0xFF, ul >> 16 amp; 0xFF] ^ table2[ul >> 8 amp; 0xFF, ul amp;
    0xFF];
    return v;
}
  

В этом коде «table» и «table2» представляют собой таблицы размером 256 x 256 со случайными значениями ulong в них. Я хотел бы изменить вышесказанное, добавив:

 v2 = /* Add code to compute the same value using unsafe method, byte pointer arithmetic into the ulong "ul" instead of whole bunch of shifting.*/

Console.WriteLine("Called seek, v={0:X16}, v2={1:X16},
                     result={2}",v, v2, v==v2 ? "Pass" : "Fail"
                 );
  

Таким образом, я могу проверить, что оба метода возвращают одно и то же значение, вызывая эту функцию повторно со случайными значениями

Ответ №1:

Если вы находитесь в небезопасном контексте, вы действительно можете избавиться от сдвига битов:

Эти два оператора равны по значению:

   var a = (ul >> 48) amp; 255;
  var b = ((byte*) amp;ul)[6];
  

И если вы поставите точку останова в коде и перейдете к разборке, вы увидите, что, хотя вторая строка выглядит сложной, она совсем не сложная, вы не можете получить быстрее!

 var a = (ul >> 48) amp; 255;
  0000007e  mov         rax,qword ptr [rsp 20h] 
  00000083  sar         rax,30h 
  00000087  and         rax,0FFh 
  0000008d  mov         qword ptr [rsp 28h],rax 
var b = ((byte*) amp;ul)[6];
  00000092  movzx       eax,byte ptr [rsp 26h] 
  00000097  mov         byte ptr [rsp 30h],al 
  

И, во-вторых, индексирование массива происходит медленно. Как правило, вместо:

 var sum = 0;
for ( var i = 0 ; i < barr.Length ; i   )
    sum  = barr[i];
  

Вы можете вместо этого сделать:

 unsafe
{
    fixed (byte* pb = barr)
    {
        for ( var i = 0 ; i < barr.Length ; i   )
            sum  = pb[i];
    }
}
  

И тогда я предполагаю, что ваша «таблица» — это не массив байтов, а вместо этого массив длин? Тогда вы, конечно, также можете преобразовать это в массив байтов:

 unsafe
{
    fixed (ulong* ptable = table)
    {
        var pbtable = (byte*)ptable;
        //now you can access table as if it were a byte array
    }
}
  

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

Комментарии:

1. Вы уверены, что этот код быстрее? На моей машине они оба кажутся идентичными с точки зрения скоростей. Вот код, который я использовал для его тестирования: pastebin.com/XgjJfyc7

2. @John Smith — Действительно, этот пост неверен. Индексирование массива на самом деле НЕ происходит медленно. на самом деле использование небезопасного кода здесь может даже вызвать больше проблем с производительностью, поскольку ему приходится каждый раз закреплять массив, что плохо сказывается на производительности GC. Теперь индексация массива МОЖЕТ быть медленнее, если он находится в отмеченной области. Однако это по умолчанию отключено для режима выпуска, поэтому только в режиме отладки у вас будут различия в производительности

3. Ну, я попробовал ваш код и на своей машине (Win7 64 VS64, режим выпуска), и я получаю вдвое большую скорость из небезопасного кода. И когда я превращаю barr в двумерный массив , как в случае с OP, I он выполняется в четыре раза быстрее. Но я признаю тот факт, что его можно использовать только при циклическом переборе больших массивов, а не только внутри функции, подобной той, что есть у OP. Вместо этого всю функцию нужно сделать небезопасной и передать указатель.

4. Это довольно странно. Даже в коде выпуска они оба работают с одинаковой скоростью для меня. Не уверен, что происходит (возможно, компилятор выполняет оптимизацию?)

Ответ №2:

Я бы действительно рекомендовал вам использовать c / c и DllImport методы, которые имеют такую обработку. Не забудьте закрепить свои указатели перед вызовом этих методов.

Некоторая предыстория:

Я много работаю с аудио и видео. В процессе разработки сначала я создаю алгоритм, который работает (безопасный код, индексы в байтах [] и так далее). Затем моя первая оптимизация заключается в переходе к указателям и небезопасному коду, с помощью которого я получаю улучшение скорости примерно на 30-50%, не более. Затем, когда все улажено с точки зрения алгоритма и архитектуры, я перекодирую это в код c или c и вызываю его с помощью DllImports. Я оставлю индекс улучшения скорости, чтобы вы могли попробовать его в этом случае, но иногда я получал более чем 10-кратное улучшение скорости.

Комментарии:

1. Мне любопытно, почему вы выбираете DllImport вместо того, чтобы писать его на C / CLI. Это дало бы вам лучшее из обоих миров?

2. Я ненавижу видеть вид c / CLI — так что это просто личное предпочтение. От всех этих :: у меня кружится голова. На самом деле у меня есть один проект (mp3-плеер), который был написан как оболочка CLI вокруг устаревшего кода — и я бы все равно использовал это для завершения классов, но для простых циклов, я думаю, это излишне.