我正在尝试使用SSE内在函数优化一小段代码(我是关于该主题的完全初学者),但我有点坚持使用条件.
我原来的代码是:
unsigned long c;
unsigned long constant = 0x12345678;
unsigned long table[256];
int n, k;
for( n = 0; n < 256; n++ )
{
c = n;
for( k = 0; k < 8; k++ )
{
if( c & 1 ) c = constant ^ (c >> 1);
else c >>= 1;
}
table[n] = c;
}
Run Code Online (Sandbox Code Playgroud)
这段代码的目标是计算一个crc表(常量可以是任何多项式,它在这里不起作用),
我想我的优化代码是这样的:
__m128 x;
__m128 y;
__m128 *table;
x = _mm_set_ps(3, 2, 1, 0);
y = _mm_set_ps(3, 2, 1, 0); …Run Code Online (Sandbox Code Playgroud) 当我查看最近处理器的图表和概述时 [1],我从未看到提及 MMX 寄存器 MM0 - MM7。但从规格来看,它们似乎仍然存在。可以依赖它们存在于支持 SSE 的所有处理器中吗?除了更旧的 FPU 堆栈之外,它们是否与其他任何东西冲突?它们是否与一般 64 位的物理寄存器相同?
虽然 XMM 和 YMM 对于向量要好得多,但我偶尔想使用 MMX 寄存器来存储值,否则这些值会溢出到堆栈中。Speedwise 这看起来好一点,而且有时我想避免额外的商店和负载。
使用SSE内在函数,我得到了一个四个32位浮点数的向量,它被钳位到0-255范围并四舍五入到最接近的整数.我现在想把这四个写成字节.
有一个内部函数_mm_cvtps_pi8会将32位转换为8位有符号整数,但问题是任何超过127的值都会被钳位到127.我找不到任何会压缩无符号8位值的指令.
我有一种直觉,我可能想要做的是移动指令的一些组合_mm_cvtps_pi16和_mm_shuffle_pi8后面的操作,以获得我关心的四个字节到内存中.这是最好的方法吗?我将看看我是否可以弄清楚如何编码shuffle控制掩码.
更新:以下似乎完全符合我的要求.有没有更好的办法?
#include <tmmintrin.h>
#include <stdio.h>
unsigned char out[8];
unsigned char shuf[8] = { 0, 2, 4, 6, 128, 128, 128, 128 };
float ins[4] = {500, 0, 120, 240};
int main()
{
__m128 x = _mm_load_ps(ins); // Load the floats
__m64 y = _mm_cvtps_pi16(x); // Convert them to 16-bit ints
__m64 sh = *(__m64*)shuf; // Get the shuffle mask into a register
y = _mm_shuffle_pi8(y, sh); // Shuffle the …Run Code Online (Sandbox Code Playgroud) 我一直在看MMX / SSE,我想知道。对于无符号字节和字(而非双字),有打包,饱和减法的说明。
有什么方法可以做我想要的,如果没有,为什么没有呢?
我目前正在将大型代码库从 VS2013 更新到 VS2019。我遇到的编译器错误之一如下:
内部函数.h(348):错误C3861:“_mm_cvtpd_pi32”:未找到标识符
这个内部函数在 Visual Studio 的“emmintrin.h”中定义。我只在针对 64 位构建时收到此错误。仔细检查会发现,在 2013 年到 2019 年之间,emmintrin.h 的定义从这个改变了:
extern __m64 _mm_cvtpd_pi32(__m128d _A);
extern __m64 _mm_cvttpd_pi32(__m128d _A);
extern __m128d _mm_cvtpi32_pd(__m64 _A);
Run Code Online (Sandbox Code Playgroud)
对此:
#if defined(_M_IX86)
extern __m64 _mm_cvtpd_pi32(__m128d _A);
extern __m64 _mm_cvttpd_pi32(__m128d _A);
extern __m128d _mm_cvtpi32_pd(__m64 _A);
#endif
Run Code Online (Sandbox Code Playgroud)
即:预处理器指令确保函数现在仅可用于 32 位目标。无论目标是什么(64 位或 32 位),产生错误的第 3 方头文件都会使用这些函数。据推测,这里最好的做法是编辑这个头文件,以确保仅对 32 位目标调用此函数。然而,我更好奇的是,为什么从 2013 年到 2019 年发生了变化?我在这里看到了这个函数的描述:
https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtpd_pi32&expand=1705
从一开始它就从未适用于 64 位目标吗?或者它是否已被我需要考虑的 64 位版本替换?
英特尔内在函数的名称中内置了向量的子类型。例如,_mm_set1_ps是 a ps,即packed single-precisionaka。A float。尽管它们中的大多数的含义是明确的,但它们的“全名”packed single-precision从功能描述中并不总是清晰的。我创建了下表。不幸的是,有些条目丢失了。它们有何价值?其他问题见表下方。
| 缩写 | 全名 | C/++ 等效项 |
|---|---|---|
| 附注 | 压缩单精度 | 漂浮 |
| 酸碱度 | 压缩半精度 | 没有任何** |
| PD | 压缩双精度 | 双倍的 |
| 个人电脑 | 压缩半精度复数 | 没有任何** |
| pi8 | ??? | int8_t |
| PI16 | ??? | int16_t |
| pi32 | ??? | int32_t |
| Epi8 | ??? | int8_t |
| 表观16 | ??? | int16_t |
| 表观32 | ??? | int32_t |
| 表观64 | ??? | int64_t |
| 外延64x | ??? | int64_t |
附加问题:
epiX和 和有什么区别piX?pi64存在?epi64和 和有什么区别epi64x?** 我发现了这个,但似乎没有标准方法来表示 C/++ 中的半精度(复数)值。如果这有任何改变,请纠正我。
我正在编写一个多线程的高度并行应用程序.我已经编写了一个SSE加速线程类.如果我要写一个MMX加速线程类,那么同时运行两个(一个SSE线程和每个核心一个MMX线程)性能会明显提高吗?
我认为这个设置有助于隐藏内存延迟,但我想在开始花时间之前确定它.
我在 ASM 方面的经验极其有限,在 SIMD 方面的经验就更少了。
但碰巧我有以下 MMX/SSE 优化代码,我想将其移植到 AltiVec 指令以在 PPC/Cell 处理器上使用。
这可能是一个很大的问题..尽管只有几行代码,但我在尝试弄清楚这里发生的事情时遇到了无穷无尽的麻烦。
原函数:
static inline int convolve(const short *a, const short *b, int n)
{
int out = 0;
union {
__m64 m64;
int i32[2];
} tmp;
tmp.i32[0] = 0;
tmp.i32[1] = 0;
while (n >= 4) {
tmp.m64 = _mm_add_pi32(tmp.m64,
_mm_madd_pi16(*((__m64 *)a),
*((__m64 *)b)));
a += 4;
b += 4;
n -= 4;
}
out = tmp.i32[0] + tmp.i32[1];
_mm_empty();
while (n --)
out += (*(a++)) * (*(b++)); …Run Code Online (Sandbox Code Playgroud) section .data
qVar1: dq 1
section .bss
var28: resb 28
section .text
_main:
; Use an MMX instruction
movq mm0, [qVar1] ; Move quadword from r/m64 to mm.
; Read Tag Word
fstenv [var28]
mov ax, [var28 + 8] ; move the Tag Word to ax
Run Code Online (Sandbox Code Playgroud)
这一刻ax是0101 0101 0101 0110
但是从英特尔手册,第9.5.1节MMX指令和x87 FPU标签字,我引用:
在每个MMX指令之后,整个x87 FPU标记字被设置为有效(00B).
那么为什么ax不是全零呢?
我在xmm1寄存器中加载了一些内容,让我们说它可以被视为
xmm1 = | bgra | bgra | bgra | bgra | (each one a dw)
Run Code Online (Sandbox Code Playgroud)
现在,我希望每个双字在逻辑上向右移1个字节,所以它最终会像这样:
xmm1 = | 0bgr | 0bgr | 0bgr | 0bgr | (each one a dw)
Run Code Online (Sandbox Code Playgroud)
我在intel doc上发现我可能正在寻找函数"psrld":

然而,它起初并没有像我预期的那样发挥xmm1作用
xmm1 {v4_float = {0x0, 0x0, 0x0, 0x0}, v2_double = {0x0, 0x0}, v16_int8 = {0x37, 0x51, 0x9e, 0x0, 0x3e, 0x58, 0xa5, 0x0, 0x3e, 0x5a, 0xa7, 0x0, 0x4a, 0x66, 0xb3, 0x0}, v8_int16 = {0x5137, 0x9e, 0x583e, 0xa5, 0x5a3e, 0xa7, 0x664a, 0xb3}, …Run Code Online (Sandbox Code Playgroud)