2013-09-23 27 views
1

我有一个两阶段过程,在我的模拟程序中构成一个循环。更多或更少,我有以下:对STL向量执行重复操作是否允许“固有并行性”/改进的内存访问?

struct Coordinates 
{ 
    double * x, * y, * z; 
    uint * kind, count; 
    double GetDist(const uint p1, const uint p2); 
}; 

struct Polynomial 
{ 
    double * A, * B; 
    uint n1, n2; 
    uint Flatten(const uint i, const uint j); 
    double CalcResult(double distSq, uint kind1, uint kind2) 
    { 
     uint ij = Flatten(kind1, kind2); 
     double base = B * distSq; 
     return A[ij]*(pow(base,n2)-pow(base,n1)); 
    } 
}; 

我的问题是,如果我写我的代码就像

struct Model 
{ 
    Coordinates c; 
    Polynomial f; 
    double DoTest() 
    { 
     double result = 0; 
     uint count = 0; 
     std::vector<double> distSq; 
     for (uint i=0; i<c.count; i++) 
     { 
     for (uint j=i; j<c.count; j++) 
     { 
      result = c.GetDist(i,j); 
      distSq.push_back(result); 
     } 
     } 
     result = 0; 
     for (uint i=0; i<c.count; i++) 
     { 
     for (uint j=i; j<c.count; j++) 
     { 
      result += f.CalcResult(distSq[count], i, j); 
      count++; 
     } 
     } 
     return result; 
    } 
    double DoTest2() 
    { 
     double result = 0; 
     for (uint i=0; i<c.count; i++) 
     for (uint j=i; j<c.count; j++) 
      result += f.CalcResult(c.GetDist(i,j), i, j); 
     return result; 
    } 
} 

威尔Test自动启用并行性(如矢量数学或改进了内存访问)的x86芯片,给出它在单个数据集上的重复操作?

否则Test是一种垃圾方法 - 它使用额外的存储空间(std::vector<double> distSq;),并且在代码读取方面更长。从逻辑上讲它是或多或少相同,但如果我们调用GetDistf_A(功能A)和CalcResultf_B(函数B),测试是:

f_A f_A f_A ... f_A f_B f_B .... f_B 

凡为短/更少的内存密集型功能

f_A f_B f_A f_B .... f_A f_B 

我听说过-O#编译的C代码中所谓的“固有并行性”,这是由于生成的向量化数学运算等原因造成的。可能Test在x86上启用了这种编译器派生的并行性(例如向量化数学或优化内存访问?)芯片,给它的对单个数据集重复操作?

(否则Test2是唯一合理的方法,因为它使用更少的内存。)

而且将替换c样式xyz阵列与std::vector<double>替代有加速计算或存储器访问的可能性以任何方式?

请不要回答“你自己的基准”...我想要通过基于编译器的“理论基准”来测试方法Test以获得更好的理解,固有平行度“。

回答

1

无论并行性如何,内存访问都会导致您无法访问。您拨打.reserve(c.count*c.count())可以防止重新分配.push_back,但这还不够。如果c.count足够大,则会浪费L1缓存和可能的L2。

接下来的问题是您的f_A功能取决于内存访问。一个现代化的处理器可以同时发布读取和处理以前的f_B。没有数据依赖关系。这使得Test2效率更高。

BYW,它只是我,还是CalcResult(i,j)和CalcResult(j,i)非常相似?您可能会从组合计算中受益。

我会让ABdouble const*。毕竟,你不是通过他们写作的。

什么可能运作良好是#pragma omp for reduction(+, result)

+0

MSalters,它们是相同的,其实。这就是为什么我开始内循环@ j ---这是对唯一坐标对的计算,所以我们只计算N×N坐标空间的一半......关于内存访问的好想法,谢谢......这就是我的想法...我可能会从f_A f_B开始...方法 –

+0

正确,忽略了那一点 - 在这种情况下,您只需要预留(N * N + N)/ 2个元素。仍然是O(N * N)内存访问。 – MSalters

1

经典SIMD编译器优化

的已知易与由编译器SIMD指令来优化代码一个简单的例子如下:

for (int i = 0; i < N; ++i) 
    C[i] = A[i] + B[i]; 

SIMD optimization example with VC++

在你案例

你的第一个循环与确实看起来像所有的迭代是相互独立的,但根据什么GetDist实际上,结合将结果推回到一个向量中,我认为编译器可能比生成SIMD指令更难简单地在内置数组中添加2个矢量的情况如何。但我不是编译器专家,所以我可能是错的。它也可以从编译器到编译器不同。

确切知道的最好方法是编译代码并查看反汇编以查看编译器生成的指令类型。例如,如果您使用的是IA-32或64位Intel,请查找在MMX或XMM寄存器上执行操作的指令。您也可以尝试用内置数组替换该矢量,以查看它是否有任何区别。

Intel assembly language reference

一个有趣的谈话

我最近看了在融入本土2013会议的有趣的谈话吉姆Radigan。他在Microsoft C++编译器后端团队工作,擅长代码优化。他谈到了几个有趣的主题,其中包括在生成的机器代码中实现并行性。下面是谈话的链接:

Jim Radigan talks about compiler optimization

相关问题