Parallel processing 最小化嵌套循环中对std::max的调用数

Parallel processing 最小化嵌套循环中对std::max的调用数,parallel-processing,tbb,processing-efficiency,Parallel Processing,Tbb,Processing Efficiency,我试图减少内部循环中对std::max的调用次数,因为我调用了数百万次(毫不夸张!),这使得我的并行代码比顺序代码运行得慢。基本思想(是的,这是一个赋值)是代码计算某个网格点的温度,一次一次迭代,直到最大变化不超过某个非常小的数字(例如0.01)。新温度是其正上方、下方和旁边单元格中温度的平均值。因此,每个单元格都有不同的值,我想返回给定网格块的任何单元格中最大的更改 我已经让代码正常工作了,但速度很慢,因为我在内部循环中对std::max进行了大量调用(非常多),而它是O(n*n)。我使用了一

我试图减少内部循环中对std::max的调用次数,因为我调用了数百万次(毫不夸张!),这使得我的并行代码比顺序代码运行得慢。基本思想(是的,这是一个赋值)是代码计算某个网格点的温度,一次一次迭代,直到最大变化不超过某个非常小的数字(例如0.01)。新温度是其正上方、下方和旁边单元格中温度的平均值。因此,每个单元格都有不同的值,我想返回给定网格块的任何单元格中最大的更改

我已经让代码正常工作了,但速度很慢,因为我在内部循环中对std::max进行了大量调用(非常多),而它是O(n*n)。我使用了一维区域分解

注:tdiff不依赖于矩阵中的内容

还原函数的输入是lambda函数的结果

diff是网格块中单个单元格在一次迭代中发生的最大变化

代码前面定义了阻塞范围

t_new是该网格点的新温度,t_old是旧温度

max_diff = parallel_reduce(range, 0.0,
        //lambda function returns local max
        [&](blocked_range<size_t> range, double diff)-> double
        {
            for (size_t j = range.begin(); j<range.end(); j++)
            {
                for (size_t i = 1; i < n_x-1; i++)
                {
                    t_new[j*n_x+i]=0.25*(t_old[j*n_x+i+1]+t_old[j*n_x+i-1]+t_old[(j+1)*n_x+i]+t_old[(j-1)*n_x+i]);
                    tdiff = fabs(t_old[j*n_x+i] - t_new[j*n_x+i]);
                    diff = std::max(diff, tdiff);
                }   
            }
            return diff;    //return biggest value of tdiff for that iteration - once per 'i'
        },
        //reduction function - takes in all the max diffs for each iteration, picks the largest
        [&](double a, double b)-> double
        {
            convergence = std::max(a,b);
            return convergence;
        }
    );
max_diff=parallel_reduce(范围0.0,
//lambda函数返回局部最大值
[&](阻塞_范围,双差)->双差
{
对于(size_t j=range.begin();j double
{
收敛=标准::最大值(a,b);
回归收敛;
}
);
如何提高代码的效率?我想减少对std::max的调用,但需要保持正确的值。使用gprof我得到:

Each sample counts as 0.01 seconds.
  %   cumulative   self              self     total           
 time   seconds   seconds    calls  ms/call  ms/call  name    
 61.66      3.47     3.47  3330884     0.00     0.00  double const& std::max<double>(double const&, double const&)
 38.03      5.61     2.14     5839     0.37     0.96  _ZZ4mainENKUlN3tbb13blocked_rangeImEEdE_clES1_d
每个样本计数为0.01秒。
%累积自我总数
时间秒秒呼叫ms/呼叫ms/呼叫名称
61.66 3.47 3.47 3330884 0.00 0.00双常数和标准::最大值(双常数和双常数和)
38.03 5.61 2.14 5839 0.37 0.96

ETA:61.66%的代码执行时间是在std::max调用上,调用次数超过300万次。reduce函数是为lambda函数的每个输出调用的,因此减少lambda函数中对std::max的调用次数也会减少reduce函数的调用次数首先,我希望
std::max
被内联到它的调用者中,所以gprof指出它是一个单独的热点是可疑的。你可能会分析一个调试配置吗

另外,我不认为std::max是罪魁祸首。除非在它的实现中启用了一些特殊检查,否则我认为它应该相当于
(diffdiff)diff=tdiff;
,但我怀疑它会给你带来很多好处(也许编译器可以自己做这样的优化)

最有可能的是,
std::max
突出显示为;即真正的热点在
std::max
上方的计算中,这非常有意义,因为更多的工作和对可能具有更长延迟的非本地数据(数组)的访问,特别是当相应位置不在CPU缓存中时


根据网格中行(
n_x
)的大小,像您这样按行处理数据可能会效率低下、缓存不畅。最好在缓存中尽可能多地重用
t_old
中的数据。按行处理时,您要么在下一行之前根本不重复使用
t_old
中的点(对于
i+1
i-1
点)或仅重复使用一次(对于同一行中的两个相邻点)。更好的方法是通过矩形块处理网格,这有助于重用缓存中的热数据。使用TBB,方法是使用
blocked\u range2d
。它需要对代码进行最小的更改;基本上,更改范围类型和lambda中的两个循环:外部循环和内部循环应在
r上迭代ange.rows()
range.cols()

首先,我希望
std::max
被内联到它的调用者中,所以gprof指出它是一个单独的热点是可疑的。您可能会分析调试配置吗

另外,我不认为std::max是罪魁祸首。除非在它的实现中启用了一些特殊检查,否则我认为它应该相当于
(diffdiff)diff=tdiff;
,但我怀疑它会给你带来很多好处(也许编译器可以自己做这样的优化)

最有可能的是,
std::max
突出显示为;即真正的热点在
std::max
上方的计算中,这非常有意义,因为更多的工作和对可能具有更长延迟的非本地数据(数组)的访问,特别是当相应位置不在CPU缓存中时


根据网格中行(
n_x
)的大小,像您这样按行处理数据可能会效率低下、缓存不畅。最好在缓存中尽可能多地重用
t_old
中的数据。按行处理时,您要么在下一行之前根本不重复使用
t_old
中的点(对于
i+1
i-1
点)或仅重复使用一次(对于同一行中的两个相邻点)。更好的方法是通过矩形块处理网格,这有助于重用缓存中的热数据。使用TBB,方法是使用
blocked\u range2d
。它需要对代码进行最小的更改;基本上,更改范围类型和lambda中的两个循环:外部循环和内部循环应在
r上迭代分别是ange.rows()和
range.cols()

我最后使用了parallel\u:

parallel_for(range, [&](blocked_range<size_t> range)
        {
            double loc_max = 0.0;
            double tdiff;
            for (size_t j = range.begin(); j<range.end(); j++)
            {
                for (size_t i = 1; i < n_x-1; i++)
                {
                    t_new[j*n_x+i]=0.25*(t_old[j*n_x+i+1]+t_old[j*n_x+i-1]+t_old[(j+1)*n_x+i]+t_old[(j-1)*n_x+i]);
                    tdiff = fabs(t_old[j*n_x+i] - t_new[j*n_x+i]); 
                    loc_max = std::max(loc_max, tdiff); 
                }   
            }
            //reduction function - takes in all the max diffs for each iteration, picks the largest
            {
                max_diff = std::max(max_diff, loc_max);
            }
        }
    );
parallel_for(范围,[&](阻塞的_范围)
{
双定位最大值=0.0;
双tdiff;