C++ 找到一种有效的方法在2个巨大的缓冲区上执行最大值(字节/字节)
我需要非常快地比较900万字节,以保持每个字节的最大值。以下是我的工作: int bufSize=9000000; 字节t*buf=/*…*/; 字节_t*maxBuf=/*…*/; 对于int i=0;iC++ 找到一种有效的方法在2个巨大的缓冲区上执行最大值(字节/字节),c++,c,performance,numpy,max,C++,C,Performance,Numpy,Max,我需要非常快地比较900万字节,以保持每个字节的最大值。以下是我的工作: int bufSize=9000000; 字节t*buf=/*…*/; 字节_t*maxBuf=/*…*/; 对于int i=0;imaxBuf[i]{ maxBuf[i]=buf[i]; } } 它的工作,但我需要减少3处理时间 特别是,是否有一种方法可以使用64位CPU 你知道numpy阵列是否有帮助吗 编辑:处理器为四核ARM Cortex-A57,操作系统为Linux for Tegra。很抱歉,我以前应该写这篇文
编辑:处理器为四核ARM Cortex-A57,操作系统为Linux for Tegra。很抱歉,我以前应该写这篇文章。就你所拥有的而言,没有比这更快的方法了。使用python的numpy实际上只会改进python,从而提供类似C的行为 我认为你最好的选择是使用OpenMP。这是一个简单的教程。由于每个迭代都是相互独立的,我认为您的代码应该如下所示:
#pragma omp parallel for
for (int i = 0; i < bufSize; ++i) {
#pragma omp simd
if (buf[i] > maxBuf[i]) {
maxBuf[i] = buf[i];
}
}
然后使用-fopenmp编译。不过,我不确定pragma-omp-simd系列是否会有多大帮助
您还可以添加编译器优化。这是一份清单。另请参阅。但这些并不总能提高速度,这取决于很多因素。只要试一下,它就可以大大优化你的代码
例如,我有一个需要几个小时的算法。在进行了编译器优化和OpenMP之后,我能够将它缩短到30秒左右。但是,这个领域的编程会变得非常困难,有很多因素需要考虑。
就你所拥有的来说,没有更快的方法来完成它。使用python的numpy实际上只会改进python,从而提供类似C的行为
我认为你最好的选择是使用OpenMP。这是一个简单的教程。由于每个迭代都是相互独立的,我认为您的代码应该如下所示:#pragma omp parallel for
for (int i = 0; i < bufSize; ++i) {
#pragma omp simd
if (buf[i] > maxBuf[i]) {
maxBuf[i] = buf[i];
}
}
然后使用-fopenmp编译。不过,我不确定pragma-omp-simd系列是否会有多大帮助
您还可以添加编译器优化。这是一份清单。另请参阅。但这些并不总能提高速度,这取决于很多因素。只要试一下,它就可以大大优化你的代码
例如,我有一个需要几个小时的算法。在进行了编译器优化和OpenMP之后,我能够将它缩短到30秒左右。但是,这个编程领域会变得非常困难,有很多因素需要考虑。 < P>你可以在我的系统上找到一个高效的解决方案[英特尔I5-8250U] ~45毫秒vs 1ms,如果你有一个AVX2能力的CPU,同时使用因特尔SIMD内含子处理32个字节。 因为9000000可以被32整除,所以您甚至不需要额外的循环来完成
// #include <immintrin.h>, also for g++ add `-mavx2`-flag
int bufSize = 9000000;
byte *buf = static_cast<byte *>(_mm_malloc(sizeof(*buf) * bufSize, 32));
byte *maxBuf = static_cast<byte *>(_mm_malloc(sizeof(*maxBuf) * bufSize, 32));
for (int i = 0; i < bufSize; ++i)
{
buf[i] = (byte) rand();
maxBuf[i] = (byte) rand();
}
for (int i = 0; i < bufSize; i += 32)
{
__m256i *buf_simd = (__m256i *) &buf[i];
__m256i *maxBuf_simd = (__m256i *) &maxBuf[i];
*maxBuf_simd = _mm256_max_epu8(*maxBuf_simd, *buf_simd);
}
_mm_free(buf);
_mm_free(maxBuf);
因为我没有你的数据,我用随机数据创建了两个数组。这里非常重要的一点是,它们是32字节对齐的
之后,在for循环的每次迭代中,我将32字节加载到向量寄存器中,并执行_mm256_max_epu8,它基本上将256bit划分为32字节的数据包,即所谓的压缩向量,并选取每个字节的最大值。可以通过上面的链接找到更详细的解释
如果您只有支持SSE2的cpu,则可以使用128位向量的_mm_max_epu8。如果您有支持AVX2的cpu,并使用Intels SIMD Intrinsic一次性处理32个字节,则可以在我的系统[Intel i5-8250U]上获得高效的解决方案 因为9000000可以被32整除,所以您甚至不需要额外的循环来完成
// #include <immintrin.h>, also for g++ add `-mavx2`-flag
int bufSize = 9000000;
byte *buf = static_cast<byte *>(_mm_malloc(sizeof(*buf) * bufSize, 32));
byte *maxBuf = static_cast<byte *>(_mm_malloc(sizeof(*maxBuf) * bufSize, 32));
for (int i = 0; i < bufSize; ++i)
{
buf[i] = (byte) rand();
maxBuf[i] = (byte) rand();
}
for (int i = 0; i < bufSize; i += 32)
{
__m256i *buf_simd = (__m256i *) &buf[i];
__m256i *maxBuf_simd = (__m256i *) &maxBuf[i];
*maxBuf_simd = _mm256_max_epu8(*maxBuf_simd, *buf_simd);
}
_mm_free(buf);
_mm_free(maxBuf);
因为我没有你的数据,我用随机数据创建了两个数组。这里非常重要的一点是,它们是32字节对齐的
之后,在for循环的每次迭代中,我将32字节加载到向量寄存器中,并执行_mm256_max_epu8,它基本上将256bit划分为32字节的数据包,即所谓的压缩向量,并选取每个字节的最大值。可以通过上面的链接找到更详细的解释
如果您只有支持SSE2的cpu,则可以使用128位向量的_mm_max_epu8。暂时指出显而易见的问题。您的代码有选择地修改maxBuf中的数据,这导致矢量器失败。只需将代码改为使用std::max 对于int i=0;i
.LBB0_12: # =>This Inner Loop Header: Depth=1
vmovdqu ymm0, ymmword ptr [rsi + rax]
vmovdqu ymm1, ymmword ptr [rsi + rax + 32]
vmovdqu ymm2, ymmword ptr [rsi + rax + 64]
vmovdqu ymm3, ymmword ptr [rsi + rax + 96]
vpmaxub ymm0, ymm0, ymmword ptr [rdi + rax]
vpmaxub ymm1, ymm1, ymmword ptr [rdi + rax + 32]
vmovdqu ymmword ptr [rsi + rax], ymm0
vmovdqu ymmword ptr [rsi + rax + 32], ymm1
vpmaxub ymm0, ymm2, ymmword ptr [rdi + rax + 64]
vpmaxub ymm1, ymm3, ymmword ptr [rdi + rax + 96]
vmovdqu ymmword ptr [rsi + rax + 64], ymm0
vmovdqu ymmword ptr [rsi + rax + 96], ymm1
vmovdqu ymm0, ymmword ptr [rsi + rax + 128]
vmovdqu ymm1, ymmword ptr [rsi + rax + 160]
vpmaxub ymm0, ymm0, ymmword ptr [rdi + rax + 128]
vpmaxub ymm1, ymm1, ymmword ptr [rdi + rax + 160]
vmovdqu ymmword ptr [rsi + rax + 128], ymm0
vmovdqu ymmword ptr [rsi + rax + 160], ymm1
vmovdqu ymm0, ymmword ptr [rsi + rax + 192]
vmovdqu ymm1, ymmword ptr [rsi + rax + 224]
vpmaxub ymm0, ymm0, ymmword ptr [rdi + rax + 192]
vpmaxub ymm1, ymm1, ymmword ptr [rdi + rax + 224]
vmovdqu ymmword ptr [rsi + rax + 192], ymm0
vmovdqu ymmword ptr [rsi + rax + 224], ymm1
add rax, 256
add rdx, 4
jne .LBB0_12
暂时指出显而易见的问题。您的代码有选择地修改maxBuf中的数据,这导致矢量器失败。只需将代码改为使用std::max 对于int i=0;i
.LBB0_12: # =>This Inner Loop Header: Depth=1
vmovdqu ymm0, ymmword ptr [rsi + rax]
vmovdqu ymm1, ymmword ptr [rsi + rax + 32]
vmovdqu ymm2, ymmword ptr [rsi + rax + 64]
vmovdqu ymm3, ymmword ptr [rsi + rax + 96]
vpmaxub ymm0, ymm0, ymmword ptr [rdi + rax]
vpmaxub ymm1, ymm1, ymmword ptr [rdi + rax + 32]
vmovdqu ymmword ptr [rsi + rax], ymm0
vmovdqu ymmword ptr [rsi + rax + 32], ymm1
vpmaxub ymm0, ymm2, ymmword ptr [rdi + rax + 64]
vpmaxub ymm1, ymm3, ymmword ptr [rdi + rax + 96]
vmovdqu ymmword ptr [rsi + rax + 64], ymm0
vmovdqu ymmword ptr [rsi + rax + 96], ymm1
vmovdqu ymm0, ymmword ptr [rsi + rax + 128]
vmovdqu ymm1, ymmword ptr [rsi + rax + 160]
vpmaxub ymm0, ymm0, ymmword ptr [rdi + rax + 128]
vpmaxub ymm1, ymm1, ymmword ptr [rdi + rax + 160]
vmovdqu ymmword ptr [rsi + rax + 128], ymm0
vmovdqu ymmword ptr [rsi + rax + 160], ymm1
vmovdqu ymm0, ymmword ptr [rsi + rax + 192]
vmovdqu ymm1, ymmword ptr [rsi + rax + 224]
vpmaxub ymm0, ymm0, ymmword ptr [rdi + rax + 192]
vpmaxub ymm1, ymm1, ymmword ptr [rdi + rax + 224]
vmovdqu ymmword ptr [rsi + rax + 192], ymm0
vmovdqu ymmword ptr [rsi + rax + 224], ymm1
add rax, 256
add rdx, 4
jne .LBB0_12
多亏了@Frederik,我们找到了执行这些操作的方法 他在手臂上使用霓虹灯 代码如下: 包括 在vmax_u8上: 在vst1_u8上: 另见:
感谢@Frederik,我们发现了如何使用手臂上的霓虹灯执行这些操作 代码如下: 包括 在vmax_u8上: 在vst1_u8上: 另见:
我不知道64位能有什么帮助。您可以拆分缓冲区并在多线程中处理它。要尝试的东西是maxBuf[i]=buf[i]>maxBuf[i]?buf[i]:maxBuf[i],如果它说服编译器实现它的分支更少,这可能会有所帮助。64位解决方案将更为复杂。最具性能的解决方案将使用特定于平台的向量指令,如SSE。您应该显示所使用的编译器选项,并指出所使用的CPU和操作系统。稍微展开循环,以便在处理循环上花费更少的时间,在比较上花费更多的时间。例如,对于在循环中检查的四个元素(而不是一个),首先要确定有多少个4的集合,其余的要在另一个循环中处理。我不确定64位有什么帮助。您可以拆分缓冲区并在多线程中处理它。要尝试的东西是maxBuf[i]=buf[i]>maxBuf[i]?buf[i]:maxBuf[i],如果它说服编译器实现它的分支更少,这可能会有所帮助。64位解决方案将更为复杂。最具性能的解决方案将使用特定于平台的向量指令,如SSE。您应该显示所使用的编译器选项,并指出所使用的CPU和操作系统。稍微展开循环,以便在处理循环上花费更少的时间,在比较上花费更多的时间。例如,对于在循环中检查的四个元素而不是一个元素,首先找出有多少组4个元素,其余的元素将在另一个循环中处理。您希望simd带有For循环,而不是在内部。这些不是有效地做同样的事情吗?您希望simd带有For循环,不是在里面。这些不是有效地做同样的事情吗?谢谢!你认为你的解决方案对ARM有效吗?此代码在四核ARM Cortex-A57上运行,操作系统为Linux for Tegra。@不,抱歉,此特定示例在ARM上不起作用。然而,ARM处理器有自己的向量扩展,称为NEON。您可以尝试robthebloke的解决方案,为您的CPU提供特定的调优参数,以尝试使用NEON矢量。摆弄一下门闩可能会有帮助。谢谢!你认为你的解决方案对ARM有效吗?此代码在四核ARM Cortex-A57上运行,操作系统为Linux for Tegra。@不,抱歉,此特定示例在ARM上不起作用。然而,ARM处理器有自己的向量扩展,称为NEON。您可以尝试robthebloke的解决方案,为您的CPU提供特定的调优参数,以尝试使用NEON矢量。摆弄一下门闩可能会有帮助。非常感谢。我以前应该写过,但我的代码运行在四核ARM Cortex-A57上,操作系统是Linux for Tegra。使用std::max比使用17ms更有效。我需要在6毫秒内完成。你认为我错过了编译器的参数吗?非常感谢。我以前应该写过,但我的代码运行在四核ARM Cortex-A57上,操作系统是Linux for Tegra。使用std::max比使用17ms更有效。我需要在6毫秒内完成。你认为我错过了编译器的参数吗?