在最后几天,我观察到我的新工作站的行为,我无法解释.对此问题进行一些研究,INTEL Haswell架构以及当前的Skylake Generation 可能存在一个错误.
在编写可能的错误之前,让我先概述一下使用的硬件,程序代码和问题本身.
我目前正在运行Ubuntu 15.04 64位桌面版,安装最新更新和内核.除了使用这台机器开发CUDA内核和东西,我最近测试了一个纯C程序.该程序正在对相当大的输入数据集进行改进的ART.因此代码执行一些FFT并耗费相当长的时间来完成计算.我目前无法发布/链接到任何源代码,因为这是正在进行的无法发布的研究.如果您不熟悉ART,只需简单解释它的作用.ART是一种用于重建从计算机断层摄影机接收的数据以获得用于诊断的可见图像的技术.因此,我们的代码版本重建了大小为2048x2048x512的数据集.到目前为止,没有什么特别的,也没有涉及火箭科学.经过几个小时的调试和修复错误后,代码在参考结果上进行了测试,我们可以确认代码是否正常工作.代码使用的唯一库是标准的math.h.没有特殊的编译参数,没有额外的库可能带来额外的问题.
该代码使用一种技术来实现ART,以最小化重建数据所需的投影.因此,我们假设我们可以重建一个涉及25个投影的数据片.代码以12个内核上完全相同的输入数据启动.请注意,实现不是基于多线程,目前启动了12个程序实例.我知道这不是最好的方法,涉及正确的线程管理是强烈建议,这已经在改进列表:)
因此,当我们运行至少两个程序实例(每个实例在一个单独的数据切片上工作)时,结果是一些预测是随机的错误.为了让您了解结果,请参阅表1.请注意,输入数据始终相同.
只运行一个涉及CPU核心的代码实例,结果都是正确的.即使执行一些涉及一个CPU内核的运行,结果仍然是正确的.仅涉及至少两个或更多核心会生成结果模式,如表1所示.
好吧,花了相当长的时间来了解究竟出了什么问题.所以我们完成了整个代码,大多数问题都是以一个小的实现错误开始的.但是,嗯,没有(当然我们不能证明没有错误也不能保证它).为验证我们的代码,我们使用了两台不同的机器:
出人意料的是,这两个MACHINE1和机器2产生总是正确的结果.即使使用所有CPU核心,结果仍然是正确的.每台机器上超过50次运行甚至没有错误的结果.代码是在没有优化选项或任何特定编译器设置的每台目标机器上编译的.因此,阅读新闻导致以下发现:
因此,在Prime95和Mersenne社区的人们 似乎是第一批发现和识别这个讨厌的bug的人.引用的帖子和新闻支持怀疑,这个问题只存在于繁重的工作量下.根据我的观察,我可以确认这种行为.
我在 Intel i3-N305 3.8GHz 和 AMD Ryzen 7 3800X 3.9GHz PC 上运行了使用 gcc-13 ( https://godbolt.org/z/qq5WrE8qx )编译的相同二进制文件。此代码使用 VCL 库(https://github.com/vectorclass/version2):
int loop_vc_nested(const array<uint8_t, H*W> &img, const array<Vec32uc, 8> &idx) {
int sum = 0;
Vec32uc vMax, iMax, vCurr, iCurr;
for (int i=0; i<H*W; i+=W) {
iMax.load(&idx[0]);
vMax.load(&img[i]);
for (int j=1; j<8; j++) {
iCurr.load(&idx[j]);
vCurr.load(&img[i+j*32]);
iMax = select(vCurr > vMax, iCurr, iMax);
vMax = max(vMax, vCurr);
}
Vec32uc vMaxAll{horizontal_max(vMax)};
sum += iMax[horizontal_find_first(vMax == vMaxAll)];
}
return sum;
}
Run Code Online (Sandbox Code Playgroud)
完整的基准源位于:https://github.com/pauljurczak/simd-benchmarks/blob/main/main-5-vcl-eve.cpp …
如果你有一个输入数组和一个输出数组,但是你只想写那些通过某个条件的元素,那么在AVX2中这样做最有效的方法是什么?
我在SSE看到它是这样做的:(来自:https://deplinenoise.files.wordpress.com/2015/03/gdc2015_afredriksson_simd.pdf)
__m128i LeftPack_SSSE3(__m128 mask, __m128 val)
{
// Move 4 sign bits of mask to 4-bit integer value.
int mask = _mm_movemask_ps(mask);
// Select shuffle control data
__m128i shuf_ctrl = _mm_load_si128(&shufmasks[mask]);
// Permute to move valid values to front of SIMD register
__m128i packed = _mm_shuffle_epi8(_mm_castps_si128(val), shuf_ctrl);
return packed;
}
Run Code Online (Sandbox Code Playgroud)
这对于4宽的SSE来说似乎很好,因此只需要16个入口LUT,但对于8宽的AVX,LUT变得非常大(256个条目,每个32个字节或8k).
我很惊讶AVX似乎没有简化此过程的指令,例如带有打包的蒙版存储.
我想通过稍微改变来计算左边设置的符号位数,你可以生成必要的排列表,然后调用_mm256_permutevar8x32_ps.但这也是我认为的一些指示......
有没有人知道用AVX2做这个的任何技巧?或者什么是最有效的方法?
以下是上述文件中左包装问题的说明:
谢谢
我一直在研究使用AVX2指令集的新收集指令.具体来说,我决定对一个简单问题进行基准测试,其中一个浮点数组被置换并添加到另一个浮点数组中.在c中,这可以实现为
void vectortest(double * a,double * b,unsigned int * ind,unsigned int N)
{
int i;
for(i=0;i<N;++i)
{
a[i]+=b[ind[i]];
}
}
Run Code Online (Sandbox Code Playgroud)
我用g ++ -O3 -march = native编译这个函数.现在,我以三种方式在汇编中实现它.为简单起见,我假设数组N的长度可以被4整除.简单的非矢量化实现:
align 4
global vectortest_asm
vectortest_asm:
;; double * a = rdi
;; double * b = rsi
;; unsigned int * ind = rdx
;; unsigned int N = rcx
push rax
xor rax,rax
loop: sub rcx, 1
mov eax, [rdx+rcx*4] ;eax = ind[rcx]
vmovq xmm0, [rdi+rcx*8] ;xmm0 = a[rcx]
vaddsd xmm0, [rsi+rax*8] ;xmm1 …Run Code Online (Sandbox Code Playgroud) 假设我正在使用AVX2的VGATHERDPS - 这应该使用8个DWORD索引加载8个单精度浮点数.
当要加载的数据存在于不同的缓存行中时会发生什么?指令是作为硬件循环实现的,它逐个获取缓存行吗?或者,它是否可以立即向多个缓存行发出负载?
我读了几篇论述前者的文章(这是对我更有意义的文章),但我想更多地了解这一点.
链接到一篇论文:http://arxiv.org/pdf/1401.7494.pdf
内在的:
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之外的位.
我在SUSE Linux Enterprise 10/11计算机上.我将回归发送到运行英特尔处理器的计算机的农场.我的一些测试失败是因为我的工具是使用需要AVX/AVX2指令支持的库构建的.我收到一个Illegal exception错误.
在Linux中,是否有任何命令可用于确定什么是CPU代码/系列名称?
我相信AVX和AVX2分别可以从Intel SandyBridge和Haswell系列开始.
我正在尝试使用AVX -AVX2指令集来查看连续阵列上的流媒体性能.所以我有下面的例子,我做基本的内存读取和存储.
#include <iostream>
#include <string.h>
#include <immintrin.h>
#include <chrono>
const uint64_t BENCHMARK_SIZE = 5000;
typedef struct alignas(32) data_t {
double a[BENCHMARK_SIZE];
double c[BENCHMARK_SIZE];
alignas(32) double b[BENCHMARK_SIZE];
}
data;
int main() {
data myData;
memset(&myData, 0, sizeof(data_t));
auto start = std::chrono::high_resolution_clock::now();
for (auto i = 0; i < std::micro::den; i++) {
for (uint64_t i = 0; i < BENCHMARK_SIZE; i += 1) {
myData.b[i] = myData.a[i] + 1;
}
}
auto end = std::chrono::high_resolution_clock::now();
std::cout << (end - start).count() / …Run Code Online (Sandbox Code Playgroud) 我正在尝试编写非常有效的汉明距离代码.受WojciechMuła非常聪明的SSE3 popcount 实现的启发,我编写了一个AVX2等效解决方案,这次使用256位寄存器. 基于所涉及的操作的双倍并行性,我预计至少会有30%-40%的改进,但令我惊讶的是,AVX2代码稍慢(约2%)!
有人可以告诉我可能的原因,为什么我没有获得预期的性能提升?
展开,两个64字节块的SSE3汉明距离:
INT32 SSE_PopCount(const UINT32* __restrict pA, const UINT32* __restrict pB) {
__m128i paccum = _mm_setzero_si128();
__m128i a = _mm_loadu_si128 (reinterpret_cast<const __m128i*>(pA));
__m128i b = _mm_loadu_si128 (reinterpret_cast<const __m128i*>(pB));
__m128i err = _mm_xor_si128 (a, b);
__m128i lo = _mm_and_si128 (err, low_mask);
__m128i hi = _mm_srli_epi16 (err, 4);
hi = _mm_and_si128 (hi, low_mask);
__m128i popcnt1 = _mm_shuffle_epi8(lookup, lo);
__m128i popcnt2 = _mm_shuffle_epi8(lookup, hi);
paccum = _mm_add_epi8(paccum, popcnt1);
paccum = _mm_add_epi8(paccum, popcnt2);
a = _mm_loadu_si128 (reinterpret_cast<const …Run Code Online (Sandbox Code Playgroud) 我想矢量化两个内存对齐数组的乘法.我没有找到任何方法在AVX/AVX2中乘以64*64位,所以我只是循环展开和AVX2加载/存储.有更快的方法吗?
注意:我不想保存每次乘法的高半结果.
void multiply_vex(long *Gi_vec, long q, long *Gj_vec){
int i;
__m256i data_j, data_i;
__uint64_t *ptr_J = (__uint64_t*)&data_j;
__uint64_t *ptr_I = (__uint64_t*)&data_i;
for (i=0; i<BASE_VEX_STOP; i+=4) {
data_i = _mm256_load_si256((__m256i*)&Gi_vec[i]);
data_j = _mm256_load_si256((__m256i*)&Gj_vec[i]);
ptr_I[0] -= ptr_J[0] * q;
ptr_I[1] -= ptr_J[1] * q;
ptr_I[2] -= ptr_J[2] * q;
ptr_I[3] -= ptr_J[3] * q;
_mm256_store_si256((__m256i*)&Gi_vec[i], data_i);
}
for (; i<BASE_DIMENSION; i++)
Gi_vec[i] -= Gj_vec[i] * q;
}
Run Code Online (Sandbox Code Playgroud)
更新:
我正在使用Haswell微体系结构和ICC/GCC编译器.所以AVX和AVX2都很好.我在乘法循环展开后-=用C inrisic 替换_mm256_sub_epi64它,在那里得到一些加速.目前,它是ptr_J[0] *= q; ...
我用, …