"最佳"意味着最少的指令(或最少的uops,如果任何指令解码到多个uop).机器码大小(以字节为单位)是相同insn计数的平局.
恒定生成本质上是一个新的依赖链的开始,所以延迟很重要.在循环内生成常量也很不寻常,因此吞吐量和执行端口需求也几乎无关紧要.
生成常量而不是加载它们需要更多指令(除了全零或全一),因此它会占用宝贵的uop-cache空间.这可能是比数据缓存更有限的资源.
Agner Fog优秀的优化装配指南涵盖了这一点Section 13.4.表13.10具有用于产生向量序列,每一个元素是0,1,2,3,4,-1,或-2,与从8位到64位单元大小.表13.11具有用于产生一些浮点值序列(0.0,0.5,1.0,1.5,2.0,-2.0,和位掩码为符号位.)
Agner Fog的序列仅使用SSE2,无论是设计还是因为它尚未更新一段时间.
使用短的非显而易见的指令序列可以生成哪些其他常量? (具有不同移位计数的进一步扩展是显而易见的而不是"有趣的".)是否有更好的序列用于生成Agner Fog列出的常量?
如何将128位immediates移动到XMM寄存器说明了将任意128b常量放入指令流的一些方法,但这通常是不合理的(它不会节省任何空间,并占用大量的uop-cache空间.)
movemask指令采用__m256i并返回int32,其中每个位(取决于输入向量元素类型的前4位,8位或所有32位)是相应向量元素的最高有效位.
我想做反过来:取一个32(其中只有4,8或32个最低有效位有意义),并获得__m256i,其中每个int8,int32或int64大小的块的最高有效位设置为原始位.
基本上,我想从压缩的位掩码转到可被其他AVX2指令(例如maskstore,maskload,mask_gather)用作掩码的位掩码.
我无法快速找到这样做的指令,所以我在这里问.如果没有一条具有该功能的指令,您是否可以想到一个聪明的黑客,只需很少的指令即可实现这一点?
我目前的方法是使用256元素查找表.我想在一个没有其他事情发生的循环中使用这个操作来加速它.注意,我对长多指令序列或实现此操作的小循环不太感兴趣.
我没有特定的用例; 我问这是否真的是英特尔内在函数中的设计缺陷/限制,或者我是否只是遗漏了某些内容.
如果你想将标量浮点数与现有向量相结合,那么在没有高元素归零或使用英特尔内在函数将标量广播到向量中的情况下似乎没有办法实现.我没有研究过GNU C本机向量扩展和相关的内置函数.
如果额外的内在优化了,这不会太糟糕,但它不与gcc(5.4或6.2).也没有好的方法可以使用pmovzx或insertps作为负载,因为他们的内在函数只采用向量args的相关原因.(并且gcc不会将标量 - >向量加载到asm指令中.)
__m128 replace_lower_two_elements(__m128 v, float x) {
__m128 xv = _mm_set_ss(x); // WANTED: something else for this step, some compilers actually compile this to a separate insn
return _mm_shuffle_ps(v, xv, 0); // lower 2 elements are both x, and the garbage is gone
}
Run Code Online (Sandbox Code Playgroud)
gcc 5.3 -march = nehalem -O3输出,启用SSE4.1并调整该Intel CPU :(没有SSE4.1会更糟;多个指令将上层元素归零).
insertps xmm1, xmm1, 0xe # pointless zeroing of upper elements. shufps only reads the low element of xmm1 …Run Code Online (Sandbox Code Playgroud) 我想计算y = ax + b,其中x和y是一个像素值[即,值范围为0~255的字节],a而且b是浮点数
由于我需要对图像中的每个像素应用此公式,此外,a和b对于不同的像素是不同的.在C++中直接计算很慢,所以我很有兴趣知道c ++中的sse2指令.
搜索之后,我发现浮点数与sse2的乘法和加法一样_mm_mul_ps和_mm_add_ps.但首先,我需要将字节中的x转换为float(4字节).
问题是,在我从字节数据源(_mm_load_si128)加载数据后,如何将数据从byte转换为float?
是否存在强制 C 编译器直接使用内存操作数的语法?
在古老的汇编时代,我们只需在指令中写入获取操作数的位置 - “真实”寄存器或内存指针(地址指向的位置)。
但是在 CI 的内在函数伪 asm 中,没有看到强制编译器在指令中使用内存指针的方法(拒绝从内存(缓存)加载数据到“寄存器”,即垃圾寄存器文件将内容加载到缓存并导致重新加载惩罚)。
我知道程序员很容易简单地将“变量”操作数写入 instinsic 并让编译器决定是首先从内存加载还是直接使用它(如果可能)。
当前任务:我想计算 AVX2 CPU 上具有 512 字节寄存器文件(每个 32 字节的 16 ymm“寄存器”)的 8x8 8 位块序列的 SAD。因此它可以加载 8 个 8x8 8 位源块以完全填充可用的 AVX2 寄存器文件。
我想在所有寄存器文件中加载源块,并针对这些源块和每个引用位置仅测试内存中的不同“引用”位置。所以我想阻止CPU将引用块从缓存加载到寄存器文件并在悲伤指令中使用“内存操作数”。
使用asm我们只需写一些类似的东西
(load all 16 ymm registers with src)
vpsadbw ymm0, ymm0, [ref_base_address_register + some_offset...]
Run Code Online (Sandbox Code Playgroud)
但在具有内在的 C 文本中,它是
__m256i src = load_src(src_pointer);
__m256i ref = load_ref(ref_pointer);
__m256i sad_result= _mm256_sad_epu8(src, ref)
Run Code Online (Sandbox Code Playgroud)
它没有办法让编译器使用有效的内存操作数,例如
__m256i src = load_src(src_pointer);
__m256i sad_result= _mm256_sad_epu8(src, *ref_pointer)
Run Code Online (Sandbox Code Playgroud)
或者取决于“任务大小”,如果编译器用完可用寄存器,它将自动切换到内存操作数版本,程序员可以编写
__m256i sad_result=_mm256_sad_epu8(*(__m256i*)src_pointer, *(__m256i*)ref_pointer) …Run Code Online (Sandbox Code Playgroud)