C 在x86上给出无分支FP min和max的指令是什么?
引用(感谢作者开发和分享算法!): 因为现代浮点指令集可以计算最小值和最大值,而不需要分支 作者的相应代码只是C 在x86上给出无分支FP min和max的指令是什么?,c,assembly,optimization,floating-point,x86,C,Assembly,Optimization,Floating Point,X86,引用(感谢作者开发和分享算法!): 因为现代浮点指令集可以计算最小值和最大值,而不需要分支 作者的相应代码只是 dmnsn_min(double a, double b) { return a < b ? a : b; } dmnsn\u最小值(双a双b) { 返回a
dmnsn_min(double a, double b)
{
return a < b ? a : b;
}
dmnsn\u最小值(双a双b)
{
返回a
我熟悉例如\u mm\u max\u ps
,但这是一种矢量指令。上面的代码显然是以标量形式使用的
问题:
- x86上的标量无分支minmax指令是什么?这是一系列指令吗
- 假设它将被应用是安全的,或者我如何称呼它
- 关心min/max的无分支性有意义吗?据我所知,对于光线跟踪器和/或其他viz软件,给定光线盒交点例程,分支预测器没有可靠的模式可拾取,因此消除分支是有意义的。我说的对吗
- 最重要的是,所讨论的算法是围绕比较(+/-)无穷大而构建的。这个可靠的w.r.t是我们正在讨论的(未知)指令还是浮点标准
以防万一:我很熟悉,相信这是相关的,但不是我的问题。警告:小心编译器将
\u mm\u min\u ps
/\u mm\u max\u ps
(和_pd)内部函数视为可交换的,即使在严格的FP(非快速数学)模式下;即使asm指令不是。GCC似乎特别有这样一个bug:它在GCC7中被修复了,但是对于标量内部函数(,GCC bugzilla),它可能会回来或者永远不会被修复
GCC知道asm指令本身是如何工作的,当使用它们在纯标量代码中实现严格的FP语义时,只有使用C/C++内部函数时,就不会有这个问题
不幸的是,没有一条指令能够实现fmin(a,b)(保证NaN传播),因此您必须在简单的问题检测和更高的性能之间做出选择
大多数向量FP指令都具有标量等价项/MAXSS/MINSD/MAXSD是您想要的。他们以你期望的方式处理+/-无穷大
分钟a、b
完全实现了(a彼得·科尔德斯的答案很棒,我只是想用一些简短的逐点回答:
- x86上的标量无分支最小最大指令是什么?它是一个指令序列吗
我指的是mins
/minsd
。即使没有此类指令的其他体系结构也应该能够通过有条件的移动来实现这一点
- 假设它将被应用是安全的,或者我如何称呼它
gcc
和clang
都会将(a
优化为分钟
/分钟
,所以我不用麻烦使用内部函数。不过,我无法与其他编译器对话
- 担心最小/最大值的无分支性有意义吗?据我所知,对于光线跟踪器和/或其他viz软件,给定一个光线盒相交例程,分支预测器没有可靠的模式可拾取,因此消除分支是有意义的。我说的对吗
单个a
测试几乎完全不可预测,因此避免这些测试的分支非常重要
是非常可预测的,因此避免这些分支就不那么重要了,但它确实缩小了代码大小,使矢量化更容易。不过,最重要的部分可能是删除分区
- 最重要的是,所讨论的算法是建立在与(+/-)无穷大进行比较的基础上的。这个可靠的w.r.t是我们正在讨论的(未知)指令还是浮点标准
mins
/minsd
的行为与(a
完全相同,包括它们对无穷大和nan的处理
另外,我还写了一篇文章给你提到的那篇文章,其中更详细地讨论了NaNs和min/max。
\u mm\u max\u ps
具有标量等价物\u mm\u max\u ss
好吧,这是为了显示指令存在。编译器可以在需要时随时使用它。@iksemyonov看到这个标量用法的工作示例,请参见fcomi
和fcmov
@PeterCordes因为OP没有要求fmin()
,fmax()
行为,所以我没有向他们建议,尽管就个人而言,这是我的偏好(这是最简单的C代码,我习惯于在直接映射到机器指令的平台上工作;我惊讶地发现这对于x86来说仍然不是真的)。我对你的答案也没有挑剔的地方,所以我认为我们达成了“暴力协议”:-)无言以对,不胜感激!将给予奖励,如果可能的话,不超过+10。一旦我使用这个raybox例程完成一些代码,我就会阅读。是的,低浮点值的东西也让我感到困扰,遗憾的是我还不知道AVX,但很快就会进入它。@iksemyonov:你可以单击“接受”勾选向上/向下投票箭头下的复选框,给我另一个+15,并告诉未来的读者这回答了你的问题:)Suuure,让问题暂时搁置只是一种习惯。在你的回答之后,让它挂起来可能没有什么意义。我负责接受答案,不用担心:)我想我的主要观点是很多人认为他们不需要NAN,而事实上他们可能需要NAN。但这确实是一种直觉,所以我不能再进一步辩论了。@Zboson:是的,我知道你的意思。南斯很好,因为他们毒害了下游的一切,所以你让南斯在结果中告诉你有问题。任何不符合标准的东西都是不利的。
// can and does inline to a single MINSD instruction, and can auto-vectorize easily
static inline double
dmnsn_min(double a, double b) {
return a < b ? a : b;
}
float minfloat(float a, float b) {
return (a<b) ? a : b;
}
# any decent compiler (gcc, clang, icc), without any -ffast-math or anything:
minss xmm0, xmm1
ret
// C++
float minfloat_std(float a, float b) { return std::min(a,b); }
# This implementation of std::min uses (b<a) : b : a;
# So it can produce the result only in the register that b was in
# This isn't worse (when inlined), just opposite
minss xmm1, xmm0
movaps xmm0, xmm1
ret
float minfloat_fmin(float a, float b) { return fminf(a, b); }
# clang inlines fmin; other compilers just tailcall it.
minfloat_fmin(float, float):
movaps xmm2, xmm0
cmpunordss xmm2, xmm2
movaps xmm3, xmm2
andps xmm3, xmm1
minss xmm1, xmm0
andnps xmm2, xmm1
orps xmm2, xmm3
movaps xmm0, xmm2
ret
# Obviously you don't want this if you don't need it.
lowest = _mm_min_ps(lowest, some_loop_variable);
if(some condition)
lowest = min(lowest, x);
__m128 inverse_condition = _mm_cmplt_ps(foo, bar);
__m128 x = whatever;
x = _mm_or_ps(x, condition); // turn elements into NaN where the mask is all-ones
lowest = _mm_min_ps(x, lowest); // NaN elements in x mean no change in lowest
// REQUIRES NON-COMMUTATIVE _mm_min_ps: no -ffast-math
// AND DOESN'T WORK AT ALL WITH MOST GCC VERSIONS.