- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
在给定std::bitset<64> bits
的情况下设置了任意数量的位,并且设置了位位置X
(0-63)
最有效的方法是对X或更低位置的位数进行计数,或者如果未设置X的位,则返回0是最有效的方法
注意:如果该位置1,则返回值始终至少为1
蛮力方式很慢:
int countupto(std::bitset<64> bits, int X)
{
if (!bits[X]) return 0;
int total=1;
for (int i=0; i < X; ++i)
{
total+=bits[i];
}
return total;
}
count()
的
bitset
methof将为您提供所有位的
popcount
,但
bitset
不支持范围
最佳答案
此C++使g++发出very good x86 ASM (godbolt compiler explorer)。我希望它也可以在其他64位体系结构上有效地进行编译(如果有供std::bitset::count
使用的HW popcount,否则它将始终很慢;例如,请确保使用g++ -march=nehalem
或更高版本,或者如果您不想使用-mpopcnt
如果可以将代码限制为仅在支持该x86指令的CPU上运行,请启用其他功能):
#include <bitset>
int popcount_subset(std::bitset<64> A, int pos) {
int high_bits_to_eliminate = 63 - pos;
A <<= (high_bits_to_eliminate & 63); // puts A[pos] at A[63].
return (A[63]? ~0ULL : 0) & A.count(); // most efficient way: great code with gcc and clang
// see the godbolt link for some #ifdefs with other ways to do the check, like
// return A[BSET_SIZE-1] ? A.count() : 0;
}
63
做一些处理,并将移位计数的
& 63
掩码更改为更常规的范围检查。为了获得奇特大小的位集的最佳性能,请对目标计算机的
size <= register width
进行专门化的模板功能。在这种情况下,将位集提取为适当宽度的
unsigned
类型,然后移至寄存器的顶部而不是位集的顶部。
bitset<32>
生成理想的代码,但事实并非如此。 gcc/clang仍在x86-64上使用64位寄存器。
pos
的单词下面的单词进行弹出计数并在该单词上使用该单词慢。 (如果您可以假设SSSE3而不是
popcnt
insn硬件支持或针对32位目标,则这是矢量化popcount真正在x86上的亮点。AVX2256位
pshufb
是执行批量popcount的最快方法,但是如果没有AVX2,我认为64位
popcnt
很漂亮接近于128位
pshufb
实现。有关更多讨论,请参见评论。)
psadbw
,以在基于
pshufb
的popcnt单独生成每个字节的位数之后,对64位块中的水平和字节进行水平求和。 SSE/AVX没有64位算术右移,但是您可以使用另一种技术来混合每个元素的高位。
(1<<(pos+1)) -1
)并对其进行
&
。一种更有效的方法是通过
63-pos
左移,将要打包的位保留在寄存器的顶部。
popcnt
指令仅在Nehalem及更高版本上可用。我忘了AMD增加支持的时间。
popcnt
的后备资源来进行CPU调度。或者,制作独立的/不依赖于某些CPU功能的二进制文件。
popcnt
指令的popcount可以通过几种方法来完成。一个使用SSSE3
pshufb
来实现4位LUT。当在整个阵列上使用时,这是最有效的,而不是一次使用单个64b。标量bithack可能是这里最好的,并且不需要SSSE3(因此可以与具有64位但不包含pshufb的古老AMD CPU兼容)。
(A[63]? ~0ULL : 0)
要求编译器将高位广播到所有其他位位置,以便将其用作AND掩码,以将popcount结果归零(或不归零)。请注意,即使对于较大的位集大小,它仍然仅掩盖了
popcnt
的输出,而不是位集本身,因此
~0ULL
很好,我使用ULL来确保不再要求编译器仅将该位广播到ab的低32b。注册(例如,在Windows上使用
UL
)。
((int64_t)something) >> 63
并不是严格可移植的,因为带符号的右移是
implementation-defined as either arithmetic or logical。该标准未提供任何可移植的算术右移运算符。 (不过,它不是
undefined behaviour。)幸运的是,编译器足够聪明:一旦给了足够的提示,gcc就会找到最佳方法。
-O3 -march=nehalem -mtune=haswell
编译(较旧的gcc,如4.9.2,仍会发出此代码):
; the original ternary-operator version. See below for the optimal version we can coax gcc into emitting.
popcount_subset(std::bitset<64ul>, int):
; input bitset in rdi, input count in esi (SysV ABI)
mov ecx, esi ; x86 variable-count shift requires the count in cl
xor edx, edx ; edx=0
xor eax, eax ; gcc's workaround for popcnt's false dependency on the old value of dest, on Intel
not ecx ; two's complement bithack for 63-pos (in the low bits of the register)
sal rdi, cl ; rdi << ((63-pos) & 63); same insn as shl (arithmetic == logical left shift)
popcnt rdx, rdi
test rdi, rdi ; sets SF if the high bit is set.
cmovs rax, rdx ; conditional-move on the sign flag
ret
-x == ~x + 1
2的补码身份的背景信息,请参见
How to prove that the C statement -x, ~x+1, and ~(x-1) yield the same results?。 (并且
Which 2's complement integer operations can be used without zeroing high bits in the inputs, if only the low part of the result is wanted?切线地提到
shl
掩盖了移位计数,因此我们只需要
ecx
的低6位来保存
63 - pos
。主要是因为我最近写了它,而且仍然在阅读此段落的任何人都可能觉得它有趣。)
USE_mul
启用),gcc可以
shr rdi, 63
imul eax, edi
xor
/
test
/
cmovs
。
mov r,r
:1个融合域uop,0个延迟,无执行单元xor
-zeroing:1个融合域uop,无执行单元not
:p0/p1/p5/p6 1 uop,1c延迟,每0.25c吞吐量1个shl
(又名sal
),其中cl
计数:p0/p6为3 uops:2c延迟,每2c吞吐量1。 (奇怪的是,Agner Fog的数据表明IvyBridge仅需2 oups。)popcnt
:1 uop for p1,3c延迟,每1c吞吐量1 shr r,imm
:p0/p6为1 uop,延迟为1c。每0.5c吞吐量1个。 imul r,r
:p1为1uop,延迟为3c。 ret
shl
(2)->
popcnt
(3)->
imul
(3)。总共
8个周期。或准备好
pos
时的9c,因为
not
为其额外的1c延迟。
bitbroadcast
的
版本将
shr
替换为
sar
(相同的性能),并使用
imul
替换
and
(延迟为1c,而不是3c,可在任何端口上运行)。因此,唯一的性能变化是
,将关键路径延迟减少到6个周期。吞吐量仍然是前端的瓶颈。能够在任何端口上运行的
and
没什么区别,除非您将其与端口1上出现瓶颈的代码混合在一起(而不是在紧密循环中查看仅运行此代码的吞吐量)。
cmov
是2c延迟,对于p0/p1/p5/p6中的任何一个都是2微秒。)
test
/
cmovs
,它使用算术右移将符号位广播到寄存器的所有位置,从而生成全1或全0的掩码。我喜欢它:在英特尔上,使用
and
代替
cmov
更有效。但是,它仍然具有数据依赖性,并且可以在分支的两侧进行工作(这通常是cmov的主要缺点)。更新:通过正确的源代码,gcc也将使用此方法。
-O3 -Wall -march=nehalem -mtune=haswell
popcount_subset(std::bitset<64ul>, int):
mov ecx, 63
sub ecx, esi ; larger code size, but faster on CPUs without mov-elimination
shl rdi, cl ; rdi << ((63-pos) & 63)
popcnt rax, rdi ; doesn't start a fresh dep chain before this, like gcc does
sar rdi, 63 ; broadcast the sign bit
and eax, edi ; eax = 0 or its previous value
ret
sar / and
替换了
xor / test / cmov
,并且
cmov
是Intel CPU上的2 uop指令,因此非常好。 (对于三元运算符版本)。
sar / and
技巧而不是实际的
imul
。因此,这些帮助gcc不会损害clang。 (
sar/and
绝对比
shr/imul
更好:在关键路径上的延迟减少了2c。)
pow_of_two_sub
版本确实伤害了clang(请参阅第一个“godbolt”链接:此答案中省略了它,以避免困惑的想法没有被提出)。
mov ecx, 63
/
sub ecx, esi
实际上更快。这包括Intel之前的IvyBridge,但不包括更新的Intel和AMD CPU。
mov imm
/
sub
方法仅将
pos
的延迟周期放到关键路径上(除了bitset-> result延迟之外),而不是在
mov ecx, esi
具有1c延迟的CPU上将
not ecx
/
mov r,r
延迟两个周期。
mov
保存为
ecx
。其他所有功能都一样,因为
shlx
将其移位计数输入寄存器屏蔽为操作数大小,就像
shl
一样。
shl r, cl
解码为3 oups,但是BMI2
shlx r, r, r
只有1。因此,非常糟糕的是gcc仍然使用
sal
而不是
-march=haswell
发出
shlx
(在某些其他情况下会使用)。
// hand-tuned BMI2 version using the NOT trick and the bitbroadcast
popcount_subset(std::bitset<64ul>, int):
not esi ; The low 6 bits hold 63-pos. gcc's two-s complement trick
xor eax, eax ; break false dependency on Intel. maybe not needed when inlined.
shlx rdi, rdi, rsi ; rdi << ((63-pos) & 63)
popcnt rax, rdi
sar rdi, 63 ; broadcast the sign bit: rdi=0 or -1
and eax, edi ; eax = 0 or its previous value
ret
shlx
(1)->
popcnt
(3)->
and
(1)= 5c位集->结果。 (或
pos
-> result中的6c)。
xor eax, eax
。仅因为
popcnt
's false dependency on the output register (on Intel)而存在,并且我们需要
eax
的输出(调用者最近可能在较长的dep链上使用了该输出)。使用
-mtune=bdver2
或其他东西,gcc不会将要用于
popcnt
输出的寄存器清零。
popcnt
的源寄存器就已经准备就绪的输出寄存器,以避免出现此问题。当以后不需要源代码时,编译器将执行就地
popcnt rdi,rdi
,但这不是这种情况。相反,我们可以选择另一个在源之前已经准备好的寄存器。
popcnt
的输入取决于
63-pos
,我们可以破坏它,因此
popcnt rsi,rdi
的对rsi的依赖关系不会延迟它。或者,如果我们在寄存器中包含
63
,则可以
popcnt rsi,rdi
/
sarx rax, rsi, reg_63
/
and eax, esi
。否则,BMI2 3操作数转换指令也不会让我们在以后需要输入时费心。
63-pos
可以使用编译时常量进行优化,也可以优化到变量计数来自何处。)
shl
/
bt rdi, 63
/
jc
。它甚至以非常愚蠢的方式建立分支机构。它可以将eax设为零,然后根据
shl
设置的符号标志跳过popcnt或不跳过。
-O3 -march=corei7
的ICC13输出开始:
// hand-tuned, not compiler output
mov ecx, esi ; ICC uses neg/add/mov :/
not ecx
xor eax, eax ; breaks the false dep, or is the return value in the taken-branch case
shl rdi, cl
jns .bit_not_set
popcnt rax, rdi
.bit_not_set:
ret
A[pos] == true
案例有一个未采用的分支。但是,与无分支方法相比,它并没有节省太多。
A[pos] == false
情况更常见:跳过
ret
指令,转到
popcnt
/
ret
。 (或内联之后:跳转到执行
popcnt
的末尾的一个块并跳回)。
关于c++ - 在一个位置或更低的位置计数设置位的有效方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34407437/
#include using namespace std; class C{ private: int value; public: C(){ value = 0;
这个问题已经有答案了: What is the difference between char a[] = ?string?; and char *p = ?string?;? (8 个回答) 已关闭
关闭。此题需要details or clarity 。目前不接受答案。 想要改进这个问题吗?通过 editing this post 添加详细信息并澄清问题. 已关闭 7 年前。 此帖子已于 8 个月
除了调试之外,是否有任何针对 c、c++ 或 c# 的测试工具,其工作原理类似于将独立函数复制粘贴到某个文本框,然后在其他文本框中输入参数? 最佳答案 也许您会考虑单元测试。我推荐你谷歌测试和谷歌模拟
我想在第二台显示器中移动一个窗口 (HWND)。问题是我尝试了很多方法,例如将分辨率加倍或输入负值,但它永远无法将窗口放在我的第二台显示器上。 关于如何在 C/C++/c# 中执行此操作的任何线索 最
我正在寻找 C/C++/C## 中不同类型 DES 的现有实现。我的运行平台是Windows XP/Vista/7。 我正在尝试编写一个 C# 程序,它将使用 DES 算法进行加密和解密。我需要一些实
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 1
有没有办法强制将另一个 窗口置于顶部? 不是应用程序的窗口,而是另一个已经在系统上运行的窗口。 (Windows, C/C++/C#) 最佳答案 SetWindowPos(that_window_ha
假设您可以在 C/C++ 或 Csharp 之间做出选择,并且您打算在 Windows 和 Linux 服务器上运行同一服务器的多个实例,那么构建套接字服务器应用程序的最明智选择是什么? 最佳答案 如
你们能告诉我它们之间的区别吗? 顺便问一下,有什么叫C++库或C库的吗? 最佳答案 C++ 标准库 和 C 标准库 是 C++ 和 C 标准定义的库,提供给 C++ 和 C 程序使用。那是那些词的共同
下面的测试代码,我将输出信息放在注释中。我使用的是 gcc 4.8.5 和 Centos 7.2。 #include #include class C { public:
很难说出这里问的是什么。这个问题是含糊的、模糊的、不完整的、过于宽泛的或修辞性的,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开它,visit the help center 。 已关
我的客户将使用名为 annoucement 的结构/类与客户通信。我想我会用 C++ 编写服务器。会有很多不同的类继承annoucement。我的问题是通过网络将这些类发送给客户端 我想也许我应该使用
我在 C# 中有以下函数: public Matrix ConcatDescriptors(IList> descriptors) { int cols = descriptors[0].Co
我有一个项目要编写一个函数来对某些数据执行某些操作。我可以用 C/C++ 编写代码,但我不想与雇主共享该函数的代码。相反,我只想让他有权在他自己的代码中调用该函数。是否可以?我想到了这两种方法 - 在
我使用的是编写糟糕的第 3 方 (C/C++) Api。我从托管代码(C++/CLI)中使用它。有时会出现“访问冲突错误”。这使整个应用程序崩溃。我知道我无法处理这些错误[如果指针访问非法内存位置等,
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以便用事实和引用来回答。 关闭 7 年前。
已关闭。此问题不符合Stack Overflow guidelines 。目前不接受答案。 要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于 Stack Overflow 来说是偏离主题的,因为
我有一些 C 代码,将使用 P/Invoke 从 C# 调用。我正在尝试为这个 C 函数定义一个 C# 等效项。 SomeData* DoSomething(); struct SomeData {
这个问题已经有答案了: Why are these constructs using pre and post-increment undefined behavior? (14 个回答) 已关闭 6
我是一名优秀的程序员,十分优秀!