什么是将bool转换为字节的最快方法?

时间:2011-02-12 21:58:08

标签: c#

将bool转换为byte的最快方法是什么?

我想要这个映射:False = 0,True = 1

注意:我不想使用任何if语句或其他条件语句。我不希望CPU停止或猜测下一个语句。

更新 对于那些想要看到这个问题的人。 此示例显示如何从代码中减少两个if语句。

byte A = k > 9 ; //If it was possible (k>9) == 0 || 1
c[i * 2] = A * (k + 0x37) - (A - 1) * (k + 0x30);

9 个答案:

答案 0 :(得分:25)

使用unsafe代码这种方法非常快。启用优化后,它比条件运算符快约30%。

bool input = true;
byte value = *((byte*)(&input)); // 1

答案 1 :(得分:20)

怎么样:

byte x = value ? (byte) 1 : (byte) 0;

如果你正在讨论最有效的方法,可能是你可以用不安全代码做的一些技巧......但这真的是你的瓶颈吗?

编辑:我刚刚意识到条件运算符需要为操作数进行转换,以使整个表达式成为一个字节。

编辑:看过你的问题后,有一种更好的方式来优化它IMO。目前,您将执行任何一种方式都不需要的操作。试试这个:

c[i << 1] = k > 9 ? k + 0x37 : k + 0x30;

c[i << 1] = k + (k > 9 ? 0x37 : 0x30);

(我怀疑哪个没关系。)

你只需要执行比较然后再添加一个 - 而不是两次加法和两次乘法 从bool到byte的转换。

编辑:刚刚尝试了这个,由于潜在的分支未命中,这仍然肯定比不安全的版本慢......或者它可以更快。在[0,18]范围内选取k的随机值,这种方法需要的时间是不安全代码的两倍。在[0,1000]范围内挑选k的随机值(即,一个分支比另一个更频繁地挑选),这种方法比无条件的更快。那么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上你可能会得到错误的答案。

但是,我要指出,在我的笔记本电脑上,这个1亿次操作的循环只需要大约300ms(这包括增加总和和初始阵列访问,这可能需要花费大量时间,特别是由于缓存错过了......你真的确定这是瓶颈吗?您是如何希望获得数据以便如此快速地散列以至于这成为问题?

编辑:我刚刚添加了另一个循环来查看“基本案例”:

for (int j = 0; j < ks.Length; j++)
{
    int k = ks[j];
    sum += k + 0x30;
}

大约需要一半的时间......因此,只有一半的时间花在特定于哈希的代码上。您是否确实确定这是以可读性和潜在正确性为代价进行优化的关键代码?

答案 2 :(得分:9)

怎么样

byte x = Convert.ToByte(true);

答案 3 :(得分:6)

// 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);
}

答案 4 :(得分: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”版本的相对缓慢是由于分支的高成本。 如果Microsoft在编译时进行转换,则可以避免转换成本。

答案 5 :(得分:3)

Convert.ToByte(myBool) 
如果myBool为False,

将为0,如果为True,则为0。

答案 6 :(得分:0)

手写IL:

.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

唯一的问题是,我发现没有简单的方法可以在C#中内联IL。这个答案是使用dnSpy完成的。

答案 7 :(得分:0)

您可以使用此结构执行与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等效单声道运行了基准测试,它看起来比普通的if check(在我的macbook pro上)慢了约10%。因此,忘记这一点。我怀疑.NET 4+是否会在其中有所作为。

答案 8 :(得分:0)

自.NET Core 2.1开始,您可以将bool重新解释为byte this way。这是无分支的,并且应该非常快,因为它几乎不需要“做”任何事情。

从技术上讲,true的值可以是任何非零的byte,但实际上它是1。值得考虑。如果您需要绝对确定性,则可以寻找一种高效,无分支的方式将字节变为1(如果非零),否则将其保留为0。 (我想到了两种方法:A)对位进行涂抹,以使所有位均为0或所有位均为1,然后执行& 1得到0或{ {1}}。 B)将1作为0 - n,它将为零或负。移位符号位,使其变为最低有效位,从而产生int0。)