如何在 C# 中将 bool 重新解释为 byte/int 而无需分支?

Tim*_*imo 1 c# boolean reinterpret-cast type-punning branchless

在 C# 中是否可以在不分支的情况下bool将 a 转换为byteor int(或任何整数类型)?

换句话说,三元还不够好:

var myInt = myBool ? 1 : 0;   // could compile to a branch
Run Code Online (Sandbox Code Playgroud)

我们可能会说我们希望将 a 重新解释bool为底层byte,最好使用尽可能少的指令。目的是避免分支预测失败,如下所示


为了便于论证,我们假设 abool已经作为 0/1 存在于整数寄存器或内存中,而不仅仅是作为 FLAGS 中的比较结果。大多数现代 ISA 确实有一种无分支方式从 int 或 FP 比较中获取 0 或 1,因此我们希望 C# 使用它。或者,如果bool刚刚来自x<y,我们希望强制编译器将其具体化为整数,而不是变成count += myInt周围的分支count++

小智 6

unsafe
{
     byte myByte = *(byte*)&myBool;   
}
Run Code Online (Sandbox Code Playgroud)

另一种选择是 System.Runtime.CompilerServices.Unsafe,它需要非 Core 平台上的 NuGet 包:

byte myByte = Unsafe.As<bool, byte>(ref myBool);
Run Code Online (Sandbox Code Playgroud)

CLI 规范仅定义falseas0trueas 除 之外的任何内容0,因此从技术上讲,这可能无法在所有平台上按预期工作。然而,据我所知,C# 编译器还假设 仅有两个值bool,因此在实践中我希望它能够在大多数学术案例之外工作。

  • 从生成的程序集来看,这个答案中的解决方案是最快的。接下来是 [`MemoryMarshal`](/sf/answers/4125449821/),然后是 [`StructLayout`](/sf/answers/4125944371/),最后是天真的 [分支](/sf/ask/4125449751/)。 (2认同)

Pet*_*iho 5

通常的 C# 相当于“重新解释强制转换”,即struct使用要重新解释的类型的字段来定义 a。这种方法在大多数情况下效果很好。在你的情况下,看起来像这样:

[StructLayout(LayoutKind.Explicit)]
struct BoolByte
{
    [FieldOffset(0)]
    public bool Bool;
    [FieldOffset(0)]
    public byte Byte;
}
Run Code Online (Sandbox Code Playgroud)

然后你可以做这样的事情:

BoolByte bb = new BoolByte();
bb.Bool = true;
int myInt = bb.Byte;
Run Code Online (Sandbox Code Playgroud)

请注意,您只需初始化变量一次,然后就可以根据需要多次设置Bool和检索。Byte这应该比涉及不安全代码、调用方法等的任何方法表现得一样好或更好,特别是在解决任何分支预测问题方面。

需要指出的是,如果您可以将 a 读作boola byte,那么当然任何人都可以将abool为 a byte,并且 a 的实际intbool可能true是也可能不是1。从技术上讲,它可以是任何非零值。

话虽如此,这将使代码更难维护。既因为缺乏对true值实际外观的保证,也因为增加了复杂性。在现实世界中,很少会遇到您所询问的错过分支预测问题的情况。即使你有一个合法的现实世界的例子,也可以说用其他方式可以更好地解决这个问题。确切的替代方案取决于特定的现实示例,但一个示例可能是以允许在给定条件下进行批处理而不是测试每个元素的方式组织数据。

我强烈建议不要做这样的事情,直到你有一个经过论证的、可重现的现实问题,并且用尽了其他更惯用和可维护的选项。

  • 也许这对于“Unsafe.As&lt;bool, BoolByte&gt;(ref myBool);”更有用? (3认同)