Какой самый быстрый способ конвертировать bool в байт?

Каков самый быстрый способ преобразования 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+ будет иметь значение там.