Каков самый быстрый способ преобразования 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);
Ответ 1
Используя код unsafe
, этот метод выполняется довольно быстро. Оптимизация позволила примерно на 30% быстрее, чем условный оператор.
bool input = true;
byte value = *((byte*)(&input)); // 1
Ответ 2
Как насчет:
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 вы могли бы получить неправильный ответ.
Тем не менее, я хотел бы указать, что на моем ноутбуке этот цикл из 100 миллионов операций занимает всего около 300 мс (и это включает добавление к сумме и начальному доступу к массиву, что вполне может занять значительное время, особенно из-за кеша промахи)... ты действительно уверен, что это узкое место? Как вы надеетесь получить данные в хэш так быстро, что это станет проблемой?
EDIT: я просто добавил еще один цикл, чтобы увидеть "базовый случай":
for (int j = 0; j < ks.Length; j++)
{
int k = ks[j];
sum += k + 0x30;
}
Это занимает примерно половину времени... так что только половина времени фактически выполняется в коде, специфичном для хэша. Действительно ли вы действительно уверены, что это ключевой бит кода для оптимизации за счет удобочитаемости и потенциальной корректности?
Ответ 3
Как насчет
byte x = Convert.ToByte(true);
Ответ 4
// 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);
}
Ответ 5
Ниже приведен простой пример сравнения трех вариантов:
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 выполнила преобразование во время компиляции.
Ответ 6
Convert.ToByte(myBool)
даст вам 0, если myBool - False или 1, если оно истинно.
Ответ 7
Рукописный ИЛ:
.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.
Ответ 8
Вы можете использовать эту структуру, чтобы сделать аналогично решению 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+ будет иметь значение там.