内在的:
int mask = _mm256_movemask_epi8(__m256i s1)
Run Code Online (Sandbox Code Playgroud)
创建一个掩码,其32位对应于每个字节的最高位s1.在使用位操作(BMI2例如)操作掩码之后,我想执行反转_mm256_movemask_epi8,即创建一个__m256i向量,每个字节的最高有效位包含相应的位uint32_t mask.
做这个的最好方式是什么?
编辑:我需要执行逆操作,因为内在函数_mm256_blendv_epi8只接受__m256i类型掩码而不是uint32_t.因此,在结果__m256i掩码中,我可以忽略除每个字节的MSB之外的位.
我有8个bool变量,我想将它们"合并"成一个字节.
有一个简单/首选的方法来做到这一点?
相反,如何将一个字节解码为8个独立的布尔值?
我认为这不是一个不合理的问题,但由于我无法通过谷歌找到相关文档,它可能是另一个"非你所有直觉都是错误的"案例.
我试图在i7上以最有效的方式计算浮点数和位向量之间的点积.实际上,我在128或256维向量上进行此操作,但为了说明,让我编写64维代码来说明问题:
// a has 64 elements. b is a bitvector of 64 dimensions.
float dot(float *restrict a, uint64_t b) {
float sum = 0;
for(int i=0; b && i<64; i++, b>>=1) {
if (b & 1) sum += a[i];
}
return sum;
}
Run Code Online (Sandbox Code Playgroud)
当然,这是有效的,但问题是,这是整个程序的时间要点(占用50分钟运行的95%CPU时间)所以我迫切需要让它更快.
我的猜测是上面的分支是游戏杀手(防止无序执行,导致坏分支预测).我不确定矢量指令是否可以在这里使用和帮助.使用gcc 4.8和-std = c99 -march = native -mtune = native -Ofast -funroll-loops,我现在得到这个输出
movl $4660, %edx
movl $5, %ecx
xorps %xmm0, %xmm0
.p2align 4,,10
.p2align 3
.L4:
testb $1, %cl
je .L2
addss (%rdx), %xmm0
.L2:
leaq …Run Code Online (Sandbox Code Playgroud) (相关:如何在Sandy Bridge上的一系列int中快速将位计数到单独的bin中?是对此的早期复制,带有一些不同的答案。编者注:这里的答案可能更好。
同样,是类似问题的AVX2版本,整行位的许多bin比一个宽得多uint64_t:改进列填充计数算法)
我正在C中的一个项目中,我需要经历数千万个掩码(ulong类型(64位)),并target基于一个简单规则更新64个短整数(uint16)的数组(称为):
// for any given mask, do the following loop
for (i = 0; i < 64; i++) {
if (mask & (1ull << i)) {
target[i]++
}
}
Run Code Online (Sandbox Code Playgroud)
问题是我需要在数以百万计的蒙版上执行上述循环,并且我需要在不到一秒钟的时间内完成。想知道是否有任何方法可以加快它的速度,例如使用某种表示上述循环的特殊汇编指令。
目前,我在ubuntu 14.04(i7-2670QM,支持AVX,而不是AVX2)上使用gcc 4.8.4来编译和运行以下代码,大约需要2秒钟。很想让它在200ms以下运行。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/stat.h>
double getTS() {
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec + tv.tv_usec / 1000000.0;
}
unsigned int target[64];
int main(int argc, char *argv[]) {
int i, …Run Code Online (Sandbox Code Playgroud) gcc 5.3 with -O3 -mavx -mtune=haswellfor x86-64使得代码处理可能错位的代码变得非常笨重,例如:
// convenient simple example of compiler input
// I'm not actually interested in this for any real program
void floatmul(float *a) {
for (int i=0; i<1024 ; i++)
a[i] *= 2;
}
Run Code Online (Sandbox Code Playgroud)
clang使用未对齐的加载/存储指令,但是gcc执行标量intro/outro和对齐的向量循环:它剥离了第一个最多7个未对齐的迭代,将其完全展开为一个序列
vmovss xmm0, DWORD PTR [rdi]
vaddss xmm0, xmm0, xmm0 ; multiply by two
vmovss DWORD PTR [rdi], xmm0
cmp eax, 1
je .L13
vmovss xmm0, DWORD PTR [rdi+4]
vaddss xmm0, xmm0, xmm0
vmovss …Run Code Online (Sandbox Code Playgroud) 将32位存储在uint32_t内存中,将每个位解压缩到AVX寄存器的单独字节元素的最快方法是什么?这些位可以位于各自字节内的任何位置.
编辑:澄清,我的意思是位0进入字节0,位1到字节1.显然,字节内的所有其他位都为零.我现在最好的是2 PSHUFB并且每个位置都有一个掩码寄存器.
如果uint32_t是位图,则相应的向量元素应为0或非0.(即我们可以得到一个矢量掩码,其中vpcmpeqb一个矢量为全零的矢量).
我有一个循环将两个float*数组加载到__m256向量中并处理它们。在这个循环之后,我有代码将平衡值加载到向量中,然后处理它们。所以函数上没有对齐要求。
以下是将数据余额加载到向量中的代码:
size_t constexpr FLOATS_IN_M128 = sizeof(__m128) / sizeof(float);
size_t constexpr FLOATS_IN_M256 = FLOATS_IN_M128 * 2;
Run Code Online (Sandbox Code Playgroud)
...
assert(bal < FLOATS_IN_M256);
float ary[FLOATS_IN_M256 * 2];
auto v256f_q = _mm256_setzero_ps();
_mm256_storeu_ps(ary, v256f_q);
_mm256_storeu_ps(&ary[FLOATS_IN_M256], v256f_q);
float *dest = ary;
size_t offset{};
while (bal--)
{
dest[offset] = p_q_n[pos];
dest[offset + FLOATS_IN_M256] = p_val_n[pos];
offset++;
pos++;
}
// the two vectors that will be processed
v256f_q = _mm256_loadu_ps(ary);
v256f_val = _mm256_loadu_ps(&ary[FLOATS_IN_M256]);
Run Code Online (Sandbox Code Playgroud)
assert(bal < FLOATS_IN_M256);当我使用编译器资源管理器时,设置为“x86-64 clang 16.0.0 -march=x86-64-v3 -O3”,编译器会在该行存在时展开循环。但是,assert()在模式中被忽略 …
这篇文章是关于_mm_add_epi32 的 Golang 汇编实现,它在两个[8]int32列表中添加成对的元素,并返回更新的第一个。
根据 pprof 配置文件,我发现传递[8]int32是昂贵的,所以我认为传递列表的指针要便宜得多,而 bech 结果证实了这一点。这是 Go 版本:
func __mm_add_epi32_inplace_purego(x, y *[8]int32) {
(*x)[0] += (*y)[0]
(*x)[1] += (*y)[1]
(*x)[2] += (*y)[2]
(*x)[3] += (*y)[3]
(*x)[4] += (*y)[4]
(*x)[5] += (*y)[5]
(*x)[6] += (*y)[6]
(*x)[7] += (*y)[7]
}
Run Code Online (Sandbox Code Playgroud)
该函数在两级循环中调用。
该算法计算一个字节数组的位置人口计数。
感谢@fuz 的建议,我知道在汇编中编写整个算法是最好的选择并且是有意义的,但这超出了我的能力范围,因为我从未学习过汇编编程。
但是,使用装配优化内循环应该很容易:
counts := make([][8]int32, numRowBytes)
for i, b = range byteSlice {
if b == 0 { // more than half of elements in byteSlice is 0. …Run Code Online (Sandbox Code Playgroud) 我正在优化图像上的高斯模糊算法,我想用下面的代码替换__m256内部变量中浮点缓冲区[8]的用法.哪一系列说明最适合此任务?
// unsigned char *new_image is loaded with data
...
float buffer[8];
buffer[x ] = new_image[x];
buffer[x + 1] = new_image[x + 1];
buffer[x + 2] = new_image[x + 2];
buffer[x + 3] = new_image[x + 3];
buffer[x + 4] = new_image[x + 4];
buffer[x + 5] = new_image[x + 5];
buffer[x + 6] = new_image[x + 6];
buffer[x + 7] = new_image[x + 7];
// buffer is then used for further operations
...
//What I want instead in pseudocode: …Run Code Online (Sandbox Code Playgroud) uint8_t data[] = "mykeyxyz:1234\nky:123\n...";。我的字符串行有 format key:value,其中每一行都有len(key) <= 16保证。我想加载mykeyxyz到 a 中__m128i,但将较高的位置填为 0。
最简单的方法是使用 255 或 0 掩码的数组,但这需要另一个内存负载。有没有办法更快地做到这一点?
接受的答案使总程序时间加快了约 2%。要进行比较,请进行测试1brc_valid13.cpp(1brc_valid14.cpp使用已接受的答案)。硬件:AMD 2950X、Ubuntu 18.04、g++ 11.4,编译命令:g++ -o main 1brc_final_valid.cpp -O3 -std=c++17 -march=native -m64 -lpthread
编辑:最好没有 AVX512
编辑 2:我需要变量,len以便我可以开始解析值部分。
编辑 3:该函数将在循环中使用(例如解析 100 万行文本)。但strcmp_mask基本上总是在 L1 缓存内
编辑 4:我通过解析 10 亿行(key,value)并处理它们来对函数进行基准测试。您可以下载代码/数据并在我的存储库中复制结果: https: //github.com/lehuyduc/1brc-simd。此外,讨论帖将包含更多信息
编辑 5:我测试maskafterc256发现它导致我的代码慢了 50 倍!如果我替换_mm256_set_epi8为 …