int eq3(int a, int b, int c, int d, int e, int f){
return a == d || a == e || a == f
|| b == d || b == e || b == f
|| c == d || c == e || c == f;
}
此函数接收6个整数,如果前3个整数中的任何一个等于后3个整数中的任何一个,则返回true。有没有类似的比特黑客方法可以让它更快?
如果您想要按位版本,请查看异或。如果您异或两个相同的数字,答案将为0。否则,如果一个设置而另一个未设置,位将翻转。例如1000 xor 0100是1100。
您拥有的代码可能会导致至少1次管道刷新,但除此之外,它在性能方面还可以。
扩展dawg的SSE比较方法,可以使用向量或组合比较结果,并将比较结果的掩码移回整数,以测试0/非零。
此外,您可以更高效地将数据转换为向量(尽管当许多单独的整数位于寄存器中时,将其转换为向量仍然相当麻烦,而不是存储在内存中)。
你应该避免因为做三个小商店和一个大负载而导致的商店转发暂停。
///// UNTESTED ////////
#include <immintrin.h>
int eq3(int a, int b, int c, int d, int e, int f){
// Use _mm_set to let the compiler worry about getting integers into vectors
// Use -mtune=intel or gcc will make bad code, though :(
__m128i abcc = _mm_set_epi32(0,c,b,a); // args go from high to low position in the vector
// masking off the high bits of the result-mask to avoid false positives
// is cheaper than repeating c (to do the same compare twice)
__m128i dddd = _mm_set1_epi32(d);
__m128i eeee = _mm_set1_epi32(e);
dddd = _mm_cmpeq_epi32(dddd, abcc);
eeee = _mm_cmpeq_epi32(eeee, abcc); // per element: 0(unequal) or -1(equal)
__m128i combined = _mm_or_si128(dddd, eeee);
__m128i ffff = _mm_set1_epi32(f);
ffff = _mm_cmpeq_epi32(ffff, abcc);
combined = _mm_or_si128(combined, ffff);
// results of all the compares are ORed together. All zero only if there were no hits
unsigned equal_mask = _mm_movemask_epi8(combined);
equal_mask &= 0x0fff; // the high 32b element could have false positives
return equal_mask;
// return !!equal_mask if you want to force it to 0 or 1
// the mask tells you whether it was a, b, or c that had a hit
// movmskps would return a mask of just 4 bits, one for each 32b element, but might have a bypass delay on Nehalem.
// actually, pmovmskb apparently runs in the float domain on Nehalem anyway, according to Agner Fog's table >.<
}
这编译成了非常好的asm,在clang和gcc之间非常相似,但是clang的fverbose asm在洗牌上有很好的注释。只有19条指令包括ret,从独立的依赖链中获得了相当多的并行性。使用msse4.1或mavx,它可以保存另一对指令。(但可能不会跑得更快)
随着叮当声,道格的版本大约是两倍的大小。使用gcc时,会发生一些糟糕的事情,这很可怕(超过80条指令。看起来像是gcc优化错误,因为它看起来比直接翻译源代码更糟糕)。即使是clang的版本也花费了很长时间将数据输入/输出向量正则表达式,因此只需无分支地进行比较和/或将真值结合在一起可能会更快。
这将编译成合适的代码:
// 8bit variable doesn't help gcc avoid partial-register stalls even with -mtune=core2 :/
int eq3_scalar(int a, int b, int c, int d, int e, int f){
char retval = (a == d) | (a == e) | (a == f)
| (b == d) | (b == e) | (b == f)
| (c == d) | (c == e) | (c == f);
return retval;
}
玩一下如何将来自调用者的数据转换为向量regs。如果三个组来自内存,那么可能。传递指针,以便向量加载可以从它们的原始位置获取它们是最好的。在通往向量的路上通过整数寄存器很糟糕(更高的延迟,更多的uops),但是如果您的数据已经存在于regs中,那么进行整数存储然后进行向量加载是一种损失。gcc很蠢,并且遵循AMD优化指南的建议在内存中反弹,尽管Agner Fog说他发现即使在AMD CPU上也不值得。英特尔的情况肯定更糟,AMD的情况显然更糟,所以这绝对是-mtune=通用
的错误选择。无论如何...
第9个可以使用整数比较完成,并将其真值与向量结果进行ORed。在某些CPU(特别是AMD,可能还有Intel Haswell和更高版本)上,根本不将6个整数中的一个转移到向量regs可能是一种胜利。将三个整数无分支比较与向量随机/比较混合在一起会很好地交织它们。
这些向量比较可以通过对整数数据使用SHUFP来设置(因为它可以组合来自两个源寄存器的数据)。这在大多数CPU上都很好,但当使用内部函数而不是实际的asm时,需要很多恼人的转换。即使存在旁路延迟,与punpckldq和pshufd相比,这也是一个不错的权衡。
aabb ccab
==== ====
dede deff
c==f
使用asm,例如:
#### untested
# pretend a is in eax, and so on
movd xmm0, eax
movd xmm1, ebx
movd xmm2, ecx
shl rdx, 32
#mov edi, edi # zero the upper 32 of rdi if needed, or use shld instead of OR if you don't care about AMD CPUs
or rdx, rdi # de in an integer register.
movq xmm3, rdx # de, aka (d<<32)|e
# in 32bit code, use a vector shuffle of some sort to do this in a vector reg, or:
#pinsrd xmm3, edi, 1 # SSE4.1, and 2 uops (same as movd+shuffle)
#movd xmm4, edi # e
movd xmm5, esi # f
shufps xmm0, xmm1, 0 # xmm0=aabb (low dword = a; my notation is backwards from left/right vector-shift perspective)
shufps xmm5, xmm3, 0b01000000 # xmm5 = ffde
punpcklqdq xmm3, xmm3 # broadcast: xmm3=dede
pcmpeqd xmm3, xmm0 # xmm3: aabb == dede
# spread these instructions out between vector instructions, if you aren't branching
xor edx,edx
cmp esi, ecx # c == f
#je .found_match # if there's one of the 9 that's true more often, make it this one. Branch mispredicts suck, though
sete dl
shufps xmm0, xmm2, 0b00001000 # xmm0 = abcc
pcmpeqd xmm0, xmm5 # abcc == ffde
por xmm0, xmm3
pmovmskb eax, xmm0 # will have bits set if cmpeq found any equal elements
or eax, edx # combine vector and scalar compares
jnz .found_match
# or record the result instead of branching on it
setnz dl
这也是19条指令(不包括最终的jcc/setcc),但其中一条是异零习惯用法,还有其他简单的整数指令。(较短的编码,有些可以在Haswell上的port6上运行,无法处理向量指令)。由于构建abcc的洗牌链,可能会有更长的dep链。
假设您预期的结果为高比率的假结果,您可以快速进行“预检查”以快速隔离此类病例:
如果在a
中设置了未在d
、e
和f
中设置的位,则a
不能等于其中任何一个。
因此像这样的东西
int pre_eq3(int a, int b, int c, int d, int e, int f){
int const mask = ~(d | e | f);
if ((a & mask) && (b & mask) && (c & mask)) {
return false;
}
return eq3(a, b, c, d, e, f);
}
可以加速它(8个操作,而不是9个操作,但如果结果实际上是真的,则成本要高得多)。如果掩码==0,那么这当然没有帮助。
如果高概率<代码>a,这可以进一步改善
int pre_eq3(int a, int b, int c, int d, int e, int f){
int const mask = ~(d | e | f);
if ((a & b & c) & mask) {
return false;
}
if ((a & mask) && (b & mask) && (c & mask)) {
return false;
}
return eq3(a, b, c, d, e, f);
}
现在,如果a,b和c都设置了位,而d,e和c都没有设置任何位,那么我们很快就会退出。
问题内容: 我知道我可以这样做: 然后只需编写语句中所需的代码。 还有其他方法可以检查它们是否相等? 问题答案: 怎么了 if(!Arrays.equals(array1,array2)) 与相同,即是同一数组。这不是大多数人期望的。 比较数组的内容。
我得到 我怀疑这是因为两者都是。
我在一个VueJS实例中有两个数组。这些阵列显示学生可以在学校参加的课程。首先是用户参加的课程,其次是有学生参加的所有课程。 数组看起来像这样: 这两个数组都由从用户输入填充的数据组成,因此它们会随时间而变化。第一个数组是用户特定的内容(用户参加的课程),第二个数组是有人参加的所有课程的数组。 我想检查与用户参加相同课程的人数。在这种情况下,我想检查数组myCourses(课程a和b)中有多少在数
每个数字都应该大于或等于另一个数字。如果所有数字相等,则返回false。 例子: 通常的方式是不断地除以10,然后比较余数。 null null
问题内容: 在Python 3中,我正在检查给定值是否为三角形,也就是说,对于某个正整数n,它可以表示为n(n + 1)/ 2 我可以写: 还是我需要这样做?: 我检查了两个函数对于x的返回结果是否相同,直到1,000,000。但是我不确定一般来说int(x)== x是否总是可以正确确定一个数字是否为整数,因为例如5表示为4.99999999999997等。 据我所知,第二种方法是正确的方法,如果
问题内容: 我想检查两个数组是否相等。我的意思是:相同的大小,相同的索引,相同的值。我怎样才能做到这一点? 根据用户的建议,如果数组中的至少一个元素不同,我希望以下内容可以打印 enter ,但实际上没有。 问题答案: $arraysAreEqual = ($a == $b); // TRUE if $a and $b have the same key/value pairs. $arraysA