文章目錄
  1. 1. Stage 1
  2. 2. Stage 2
  3. 3. Stage 3
  4. 4. Stage 4
  5. 5. Stage 5

学习Intel练习课
Hands-on Lab: Optimize Your Code for the Latest Intel®
XeonTM Processors and Intel® Xeon PhiTM Coprocessor
using Intel® Parallel Studio XE for Linux*

Stage 1

暂时不用考虑MonteCarlo的原理。Stage 1先后使用了gcc和icpc,并没有显著的进步。给我们的练习是使用mkl的并行随机数生成器。其目的就是对比mkl和串行随机数生成器的效率。mkl历史悠久,具有很高的可靠性,我们优化的时候,首先应该考虑这种集成度高的方式。

Intel的Developer Zone上有在线的API文档,查阅很方便。

typedef std::tr1::mt19937                     ENG;    // Mersenne Twister
typedef std::tr1::normal_distribution<float> DIST;   // Normal Distribution
typedef std::tr1::variate_generator<ENG,DIST> GEN;    // Variate generator

这些声明替换为如下:

float myrand[RAND_N];
VSLStreamStatePtr stream;
vslNewStream(&stream, VSL_BRNG_MT19937, 777);

一开始的时候,我一次性生成了两重循环需要的所有随机数,没有发现OPT_NRAND_N已经达十亿。不得已,只能在内层循环外生成OPT_NRAND_N个随机数。

vsRngGaussian(VSL_RNG_METHOD_GAUSSIAN_ICDF,stream,RAND_N,myrand,0.0,1.0);

vRngGaussian有double版和float版,分别是vdRngGaussian和vsRngGaussian。

在外层循环外删掉stream。

vslDeleteStream(&stream);

链接的时候又遇到了障碍,需要的所有库都得链接。

icc ./MonteCarloStep1.cpp ./Driver.cpp -I/opt/intel/composerxe/mkl/include -L/opt/intel/composexe/mkl/lib/intel64 -lmkl_intel_lp64 -lmkl_intel_thread -lmkl_core -lmkl_lapack95_lp64 -liomp5 -lpthread -O2 -o MonteCarlo

结果如下:总算是完成了。

Monte Carlo European Option Pricinig in Single Precision
Pricing 32768 Option with path length of 262144.
Completed in 151.4866 seconds.
Computation rate - 216.3096.

对照答案,发现答案有一点问题,每一次蒙特卡洛的模拟的随机变量是相同的,这样我觉得貌似失去了重复实验的意义,不排除是我对蒙特卡洛的理解出了问题。但是在效率上,仅一次生成RAND_N个随机数,的确提高了速度。

Stage 2

这几种优化手段都是第一次见到。float常量后面加上f,区分float和double对应的数学函数,减少冗余计算,避免类型转换,利用intel的计算比较快的数学函数。我对照答案修改之后的运行结果:(不过不是在同一个集群,没有参照性)

Monte Carlo European Option Pricinig in Single Precision
Pricing 32768 Option with path length of 262144.
Completed in 33.9102 seconds.
Computation rate - 966.3183.

而不用我的这些优化(不明白为什么答案没有优化这些地方,也许是我搞错了),时间是77s

对照答案,不明白为什么有的float常量后面写了f,有的没有。把强制转换预处理计算了,或者去掉了。为什么有个地方没有使用exp2f。为什么要把除法拿出来?为什么有的冗余计算给提前了,有的(如(RISKFREE - 0.5 VOLATILITY VOLATILITY)*M_LOG2E)则没有?static_cast和普通类型转换的区别?

Stage 3

由于向量化是书上着重讲的部分,于是我先自己实验,再看步骤。

-vec-report=3显示内层循环被向量化了,外层没有
使用#pragma ivdep没有用
观察发现,貌似并没有数据依赖,于是#pragma simd,时间从原来的77s变成了57s
进行到这里,感觉对我来说,已经完成了向量化工作。。。于是看资料

原来此文把内核级优化的内容也放到了该vectorization小节里。继续自己做实验。

切片,由于L2 cache是256KB,而RAND_N是2^18,l_Random是float数组,float是4字节,切成4块刚好每一块装满L2 cache,对于每个opt,处理长度为RAND_N/4的pos区间,这样提高了命中率,还不至于使内层循环并行度过低。时间从57s减少到了49s。
对齐,我有点犯晕,是否只有动态生成的内存才在这个问题上可控?
函数,这些函数应该都有对应的SIMD版本,会自动使用吧

又没有思路了。

reduction,如果不使用reduction,当数据量大了之后,可能出现竞争的问题,导致结果出错,reduction会给每一个线程一个副本,使用了reduction之后,外层循环不能向量化了,但是时间减为了14s。这是怎么回事?遂去掉了#pragma simd


照答案重做一遍
原始77s
向量对齐 声明数组时attribute((aligne(64)) float l_Random[RAND_N],第一维的首地址对齐。我想知道多维数组有什么不同。
然后#pragma vector aligned,声明向量已对齐
#pragma simd reduction(+:val) reduction(+:val2)用编译器自己向量化即可,不用openmp我还不明白openmp到底是干嘛的
#pragma unroll(4),展开了四次,因为里面加了一句话(因为max里计算了两次复杂表达式,手动展开了)总共四句
时间13s

Stage 4

计算是独立的,外层确实应该是可以向量化的
用openmp并行化外层循环
#pragma omp parallel for
平均时间1s

我不明白为什么可以用openmp而不能用simd。我确实需要复习openmp了,至少搞懂它到底是啥

Stage 5

mic的native模式,平均时间0.5s

明天继续补充,对照答案检查

我想自己做一点激进的实验,可是又没办法保证结果的正确,希望能找到什么资料讨论这个问题
当各个部件都熟悉了,问题会变成如何构建多级异构,如何使OpenMP、MPI等协调工作。

文章目錄
  1. 1. Stage 1
  2. 2. Stage 2
  3. 3. Stage 3
  4. 4. Stage 4
  5. 5. Stage 5