Gcc 如何使用内联汇编强制编译器生成条件移动

Gcc 如何使用内联汇编强制编译器生成条件移动,gcc,x86,inline-assembly,conditional-move,Gcc,X86,Inline Assembly,Conditional Move,我花了几个小时试图将以下代码转换为内联程序集(GCC),但没有成功: int new_low = mid + 1; int new_high = mid - 1; if (middle < key) { low = new_low; } if (!(middle < key)) { high = new_high; } 您通常不需要内联asm,但您的问题是,[low]“=&r”(low)是一个只写输出!!您告诉编译器变量的旧值是无关的,它是只写的。但对于cmov

我花了几个小时试图将以下代码转换为内联程序集(GCC),但没有成功:

int new_low = mid + 1;
int new_high = mid - 1;

if (middle < key) {
    low = new_low;
}

if (!(middle < key)) {
    high = new_high;
}

您通常不需要内联asm,但您的问题是,
[low]“=&r”(low)
是一个只写输出!!您告诉编译器变量的旧值是无关的,它是只写的。但对于
cmov
,情况并非如此:它是一个三输入指令:src、dst和FLAGS

对低/高(cmov目标)使用
“+&r”
读/写操作数。事实证明,这基本上是一个副本,其中显示的内联asm与您的几乎相同(使用FP比较而不是整数,但使用相同的cmov指令)

这应该是可行的,但会迫使编译器使用更多的寄存器。(例如,它可以在CMP之后使用LEA来执行mid+1和mid-1,覆盖
mid
。甚至可以使用LEA+INC,因为B和AE条件只读取CF,而且足够新的CPU根本没有部分标志暂停,如果需要,它们只需要cmov分别读取标志的两部分。这就是为什么
cmovbe
是天空上的2-uop指令湖泊,与大多数其他条件下的1相比。)


使编译器无分支:
你试过三元运算符吗,比如
low=(middle?您是否尝试过概要文件引导优化,以便GCC可以看到分支实际上不是很可预测的?(表明,至少在某些情况下,if到无分支的转换通常只在
-O3
处进行)

另外,请记住,无分支会产生数据依赖性,这对于二进制搜索来说可能更糟;推测执行有效地为您提供了内存并行/预取,而不是序列化加载。()

对于在L1d缓存中命中的小型二进制搜索,分支未命中的代价大于加载使用延迟。但是,一旦您预计会出现一些二级缓存未命中,分支恢复将比三级缓存的负载使用延迟更便宜

1/4和3/4元素的软件预取可以帮助缓解这种情况,隐藏负载使用延迟。这利用了内存级别的并行性(一次有多个负载),即使对于无分支的表单也是如此,其中通过需求负载链存在数据依赖性


如果您可以选择不同的数据结构,则可以使用SIMD非常快速地搜索宽隐式树,以平衡计算和延迟,从而只需几步就可以找到成功的结果。它是有序的,因此您可以按顺序遍历它,或者在命中后找到上一个或下一个元素


相关的:


您如何调用GCC?“寄存器阻塞存在一些问题,我无法正确处理。”这不是一个问题描述。您看到的确切问题是什么?您是否尝试过三元运算符,如
low=(middle?您是否尝试过概要文件引导优化,以便GCC可以看到分支实际上不是很可预测的?(表明,至少在某些情况下,if到无分支的转换通常只在
-O3
处进行)。另外,请记住,无分支创建了数据依赖,这对于二进制搜索来说可能更糟糕;推测性执行有效地为您提供内存并行/预取,而不是序列化加载。使用二进制搜索,使用
大小
,而不是
。这意味着您只需要一个条件(如果针处于高半部,则更新基准),并且可以无条件地更新大小(切成两半)。这通常会导致出现
cmov
。您给我的建议解决了这个问题。非常感谢。正如您所说的:对于小工作集,带有条件分支的版本会更快,但一旦工作集不适合缓存,带有分支的版本就会更快。你能告诉我更多的资源来解释为什么会这样吗?另外,不管我在做什么,编译器(GCC 4.9和GCC 8)都没有生成cmov。@Bogi:这将是我理解无序执行的首要建议。也许是关于CPU工作原理的一些基础知识。关于二进制搜索,请参见“谢谢,彼得”!我写了一篇关于分支优化的文章,你指出的是其中的关键发现:谢谢,Peter!我写了一篇关于分支优化的文章,您指出的是其中的关键发现:
__asm__ (
    "cmp %[array_middle], %[key];"
    "cmovb %[new_low], %[low];"
    "cmovae %[new_high], %[high];"
    : [low] "=&r"(low), [high] "=&r"(high)
    : [new_low] "r"(new_low), [new_high] "r"(new_high), [array_middle] "r"(middle), [key] "r"(key)
    : "cc"
);