#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 вокруг устаревшего кода — и я бы все равно использовал это для завершения классов, но для простых циклов, я думаю, это излишне.