在展开的链表上运行大约占代码运行时的40% - 是否有任何明显的优化方法?

时间:2013-07-22 18:49:33

标签: c++ performance optimization

我是一个名为vampire(http://github.com/richard-evans/vampire)的开源科学代码的作者,并且计算密集型意味着代码性能的任何改进都可以显着增加可以完成的研究。此代码的典型运行时可能是数百个核心小时,因此我一直在寻找改进代码性能关键部分的方法。但是,我有点陷入以下相对无害的代码中,这占了运行时间的40%左右:

for (int atom = start_index; atom < end_index; atom++){
    register double Hx = 0.0;
    register double Hy = 0.0;
    register double Hz = 0.0;
    const int start = atoms::neighbour_list_start_index[atom];
    const int end = atoms::neighbour_list_end_index[atom] + 1;
    for (int nn = start; nn < end; nn++){
        const int natom = atoms::neighbour_list_array[nn];
        const double Jij = atoms::i_exchange_list[atoms::neighbour_interaction_type_array[nn]].Jij;
        Hx -= Jij * atoms::x_spin_array[natom];
        Hy -= Jij * atoms::y_spin_array[natom];
        Hz -= Jij * atoms::z_spin_array[natom];
    }
    atoms::x_total_spin_field_array[atom] += Hx;
    atoms::y_total_spin_field_array[atom] += Hy;
    atoms::z_total_spin_field_array[atom] += Hz;
}

此代码的函数和变量的高级概述如下:有一个物理向量的一维数组(为了内存缓存目的,每个组件x,y,z分成三个1D数组,{{1等等')称为'旋转'。这些旋转中的每一个都与其他一些旋转相互作用,并且所有交互都存储为一维邻居列表(atoms::x_spin_array)。每个原子的相关交互列表由两个独立数组中的邻居atoms::neighbour_list_array的起始和结束索引确定。在计算结束时,每个原子自旋具有有效场,该有效场是相互作用的矢量和。

鉴于代码量很小,而且它占用的运行时间相当大,我已经做得最好了,但我觉得必须有一种方法可以进一步优化它,但作为物理学家而不是计算机科学家,我可能会失踪什么?

3 个答案:

答案 0 :(得分:6)

你有一个恒定的乘法流,减去并加上连续的数据。这似乎是SSE的理想用途。如果它的内存带宽有限,那么改为使用OpenCL / CUDA。

如果您不熟悉所有低级指令,请尝试使用this库。

然后可能会重新调整内部循环可能导致加速。

答案 1 :(得分:2)

如果xyz组件确实是链接列表,那么执行x[i]y[i]z[i]会导致列表要多次遍历,进行(n^2)/2次迭代。使用向量将使其成为O(1)操作。

您提到将三个坐标拆分出来用于内存缓存,但是当您访问内存中的3个不同区域时,这将影响1级和2级缓存位置。链表也会影响您的缓存位置。

使用类似的东西:

struct vector3d {
    double x;
    double y;
    double z;
};

std::vector<vector3d> spin;
std::vector<vector3d> total_spin;

这应该会改善缓存局部性,因为x,y和z值在内存中相邻,并且旋转占用了一个线性内存块。

答案 2 :(得分:0)

我觉得以下建议可以帮助您优化代码,如果不是完全的话:

  1. 尽可能使用初始化而不是分配
  2. 喜欢预先增加过度以获得更好的速度。(相信我,它确实会有所改变)
  3. 除此之外,我认为代码很好。每个DS都有一些优点和缺点。你必须忍受它。

    快乐的编码!

相关问题