【问题标题】:Multi Threading Performance in Multiplication of 2 Arrays / Images - Intel IPP2 个阵列/图像相乘中的多线程性能 - 英特尔 IPP
【发布时间】:2016-08-26 06:37:05
【问题描述】:

我正在使用英特尔 IPP 进行 2 个图像(数组)的乘法运算。
我正在使用 Intel Composer 2015 Update 6 附带的 Intel IPP 8.2。

我创建了一个简单的函数来放大太大的图像(整个项目附后,见下文)。
我想看看使用英特尔 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 单线程和英特尔 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;
}

结果是一样的,同样没有性能提升。

有没有办法在这种任务中从多线程中受益?
如何验证任务是否成为内存受限的,因此并行化它没有好处? 将 CPU 上的 2 个数组与 AVX 相乘的任务并行化是否有好处?

我试用的计算机基于 Core i7 4770k (Haswell)。

这是Project in Visual Studio 2013的链接。

谢谢。

【问题讨论】:

  • 你使用什么编译选项?您应该使用/O2 /openmp/O2 /openmp /arch:AVX2
  • 这并不重要,因为英特尔 IPP 是使用汇编和 CPU 调度构建的。但它是 /O2 /OpenMP。谢谢。

标签: c++ multithreading openmp intel-ipp


【解决方案1】:

您的图片总共占用 200 MB(2 x 5000 x 5000 x 4 字节)。因此,每个块包含 50 MB 的数据。这是 CPU 的 L3 缓存大小的 6 倍多(请参阅here)。每个 AVX 向量乘法对 256 位数据进行操作,即半个高速缓存行,即每条矢量指令消耗一个高速缓存行(每个参数半个高速缓存行)。 Haswell 上的向量化乘法具有 5 个周期的延迟,并且 FPU 可以在每个周期退出两条此类指令(请参阅here)。 i7-4770K 的内存总线额定速度为 25.6 GB/s(理论上最大值!)或每秒不超过 4.3 亿条缓存线。 CPU 的标称速度为 3.5 GHz。 AVX 部分的时钟频率略低,比如 3.1 GHz。以这样的速度,每秒需要多一个数量级的缓存行才能完全满足 AVX 引擎的需求。

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

  • 在具有更多内存控制器的 NUMA 系统上运行,因此总内存带宽更高,例如多路服务器主板;
  • 切换到具有更高内存带宽的不同架构,例如英特尔至强融核或 GPGPU。

【讨论】:

  • 嗨,但我们知道我们可以在多线程矩阵乘法上取得更好的成绩。这样做的策略是什么?此外,除了理论计算,VS中有没有工具可以告诉我内存是否饱和?谢谢你的回答。
  • 使用密集矩阵-矩阵乘法,每单位输出数据的计算量要多得多,并且每个输入元素都用于计算多个输出元素。这允许使用诸如缓存阻塞之类的技术。在您的情况下,数据基本上以线性方式流入和流出 CPU,并且无法使用缓存阻塞。
  • 我的意思是数组乘法(错误地写了矩阵)。另外,有没有分析这些东西的工具?
  • 如果您摆脱 IPP 并自己实现一个简单的乘法循环,然后在禁用所有优化的情况下以 32 位编译,您可能可以将其(某种程度)扩展到 2-3 个线程模式和 x87 数学,然后将 CPU 降频很多。至于分析,我已经给你介绍了理论上的分析。查阅 roofline 性能模型 了解更多详情。 CPU 具有可使用适当工具读取的性能计数器。我知道的唯一适用于 Windows 的是 Intel VTune(我通常在 Linux 上进行 HPC,并且有大量工具 - 其中许多是开源的 - 用于该操作系统)
  • 看来 V-Tune 就是我想要的。很好的分析,非常感谢!看来你对这个主题有很多了解。
【解决方案2】:

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

【讨论】:

  • 嗨,我不确定首先确定为什么没有收益。是内存瓶颈吗?如何验证?
【解决方案3】:

根据我自己的一些研究,您的总 CPU 缓存似乎在 8MB 左右。 6000*4/4(6000 个浮点数分成 4 个块)为 6MB。将此乘以 2(进出),您就在缓存之外。

我没有对此进行测试,但增加块数应该会提高性能。尝试 8 开始(您的 CPU 支持 8 个虚拟内核的超线程)。

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

如果您只是将其作为原理证明,您可能希望通过在显卡上运行它来测试它。虽然,这可能更难以正确实施。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多