通过查找优化代码 table
Optimizing code with a lookup table
我的这部分代码花费的时间太长 运行,我一直在寻找优化它的方法。我认为查找 table 是最快的方法,但我可能错了。我的程序有一个主 for 循环,对于主 for 循环中的每次迭代,嵌套循环经过 1,233,487
次迭代,然后在满足条件的情况下通过 if 语句。主 for 循环经过 898,281
次迭代,因此它必须经过 898,281 * 1,233,487
次计算。我将如何着手创建查找 table 来优化这些 calculations/is 有更好的方法来优化我的代码。
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;
}
}
首先cosmic_raw_events
和MC_cosmic_ray_events
完全没有关系。让它循环两次。
按 [1]
排序 MC_cosmic_ray_events
。按 [7]
排序 cosmic_ray_events
。按 [3]
.
排序 all_neutrinos
这不一定是就地排序——如果需要,您可以将指针数组或索引排序到它们中。
首先将宇宙射线事件的高潮指数和低潮指数设置为 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 倍。
没有太多需要优化的地方。计数确实很高,并且没有进行太多的硬计算。您可以进行一些明显的优化,例如在进入 j 循环之前将 (all_neutrinos[i][3] +/- bandwitdth) 存储在局部变量中。不过,您的编译器可能已经这样做了,但这肯定会提高调试模式下的性能。
您是否尝试过将 j 环的两半分开并有两个 j 环?如:
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 数组的结构和代码
在不同线程上将计数分割成几个较小的任务 运行 也是我此时会认真考虑的一条路线。数据访问是只读的,每个线程可以有自己的计数器,最后都可以求和。
我的这部分代码花费的时间太长 运行,我一直在寻找优化它的方法。我认为查找 table 是最快的方法,但我可能错了。我的程序有一个主 for 循环,对于主 for 循环中的每次迭代,嵌套循环经过 1,233,487
次迭代,然后在满足条件的情况下通过 if 语句。主 for 循环经过 898,281
次迭代,因此它必须经过 898,281 * 1,233,487
次计算。我将如何着手创建查找 table 来优化这些 calculations/is 有更好的方法来优化我的代码。
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;
}
}
首先cosmic_raw_events
和MC_cosmic_ray_events
完全没有关系。让它循环两次。
按 [1]
排序 MC_cosmic_ray_events
。按 [7]
排序 cosmic_ray_events
。按 [3]
.
all_neutrinos
这不一定是就地排序——如果需要,您可以将指针数组或索引排序到它们中。
首先将宇宙射线事件的高潮指数和低潮指数设置为 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 倍。
没有太多需要优化的地方。计数确实很高,并且没有进行太多的硬计算。您可以进行一些明显的优化,例如在进入 j 循环之前将 (all_neutrinos[i][3] +/- bandwitdth) 存储在局部变量中。不过,您的编译器可能已经这样做了,但这肯定会提高调试模式下的性能。
您是否尝试过将 j 环的两半分开并有两个 j 环?如:
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 数组的结构和代码
在不同线程上将计数分割成几个较小的任务 运行 也是我此时会认真考虑的一条路线。数据访问是只读的,每个线程可以有自己的计数器,最后都可以求和。