【发布时间】:2017-11-14 19:29:31
【问题描述】:
我对 SSE 编程完全陌生,但我拥有 Intel Core i7 处理器。
基本上,我想一次取 4 个 32 位无符号整数并将它们全部立方(提高到 3 的幂)。据我了解,SSE 及其后继产品的 SIMD 功能使这成为可能,但我到底该怎么做呢?最好在 C 中,但如果需要,我可以管理程序集。
编辑以明确我的最终目标:
然后,我想把所有的立方体加在一起得到一个数字。
背景:我只是想使用 SSE 来优化确定一个数字是否是 Armstrong 数字(一个三位数字,其每个数字的立方和与数字本身相同)。一个例子是 153。除了蛮力之外似乎没有办法做到这一点。这些是自恋数字的子集,其所有数字的总和到十进制数字的长度的幂等于数字本身。希望我最终将其扩展为更灵活,开始我只是在做阿姆斯壮的数字。正如您可能想象的那样,这出现在另一个网站上,我们中的一些人正在努力优化它。通过您的想法和我自己的研究,我想出了以下代码:
#include <stdio.h>
#include <smmintrin.h> // SSE 4.1
__m128i vcube(const __m128i v)
{
return _mm_mullo_epi32(v, _mm_mullo_epi32(v, v));
}
int main(int argc, const char * argv[]) {
for (unsigned int i = 1; i <= 500; i++) {
unsigned int firstDigit = i / 100;
unsigned int secondDigit = (i - firstDigit * 100) / 10;
unsigned int thirdDigit = (i - firstDigit * 100 - secondDigit * 10);
__m128i v = _mm_setr_epi32(0, firstDigit, secondDigit, thirdDigit);
__m128 v3 = (__m128) vcube(v);
v3 = _mm_hadd_ps(v3, v3);
v3 = _mm_hadd_ps(v3, v3);
if (_mm_extract_epi32((__m128i) v3, 0) == i)
printf ("%03d is an Armstrong number\n", i);
}
return 0;
}
注意:我必须进行一些类型强制才能使其在某些系统(Solaris,至少是某些 Linux)中编译。
所以这行得通,但也许可以简化。抱歉,我没有发布整个任务,但我试图将其分解为多个步骤,并且我想确保每个数字都正确立方。
(结束编辑)
谢谢!
编辑:我想我应该添加我正在运行 Mac OS X Sierra。
再次编辑:
所以,假设我制作了所有这些无符号短裤而不是无符号整数并添加更多数字,当短裤可能无法容纳所有数字的总和时,我如何将它们加在一起?如果您知道我的意思,有没有办法将它们添加并存储在更大变量的向量中,或者像 UInt64 这样的普通更大的数字?
抱歉所有问题,但就像我说的,我在矢量处理方面完全是新手,尽管我从我的第一台 Mac G4 开始就可以使用它。
【问题讨论】:
-
大概所有这些数字都是 32 位?您还需要准确的结果,还是允许有一点精度损失?
-
根据范围,您可以使用向量扩展并避免内在函数godbolt.org/g/3AicsN。但是,如果您执行 32x32 到 64 次操作,则需要内在函数。
-
十进制数字的立方体很容易适合 ushort,所以这就是你应该做的 -
_mm_mullo_epi16比_mm_mullo_epi32更有效。您也许可以安排它,以便您也可以使用_mm_madd_epi16。_mm_hadd_ps真的错了,你需要_mm_hadd_epi32来添加int。 -
已更改为 _mm_hadd_epi32,谢谢!
-
SSE 在这里真的帮不了你——循环中的大部分时间都花在提取十进制数字和准备 SIMD 向量上。您可以大量优化此循环以减少标量代码的数量,但我认为可能还有更好的非 SIMD 方法。