Ответ 1
Используя код unsafe
, этот метод выполняется довольно быстро. Оптимизация позволила примерно на 30% быстрее, чем условный оператор.
bool input = true;
byte value = *((byte*)(&input)); // 1
Каков самый быстрый способ преобразования bool в байт?
Я хочу это сопоставление: False = 0, True = 1
Примечание. Я не хочу использовать никаких операторов if
или других условных операторов. Я не хочу, чтобы процессор останавливал или предполагал следующий оператор.
Update: Для тех, кто хочет увидеть суть этого вопроса. В этом примере показано, как два кода инструкции сокращаются от кода.
byte A = k > 9 ; //If it was possible (k>9) == 0 || 1
c[i * 2] = A * (k + 0x37) - (A - 1) * (k + 0x30);
Используя код unsafe
, этот метод выполняется довольно быстро. Оптимизация позволила примерно на 30% быстрее, чем условный оператор.
bool input = true;
byte value = *((byte*)(&input)); // 1
Как насчет:
byte x = value ? (byte) 1 : (byte) 0;
Если вы говорите об наиболее эффективном способе этого делать, могут возникнуть некоторые трюки, которые вы могли бы сделать с небезопасным кодом... но действительно ли это является узким местом для вас?
EDIT: я просто понял, что условному оператору нужны эти приведения для операндов, чтобы сделать общее выражение байтом.
EDIT: Увидев ваш вопрос, есть намного лучший способ его оптимизации. В настоящее время вы будете выполнять операции, которые вам не нужны в любом случае. Вместо этого попробуйте:
c[i << 1] = k > 9 ? k + 0x37 : k + 0x30;
или
c[i << 1] = k + (k > 9 ? 0x37 : 0x30);
(Я подозреваю, что не имеет значения, какой.)
Вам нужно всего лишь выполнить сравнение, а затем одно дополнение - вместо двух добавлений и двух умножений после преобразования из bool в байт.
EDIT: просто попробовав это, из-за возможных промахов в отрасли, это все равно может быть медленнее, чем небезопасная версия... или это может быть быстрее. Выбирая случайное значение для k в диапазоне [0, 18], этот подход занимает в два раза больше, чем небезопасный код. Выбор случайного значения для k в диапазоне [0, 1000] (т.е. Одна ветвь выбрана гораздо чаще, чем другая), этот подход быстрее, чем безусловный. Итак, каков шаблон для вашего значения k
?
Здесь приведен пример кода:
using System;
using System.Diagnostics;
class Test
{
static void Main()
{
Random rng = new Random();
int[] ks = new int[100000000];
for (int i = 0; i < ks.Length; i++)
{
ks[i] = rng.Next(1000);
}
for (int i = 0; i < 3; i++)
{
Console.WriteLine("Iteration {0}", i);
long sum = 0;
Stopwatch sw = Stopwatch.StartNew();
for (int j = 0; j < ks.Length; j++)
{
int k = ks[j];
unsafe
{
bool input = k > 9;
byte A = *((byte*)(&input)); // 1
sum += A * (k + 0x37) - (A - 1) * (k + 0x30);
}
}
sw.Stop();
Console.WriteLine("Unsafe code: {0}; {1}ms",
sum, sw.ElapsedMilliseconds);
sum = 0;
sw = Stopwatch.StartNew();
for (int j = 0; j < ks.Length; j++)
{
int k = ks[j];
sum += k > 9 ? k + 0x37 : k + 0x30;
}
sw.Stop();
Console.WriteLine("Conditional: {0}; {1}ms",
sum, sw.ElapsedMilliseconds);
}
}
}
Обратите внимание, что на моем компьютере это дает те же значения для sum
, но я не уверен, гарантирован ли он. Я не знаю, что есть гарантия того, что представление true
в памяти... так что на некоторых CLR вы могли бы получить неправильный ответ.
EDIT: я просто добавил еще один цикл, чтобы увидеть "базовый случай":
for (int j = 0; j < ks.Length; j++)
{
int k = ks[j];
sum += k + 0x30;
}
Это занимает примерно половину времени... так что только половина времени фактически выполняется в коде, специфичном для хэша. Действительно ли вы действительно уверены, что это ключевой бит кода для оптимизации за счет удобочитаемости и потенциальной корректности?
Как насчет
byte x = Convert.ToByte(true);
// Warning! Brain-compiled code ahead!
static readonly char[] HexChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
public static string ToHex(this byte[] me)
{
if ( me == null ) return null;
int ml = me.Length;
char[] c = new char[2*ml];
int cp = 0;
for (int i = 0; i < ml; i++ )
{
c[cp++] = HexChars[me[i]&15];
c[cp++] = HexChars[me[i]>>4];
}
return new string(c);
}
Ниже приведен простой пример сравнения трех вариантов:
Int32 j = 0;
bool b = true;
for (int n = 0; n < 5; n++) {
Stopwatch sw1 = new Stopwatch();
Stopwatch sw2 = new Stopwatch();
Stopwatch sw3 = new Stopwatch();
sw1.Start();
for (int i = 100 * 1000 * 1000; i > 0; i--)
unsafe { j = *(int*)(&b); }
sw1.Stop();
sw2.Start();
for (int i = 100 * 1000 * 1000; i > 0; i--)
j = b ? 1 : 0;
sw2.Stop();
sw3.Start();
for (int i = 100 * 1000 * 1000; i > 0; i--)
j = Convert.ToInt32(b);
sw3.Stop();
Trace.WriteLine("sw1: " + sw1.ElapsedMilliseconds +
" sw2:" + sw2.ElapsedMilliseconds + ", +" + 100 * (sw2.ElapsedMilliseconds - sw1.ElapsedMilliseconds) / sw1.ElapsedMilliseconds + "% relative to sw1" +
" sw3:" + sw3.ElapsedMilliseconds + ", +" + 100 * (sw3.ElapsedMilliseconds - sw1.ElapsedMilliseconds) / sw1.ElapsedMilliseconds + "% relative to sw1"
);
}
Результаты:
sw1: 172 sw2:218, +26% relative to sw1 sw3:213, +23% relative to sw1
sw1: 168 sw2:211, +25% relative to sw1 sw3:211, +25% relative to sw1
sw1: 167 sw2:212, +26% relative to sw1 sw3:208, +24% relative to sw1
sw1: 167 sw2:211, +26% relative to sw1 sw3:209, +25% relative to sw1
sw1: 167 sw2:212, +26% relative to sw1 sw3:210, +25% relative to sw1
Вывод:
Небезопасный метод примерно на 25% быстрее, чем два других!
Относительная медленность версии "if" обусловлена высокой стоимостью ветвления. Стоимость Convert можно было бы избежать, если бы Microsoft выполнила преобразование во время компиляции.
Convert.ToByte(myBool)
даст вам 0, если myBool - False или 1, если оно истинно.
Рукописный ИЛ:
.method private hidebysig static
int32 BoolToInt (
bool b
) cil managed noinlining
{
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldc.i4.0
IL_0002: cgt.un
IL_0004: ret
}
И они сопоставлены с несколькими кодами x86:
(clrjit.dll версия 4.7.3131.0)
test cl,cl
setne al
movzx eax,al
ret
Единственная проблема заключается в том, что я не нашел простого способа встроить IL в С#. Этот ответ сделан с использованием dnSpy.
Вы можете использовать эту структуру, чтобы сделать аналогично решению ChaosPandion, но с безопасным кодом.
[StructLayout(LayoutKind.Explicit)]
struct BoolByte
{
[FieldOffset(0)]
public bool flag;
[FieldOffset(0)]
public byte num;
}
...
bool someBool = true;
byte num = new BoolByte() { flag = someBool }.num;
Я не тестировал его, так что я не уверен, как скорость сравнивается.
[EDIT] Ну, я запустил тест с .NET 3.5 эквивалентным моно, и похоже, что это примерно на 10% медленнее, чем обычная проверка (на моем MacBook Pro). Так что забудь об этом. Я сомневаюсь, что .NET 4+ будет иметь значение там.