任务非常简单,将整数变量写入内存:
原始代码:
for (size_t i=0; i<1000*1000*1000; ++i)
{
data[i]=i;
};并行化代码:
size_t stepsize=len/N;
#pragma omp parallel num_threads(N)
{
int threadIdx=omp_get_thread_num();
size_t istart=stepsize*threadIdx;
size_t iend=threadIdx==N-1?len:istart+stepsize;
#pragma simd
for (size_t i=istart; i<iend; ++i)
x[i]=i;
};性能很差,需要1.6秒来编写1G uint64变量(相当于每秒5GB ),通过简单的并行化(open mp parallel),可以提高速度,但是性能仍然很差,以1.4秒和i7 3970上的6个线程为例。
我的平台(i7 3970/64G DDR3-1600)的理论内存带宽为51.2GB/秒,对于上面的例子,实现的内存带宽仅为理论带宽的1/10,即使通过应用程序也是内存带宽受限的。
有人知道怎么改进代码吗?
我在GPU上写了很多内存绑定代码,GPU很容易充分利用GPU的设备内存带宽(例如,理论带宽的85%+ )。
编辑:
该代码由Intel ICC 13.1编译成64位二进制,并具有最大优化(O3)和AVX代码路径,以及自动矢量化。
更新:
我尝试了下面所有的代码(感谢Paul ),没有什么特别的事情发生,我相信编译器完全能够完成simd/矢量化优化。
至于我为什么要在那里填写数字呢,长话短说:
它是高性能异构计算算法的一部分,在设备端,算法效率很高,以至于多GPU集速度如此之快,以至于我发现性能瓶颈恰好发生在CPU试图将数个序列写入内存时。
当然,知道CPU在填充数字方面很糟糕(相比之下,GPU可以以非常接近的速度填充一系列数字(238 5GB/秒从GK110上的288 5GB/秒到51.2GB/秒中可怜的5GB/sec )到GPU的全局内存的理论带宽),我可以稍微改变一下我的算法,但让我奇怪的是,为什么CPU在这里填充数字的能力如此差。
至于我的设备的内存带宽,我相信带宽(51.2GB)是正确的,根据我的memcpy()测试,实现的带宽是关于理论带宽memcpy()的80%+的。
发布于 2013-08-23 13:13:36
假设这是x86,并且您还没有达到可用的DRAM带宽,您可以尝试使用SSE2或AVX2一次编写2或4个元素:
SSE2:
#include "emmintrin.h"
const __m128i v2 = _mm_set1_epi64x(2);
__m128i v = _mm_set_epi64x(1, 0);
for (size_t i=0; i<1000*1000*1000; i += 2)
{
_mm_stream_si128((__m128i *)&data[i], v);
v = _mm_add_epi64(v, v2);
}AVX2:
#include "immintrin.h"
const __m256i v4 = _mm256_set1_epi64x(4);
__m256i v = _mm256_set_epi64x(3, 2, 1, 0);
for (size_t i=0; i<1000*1000*1000; i += 4)
{
_mm256_stream_si256((__m256i *)&data[i], v);
v = _mm256_add_epi64(v, v4);
}请注意,data需要适当地对齐(16字节或32字节边界)。
AVX2只在英特尔的Haswell和以后的版本上提供,但是SSE2现在已经相当普及了。
FWIW我用标量循环组合了一个测试工具,上面的SSE和AVX循环用clang编译了它,并在Haswell MacBook Air (1600 the LPDDR3 DRAM)上进行了测试。我得到了以下结果:
# sequence_scalar: t = 0.870903 s = 8.76033 GB / s
# sequence_SSE: t = 0.429768 s = 17.7524 GB / s
# sequence_AVX: t = 0.431182 s = 17.6941 GB / s我还在带有3.6 GHz Haswell的Linux桌面PC上试用了它,用gcc 4.7.2编译,得到如下结果:
# sequence_scalar: t = 0.816692 s = 9.34183 GB / s
# sequence_SSE: t = 0.39286 s = 19.4201 GB / s
# sequence_AVX: t = 0.392545 s = 19.4357 GB / s因此,看起来SIMD实现比64位标量代码提高了2倍或更多(尽管256位SIMD似乎没有比128位SIMD有任何改进),而且典型的吞吐量应该比5GB/ s快得多。
我的猜测是OP的系统或基准代码有问题,导致吞吐量明显下降。
发布于 2013-08-23 14:57:27
你有什么理由期望所有的data[]都在升级的内存页面中吗?
DDR3预取器将正确预测大多数访问,但频繁的x86-64页面边界可能是一个问题。您正在写入虚拟内存,因此在每个页面边界上都存在对预取程序的潜在错误预测。通过使用大页面(例如,Windows上的MEM_LARGE_PAGES ),您可以大大减少这种情况。
https://stackoverflow.com/questions/18403568
复制相似问题