2 个数组/图像相乘的多线程性能 - Intel IPP
Multi Threading Performance in Multiplication of 2 Arrays / Images - Intel IPP
我正在使用 Intel 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;
}
结果是一样的,还是没有性能提升。
有没有办法在这种任务中从多线程中获益?
我如何验证任务是否受内存限制,因此并行化它没有好处?
使用 AVX 并行化在 CPU 上乘以 2 个数组的任务是否有好处?
我试过的电脑是基于 Core i7 4770k (Haswell) 的。
这里是link到Project in Visual Studio 2013。
谢谢。
根据我自己的一些研究,您的总 CPU 缓存似乎约为 8MB。 6000*4/4(6000 个浮点数分成 4 个块)是 6MB。将其乘以 2(进和出),您就在缓存之外。
我还没有对此进行测试,但是增加块的数量应该会提高性能。尝试从 8 开始(您的 CPU 将超线程移植到 8 个虚拟内核)。
目前,在 OpenMP 上产生的每个不同进程都存在缓存冲突并且必须从主内存(重新)加载。减小块的大小可以帮助解决这个问题。拥有不同的缓存会有效地增加缓存的大小,但这似乎不是一个选项。
如果您这样做只是为了证明原理,您可能需要 运行 在您的显卡上进行测试。虽然,这可能更难正确实施。
如果您 运行 启用了超线程,您应该尝试每个内核 1 个线程的 ipp openmp 版本,如果 ipp 没有自动执行,则设置 omp_places=cores。如果您使用 Cilk_ipp,请尝试改变 cilk_workers。
您可以尝试一个大到足以跨越多个 4kb 页面的测试用例。然后其他因素开始发挥作用。理想情况下,ipp 将使线程在不同的页面上工作。在 Linux(或 Mac?)透明大页面应该启动。在 Windows,haswell CPU 引入了硬件页面预取,它应该减少但不会消除 thp 的重要性。
您的图片总共占用 200 MB(2 x 5000 x 5000 x 4 字节)。因此,每个块包含 50 MB 的数据。这是 CPU 的 L3 缓存大小的 6 倍多(参见 here). Each AVX vector multiplication operates on 256 bits of data, which is half a cache line, i.e. it consumes one cache line per vector instruction (half a cache line for each argument). A vectorised multiplication on Haswell has a latency of 5 cycles and the FPU can retire two such instructions per cycle (see here)。 i7-4770K 的内存总线额定为 25.6 GB/s(理论 最大值!)或每秒不超过 4.3 亿缓存行。 CPU 的标称速度为 3.5 GHz。 AVX 部分的时钟频率较低,假设为 3.1 GHz。以这种速度,每秒需要多一个数量级的缓存行才能完全满足 AVX 引擎的需求。
在这些情况下,矢量化代码的单个线程几乎完全饱和了 CPU 的内存总线。添加第二个线程可能会导致非常轻微的改进。添加更多线程只会导致争用和增加开销。加速这种计算的唯一方法是增加内存带宽:
- 运行 在具有更多内存控制器的 NUMA 系统上,因此具有更高的总内存带宽,例如多路服务器主板;
- 切换到具有更高内存带宽的不同架构,例如Intel Xeon Phi 或 GPGPU。
我正在使用 Intel 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;
}
结果是一样的,还是没有性能提升。
有没有办法在这种任务中从多线程中获益?
我如何验证任务是否受内存限制,因此并行化它没有好处?
使用 AVX 并行化在 CPU 上乘以 2 个数组的任务是否有好处?
我试过的电脑是基于 Core i7 4770k (Haswell) 的。
这里是link到Project in Visual Studio 2013。
谢谢。
根据我自己的一些研究,您的总 CPU 缓存似乎约为 8MB。 6000*4/4(6000 个浮点数分成 4 个块)是 6MB。将其乘以 2(进和出),您就在缓存之外。
我还没有对此进行测试,但是增加块的数量应该会提高性能。尝试从 8 开始(您的 CPU 将超线程移植到 8 个虚拟内核)。
目前,在 OpenMP 上产生的每个不同进程都存在缓存冲突并且必须从主内存(重新)加载。减小块的大小可以帮助解决这个问题。拥有不同的缓存会有效地增加缓存的大小,但这似乎不是一个选项。
如果您这样做只是为了证明原理,您可能需要 运行 在您的显卡上进行测试。虽然,这可能更难正确实施。
如果您 运行 启用了超线程,您应该尝试每个内核 1 个线程的 ipp openmp 版本,如果 ipp 没有自动执行,则设置 omp_places=cores。如果您使用 Cilk_ipp,请尝试改变 cilk_workers。 您可以尝试一个大到足以跨越多个 4kb 页面的测试用例。然后其他因素开始发挥作用。理想情况下,ipp 将使线程在不同的页面上工作。在 Linux(或 Mac?)透明大页面应该启动。在 Windows,haswell CPU 引入了硬件页面预取,它应该减少但不会消除 thp 的重要性。
您的图片总共占用 200 MB(2 x 5000 x 5000 x 4 字节)。因此,每个块包含 50 MB 的数据。这是 CPU 的 L3 缓存大小的 6 倍多(参见 here). Each AVX vector multiplication operates on 256 bits of data, which is half a cache line, i.e. it consumes one cache line per vector instruction (half a cache line for each argument). A vectorised multiplication on Haswell has a latency of 5 cycles and the FPU can retire two such instructions per cycle (see here)。 i7-4770K 的内存总线额定为 25.6 GB/s(理论 最大值!)或每秒不超过 4.3 亿缓存行。 CPU 的标称速度为 3.5 GHz。 AVX 部分的时钟频率较低,假设为 3.1 GHz。以这种速度,每秒需要多一个数量级的缓存行才能完全满足 AVX 引擎的需求。
在这些情况下,矢量化代码的单个线程几乎完全饱和了 CPU 的内存总线。添加第二个线程可能会导致非常轻微的改进。添加更多线程只会导致争用和增加开销。加速这种计算的唯一方法是增加内存带宽:
- 运行 在具有更多内存控制器的 NUMA 系统上,因此具有更高的总内存带宽,例如多路服务器主板;
- 切换到具有更高内存带宽的不同架构,例如Intel Xeon Phi 或 GPGPU。