我最近遇到了一个问题:
向量中有8个元素,每个元素由int8_t表示。
在x86_64中实现一个将添加两个向量(uint64_t类型)的算法。
添加元素时应考虑饱和度算法。
例如:
80 + 60 = 127
(−40)+(−100)= −128
最大的挑战是所施加的限制:
*除ret外无条件指令;没有跳转、cmove、set等。
*解决方案长度不能超过48条指令(存在短于37条指令的解决方案)
我想不出有什么解决方案能满足这些限制。谁能给予我一些提示吗?C中的例子是受欢迎的。
我只能使用“标准”、传输、算术、逻辑指令和标准寄存器:
*mov cbw/cwde/cdqe cwd/cdq/cqo movzx movsx
*add sub imul mul idiv div inc dec neg
*and or xor not sar sarx shr shrx shl shlx ror rol
*莱亚ret
4条答案
按热度按时间o2rvlv0m1#
这里是一个版本(测试,不需要imul),需要22个指令时compiled with clang-16。
组装:
6za6bjd02#
我用C++写的是这样的:
然后我转到https://godbolt.org/,看看各种编译器生成了什么。clang-16给出了33条指令:
您可以尝试其他各种选项。
ktecyv1j3#
使用
paddsb
指令添加带符号饱和度的字节向量。实现可以像这样(假设amd64 sysv abi):在没有MMX的情况下,可以使用以下方法。其思想是使用SWAR技术对所有字节并行执行以下算法:
以下代码未经测试,但应该可以工作:
注意,需要一些额外的处理来避免从一个字节执行到下一个字节。
tvz2xvvm4#
下面的代码使用了一种普通的带符号饱和的字节加法方法,但在指令计数和执行时间方面与Falk Hüffner's excellent algorithm非常有竞争力。
为了避免跨越字节通道边界,模拟SIMD算法的经典方法是对低阶7位和最高有效位分别执行计算,然后合并部分结果。在这种情况下,这也有助于检测有符号整数溢出,其一个定义是最高有效位的进位与该位的进位不同。
有符号整数加法溢出只能在加数的符号相同时发生。如果发生溢出,字节大小的特殊结果(下面代码中的
spc
)是0x7f
或0x80
,因此可以从任一加数的符号计算。溢出标志扩展为全0或全1的全字节掩码,用于选择常规加法结果(下面代码中的
res
)或传统复用习惯中的特殊溢出结果。问题列出了允许的BMI 2指令集扩展(2013年引入)的各种指令,所以我假设使用BMI 1扩展的
andn
指令也是允许的,尽管问题中没有明确列出。我在Windows 10机器上开发了我的实现
epaddsb
,因此代码使用了Windows对x86-64的调用约定。对于Linux使用的System V ABI,更改这一点很简单:只需交换几个注册名。为了与Falk Hüffner的算法进行比较,我使用最新的Intel oneAPI编译器编译了他的C代码,并在hpaddsb
中捕获了生成的代码。epaddsb
需要21条指令,而不需要ret
,而hpaddsb
需要20条指令,而不需要ret
。在我的基于Skylake CPU的PC上,两种变体的性能在±2%的测量噪声水平内是相同的。我在下面展示我的测试脚手架。我构建如下
使用Microsoft Macro Assembler 14。27.29112.0和英特尔oneAPI DPC++/C++编译器2023。0.0.