我的代码的这部分需要很长时间才能运行,我正在寻找一种优化它的方法。我认为查找表是最快的方式,但我可能是错的。我的程序有一个main for循环,对于main for循环中的每次迭代,嵌套循环经过1,233,487
次迭代,然后在满足条件时通过if语句。主循环遍历898,281
迭代,因此必须经过898,281 * 1,233,487
计算。我将如何创建查找表来优化这些计算?是否有更好的方法来优化我的代码。
for (int i = 0; i < all_neutrinos.size(); i++)
{ //all_neutrinos.size() = 898281
int MC_count = 0; //counts per window in the Monte Carlo simulation
int count = 0; //count per window for real data
if (cosmic_ray_events.size() == MC_cosmic_ray_events.size())
{
for (int j = 0; j < cosmic_ray_events.size(); j++)
{ //cosmic_ray_events.size() = 1233487
if ((MC_cosmic_ray_events[j][1] >= (all_neutrinos[i][3] - band_width))
&& (MC_cosmic_ray_events[j][1] <= (all_neutrinos[i][3] + band_width)))
{
if ((earth_radius * fabs(all_neutrinos[i][2] - MC_cosmic_ray_events[j][0]))
<= test_arc_length)
{
MC_count++;
}
}
if ((cosmic_ray_events[j][7] >= (all_neutrinos[i][3] - band_width))
&& (cosmic_ray_events[j][7] <= (all_neutrinos[i][3] + band_width)))
{
if(earth_radius * fabs(all_neutrinos[i][2] - cosmic_ray_events[j][6])
<= test_arc_length)
{
count++;
}
}
}
MCcount_out << i << " " << MC_count << endl;
count_out << i << " " << count << endl;
}
}
答案 0 :(得分:4)
首先cosmic_raw_events
和MC_cosmic_ray_events
完全无关。把它做成两个循环。
按MC_cosmic_ray_events
排序[1]
。按cosmic_ray_events
排序[7]
。按all_neutrinos
排序[3]
。
这不必是就地排序 - 如果需要,可以将指针或索引数组排序。
从设置为0的宇宙射线事件中的高水位和低水位指数开始。
现在,走过all_neutrinos
。对于每一个,提前高水直到
MC_cosmic_ray_events[highwater][1] > all_neutrinos[i][3] + band_width)
。然后将低水推进到MC_cosmic_ray_events[lowwater][1] >= all_neutrinos[i][3] - band_width)
。
在半开放范围j = lowwater upto but not including highwater
上,运行:
if (
(earth_radius * fabs(all_neutrinos[i][2] - MC_cosmic_ray_events[j][0]))
<= test_arc_length
) {
MC_count++;
}
现在重复,直到i
到达all_neutrinos
的结尾。
然后使用cosmic_ray_events
和[7]
重复此过程。
您的代码需要O(NM)
次。此代码需要O(N lg N + M lg M + N * (average bandwidth intersect rate)
时间。如果通过带宽测试的人数相对较少,那么你的速度会更快。
假设每个all_neutrinos
得到平均0.5个相交,这将大约快100000倍。
答案 1 :(得分:-1)
没有太多优化。计数非常高,并且没有太多的硬计算。您可以进行一些明显的优化,例如在进入j循环之前将(all_neutrinos [i] [3] +/- bandwitdth)存储在局部变量中。但是,编译器可能已经这样做了,但这肯定会提高调试模式的性能。
您是否尝试过分离j-loop的两半并有两个j-loop?如:
auto all_neutrinos_2 = all_neutrinos[i][2];
//... precompute bandwidth limits
for (int j = 0; j < cosmic_ray_events.size(); j++)
{ //cosmic_ray_events.size() = 1233487
auto MC_events = MC_cosmic_ray_events[j][1];
if ((all_neutrinos_lower <= MC_events) &&(MC_cosmic_ray_events[j][1] <= all_neutrinos_higher))
{
if ((earth_radius * fabs(all_neutrinos_2 - MC_cosmic_ray_events[j][0]))
<= test_arc_length)
{
MC_count++;
}
}
}
for (int j = 0; j < cosmic_ray_events.size(); j++)
{ //cosmic_ray_events.size() = 1233487
auto events = cosmic_ray_events[j][7];
if ((all_neutrinos_lower <= events) && (events <= all_neutrinos_higher))
{
if(earth_radius * fabs(all_neutrinos_2 - cosmic_ray_events[j][6])
<= test_arc_length)
{
count++;
}
}
}
我觉得你可以通过这种方式从改进的内存缓存点击中获得一些改进。
除此之外的任何改进都将涉及打包输入数据以减少内存缓存未命中,并涉及修改生成MC_cosmic_ray_events和cosmic_ray_events数组的结构和代码
在不同线程上运行的几个较小的任务中计算计数也是我此时会认真看待的路线。数据访问是只读的,每个线程都可以有自己的计数器,最后都可以将它们相加。