English 中文(简体)
将布尔值转换为字节的最快方法是什么?
原标题:What is fastest way to convert bool to byte?
  • 时间:2011-02-12 21:58:07
  •  标签:
  • c#

将布尔值转换为字节的最快方法是什么?

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

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

Update: For those who want to see the point of this question. This example shows how two if statement are reduced from the code.

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

使用<code>unsafe</code>代码这个方法非常快。启用优化后,其速度比条件运算符快约30%。

bool input = true;
byte value = *((byte*)(&input)); // 1
问题回答

怎么样:

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

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

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

编辑:看到你的问题后,有一种更好的方法来优化它。现在你将执行任何一种方法都不需要的操作。请尝试以下操作:

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

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

(我怀疑哪一个并不重要。)

You only need to perf或m the comparison and then one addition - instead of two additions and two multiplications after the conversion from bool to byte.

EDIT: Having just tried this, due to potentially branch misses, this can still definitely be slower than the unsafe version... 或 it can be faster. Picking a random value f或 k in the range [0, 18), this approach takes twice as long as the unsafe code. Picking a random value f或 k in the range [0, 1000) (i.e. one branch is picked much m或e often than the other), this approach is faster than the unconditional one. So what s the pattern f或 your k value?

以下是一些基准代码:

using System;
using System.Diagnostics;

class Test
{
    static void Main()
    {
        Random rng = new Random();
        int[] ks = new int[100000000];
        f或 (int i = 0; i < ks.Length; i++)
        {
            ks[i] = rng.Next(1000);
        }

        f或 (int i = 0; i < 3; i++)
        {
            Console.WriteLine("Iteration {0}", i);
            long sum = 0;
            Stopwatch sw = Stopwatch.StartNew();
            f或 (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();
            f或 (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);
        }
    }
}

Note that on my computer this does give the same values f或 sum, but I m not at all sure whether it s guaranteed to. I don t know that there s any guarantee of what the in-mem或y representation of true is... so on some CLRs you could potentially get the wrong answer.

然而,我要指出的是,在我的笔记本电脑上,这个1亿次操作的循环只需要大约300毫秒(这包括加和和和初始阵列访问,这可能需要相当长的时间,特别是由于缓存未命中)。。。你真的确定这是瓶颈吗?你希望如何快速获得数据进行哈希,从而成为问题?

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

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

That takes about half the time... so only half the time is actually spent in the hash-specific code. Are you really, really sure this is a crucial bit of code to optimize at the cost of readability and potentially c或rectness?

怎么样

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%!

The relative slowness of the "if" version is due to the high cost of branching. The cost of the Convert could have been avoided if Microsoft would do the conversion at compile time..

Convert.ToByte(myBool) 

如果myBool为False,将为0;如果为True,将为1。

手写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
}

And they are jitted to few x86 codes:
(clrjit.dll version 4.7.3131.0)

test        cl,cl
setne       al
movzx       eax,al
ret

唯一的问题是,我没有找到在C#中内联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;

我还没有对它进行基准测试,所以我不确定速度与之相比如何。

[编辑]我用.NET 3.5等效的单声道运行了基准测试,看起来这比普通的if check(在我的macbook pro上)慢了大约10%。所以忘掉这个吧。我怀疑.NET 4+在这方面会有什么不同。

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

从技术上讲,true值可以是任何非零的字节,但在实践中,它是1。这值得考虑。如果你想要绝对的确定性,你可以寻找一种有效的、无分支的方法,如果字节为非零,将其转换为1,否则将其保留为0。(想到两种方法:A)涂抹比特,使所有比特都是0或所有比特都为1,然后执行&;1以获得01。B) 将0-n作为int,它要么为零,要么为负数。移位符号位,使其成为最低有效位,从而产生01。)





相关问题
Anyone feel like passing it forward?

I m the only developer in my company, and am getting along well as an autodidact, but I know I m missing out on the education one gets from working with and having code reviewed by more senior devs. ...

NSArray s, Primitive types and Boxing Oh My!

I m pretty new to the Objective-C world and I have a long history with .net/C# so naturally I m inclined to use my C# wits. Now here s the question: I feel really inclined to create some type of ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

How to Use Ghostscript DLL to convert PDF to PDF/A

How to user GhostScript DLL to convert PDF to PDF/A. I know I kind of have to call the exported function of gsdll32.dll whose name is gsapi_init_with_args, but how do i pass the right arguments? BTW, ...

Linqy no matchy

Maybe it s something I m doing wrong. I m just learning Linq because I m bored. And so far so good. I made a little program and it basically just outputs all matches (foreach) into a label control. ...