use*_*453 11 c c++ memory optimization
任务非常简单,将整数变量的序列写入内存:
原始代码:
for (size_t i=0; i<1000*1000*1000; ++i)
{
data[i]=i;
};
Run Code Online (Sandbox Code Playgroud)
并行化代码:
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;
};
Run Code Online (Sandbox Code Playgroud)
性能很糟糕,编写1G 变量(每秒等于5GB)需要1.6秒uint64
,通过open mp parallel
上面代码的简单并行化(),速度提升升级,但性能仍然很糟糕,4线程和1.35 需要1.4秒在i7 3970上有6个线程.
我的装备(i7 3970/64G DDR3-1600)的理论内存带宽是51.2 GB /秒,对于上面的例子,实现的内存带宽只是大约带宽的1/10,即使应用程序也是相当多的内存-bandwidth有界.
有谁知道如何改进代码?
我在GPU上写了很多内存绑定代码,GPU很容易充分利用GPU的设备内存带宽(例如85%+的带宽).
编辑:
该代码由Intel ICC 13.1编译为64位二进制文件,并具有最大优化(O3)和AVX代码路径,以及自动矢量化.
更新:
我尝试了下面的所有代码(感谢Paul R),没有什么特别的事情发生,我相信编译器完全有能力进行simd /矢量化优化.
至于我为什么要在那里填写数字,好吧,长话短说:
它是高性能异构计算算法的一部分,在设备方面,algorthim非常高效,以至于多GPU集如此之快,以至于我发现性能瓶颈恰好是当CPU尝试写几个序列时数字到记忆.
原因,知道CPU在填充数(相反抽吸中,GPU可在非常接近(速度补数的seqence 238GB /秒总分288GB /秒上GK110 VS可怜5GB /秒总分51.2GB /秒上CPU)到GPU的全局内存的理论带宽,我可以稍微改变我的algorthim,但是让我想知道为什么CPU在这里填充数字的数量非常糟糕.
至于我的装备的内存带宽,我相信带宽(51.2GB)是正确的,根据我的memcpy()
测试,实现的带宽约为理论带宽的80%+(> 40GB /秒).
Pau*_*l R 11
假设这是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);
}
Run Code Online (Sandbox Code Playgroud)
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);
}
Run Code Online (Sandbox Code Playgroud)
注意,data
需要适当对齐(16字节或32字节边界).
AVX2仅适用于英特尔Haswell及更高版本,但SSE2目前非常普及.
FWIW我把一个带有标量循环的测试工具放在一起,上面的SSE和AVX循环用clang编译它,并在Haswell MacBook Air(1600MHz 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
Run Code Online (Sandbox Code Playgroud)
我也在具有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
Run Code Online (Sandbox Code Playgroud)
因此,看起来SIMD实现比64位标量代码提供了2倍或更多的改进(尽管256位SIMD似乎没有提供超过128位SIMD的任何改进),并且典型的吞吐量应该比5 GB /更快秒.
我的猜测是OP的系统或基准测试代码存在问题,导致吞吐量明显降低.
有没有理由为什么你会期望所有的data[]
都在加电的RAM页面?
DDR3预取程序将正确预测大多数访问,但频繁的x86-64页面边界可能是一个问题.您正在写入虚拟内存,因此在每个页面边界处可能会对预取器进行错误预测.您可以通过使用大页面(例如MEM_LARGE_PAGES
在Windows上)来大大减少这种情况.
归档时间: |
|
查看次数: |
713 次 |
最近记录: |