2个数组/图像相乘的多线程性能-英特尔IPP



我正在使用"英特尔IPP"对2个图像(数组)进行乘法运算
我使用的是"英特尔IPP 8.2",它是"英特尔作曲家2015更新6"附带的。

我创建了一个简单的函数来乘以过大的图像(整个项目附在附件中,见下文)
我想看看使用"英特尔IPP多线程库"的好处。

这是一个简单的项目(我还附上了Visual Studio的完整项目表单):

#include "ippi.h"
#include "ippcore.h"
#include "ipps.h"
#include "ippcv.h"
#include "ippcc.h"
#include "ippvm.h"
#include <ctime>
#include <iostream>
using namespace std;
const int height = 6000;
const int width  = 6000;
Ipp32f mInput_image [1 * width * height];
Ipp32f mOutput_image[1 * width * height] = {0};
int main()
{
    IppiSize size = {width, height};
    double start = clock();
    for (int i = 0; i < 200; i++)
        ippiMul_32f_C1R(mInput_image, 6000 * 4, mInput_image, 6000 * 4, mOutput_image, 6000 * 4, size); 
    double end = clock();
    double douration = (end - start) / static_cast<double>(CLOCKS_PER_SEC);
    cout << douration << endl;
    cin.get();
    return 0;
}

我曾使用"英特尔IPP单线程"one_answers"英特尔IPP多线程"编译过一次此项目。

我尝试了不同大小的数组,但在所有这些数组中,多线程版本都没有取得任何效果(有时甚至更慢)。

我想知道,为什么在这个多线程的任务中没有收获
我知道"英特尔IPP"使用的是AVX,我想这项任务可能会变成"内存受限"?

我尝试了另一种方法,手动使用OpenMP,使用"英特尔IPP单线程"实现多线程方法
这是代码:

#include "ippi.h"
#include "ippcore.h"
#include "ipps.h"
#include "ippcv.h"
#include "ippcc.h"
#include "ippvm.h"
#include <ctime>
#include <iostream>
using namespace std;
#include <omp.h>
const int height = 5000;
const int width  = 5000;
Ipp32f mInput_image [1 * width * height];
Ipp32f mOutput_image[1 * width * height] = {0};
int main()
{
    IppiSize size = {width, height};
    double start = clock();
    IppiSize blockSize = {width, height / 4};
    const int NUM_BLOCK = 4;
    omp_set_num_threads(NUM_BLOCK);
    Ipp32f*  in;
    Ipp32f*  out;
    //  ippiMul_32f_C1R(mInput_image, width * 4, mInput_image, width * 4, mOutput_image, width * 4, size);
    #pragma omp parallel            
    shared(mInput_image, mOutput_image, blockSize) 
    private(in, out)
    {
        int id   = omp_get_thread_num();
        int step = blockSize.width * blockSize.height * id;
        in       = mInput_image  + step;
        out      = mOutput_image + step;
        ippiMul_32f_C1R(in, width * 4, in, width * 4, out, width * 4, blockSize);
    }
    double end = clock();
    double douration = (end - start) / static_cast<double>(CLOCKS_PER_SEC);
    cout << douration << endl;
    cin.get();
    return 0;
}

结果是一样的,再一次,没有性能的提高。

在这种任务中,有没有办法从"多线程"中获益
我如何验证一个任务是否变得有内存限制,因此在并行化时没有任何好处?用AVX在CPU上并行化2个阵列的任务有好处吗?

我试过的电脑是基于酷睿i7 4770k(Haswell)。

以下是指向Visual Studio 2013中的项目的链接。

谢谢。

您的图像总共占用200 MB(2 x 5000 x 5000 x 4字节)。因此,每个块由50MB的数据组成。这是CPU三级缓存大小的6倍多(请参阅此处)。每个AVX矢量乘法运算256位数据,这是缓存行的一半,即每个矢量指令消耗一个缓存行(每个自变量消耗半个缓存行)。Haswell上的矢量乘法有5个周期的延迟,FPU每个周期可以引退两个这样的指令(见此处)。i7-4770K的内存总线额定为25.6 GB/s(理论上的最大值!)或每秒不超过4.3亿缓存线。CPU的标称速度为3.5 GHz。AVX部分的时钟频率稍低,比如说3.1 GHz。在这个速度下,每秒需要多出一个数量级的缓存线才能完全馈送AVX引擎。

在这种情况下,矢量化代码的单个线程几乎会使CPU的内存总线饱和。添加第二个线程可能会带来非常轻微的改进。添加更多的线程只会导致争用和增加开销。加快这种计算的唯一方法是增加内存带宽:

  • 在具有更多存储器控制器并因此具有更高聚合存储器带宽的NUMA系统上运行,例如多套接字服务器板
  • 切换到具有高得多的内存带宽的不同架构,例如Intel Xeon Phi或GPGPU

根据我自己的一些研究,您的总CPU缓存大约为8MB。6000*4/4(6000个浮子分成4块)为6MB。将其乘以2(输入和输出),则表示您在缓存之外。

我还没有测试过,但增加块的数量应该会提高性能。试着从8开始(你的CPU将超线程移植到8个虚拟内核)。

目前,OpenMP上产生的每个不同进程都存在缓存冲突,并且必须从主存中(重新)加载。减小块的大小可以帮助实现这一点。拥有不同的cahce会有效地增加缓存的大小,但这似乎不是一种选择。

如果你这样做只是为了证明原理,你可能想在你的显卡上运行它来测试一下。尽管如此,这可能更难正确实施。

如果您在启用超线程的情况下运行,您应该尝试每个内核有1个线程的ipp的openmp版本,如果ipp没有自动执行,则设置omp_places=cores。如果您使用Cilk_ipp,请尝试使用不同的Cilk_workers。您可能会尝试一个足够大的测试用例来跨越多个4kb的页面。然后,其他因素开始发挥作用。理想情况下,ipp会让线程在不同的页面上工作。在Linux(或Mac?)上,透明的巨大页面应该会出现。在Windows上,haswell CPU引入了硬件页面预取,这应该会降低但不会消除thp的重要性。

最新更新