繁体   English   中英

C ++优化简单循环

[英]C++ optimize simple loop

我正在使用Visual Studio 2012并在x64发布模式下构建。 以下代码花费了我的程序运行时间的33.5%。 我使用Visual Studio Profiler对其进行了测量。

    //every variable is unsigned int or unsigned int*

    for(unsigned int i = 0; i < num; i++)
    {
        unique[ids[i]]++;//2.1%
        total[ids[i]] += main_list[id];//31.4%
    }

有人可以建议一种减少此功能运行时间的方法吗?

编辑 :根据您的输入,我尝试了以下代码:

    const unsigned int now = main_list[id];

    for(unsigned int i = ids[0], j = 0; j < num; j++)
    {
        ++unique[i];//2.0%
        total[i] += now;//16.7%
        i = ids[j];//16.8%
    }

这证实了以下理论:CPU分支预测可能会失败,因为位置是随机的(顺便说一下,它们不是完全随机的,而是经过排序的)。 请问是否有可能加快我的代码的速度?

第2次修改:我尝试了以下操作:

    const unsigned int now = main_list[id];

    for(unsigned int i = ids[0], j = 0; j < num; j++)
    {
        total[i] += now;//2.0%
        ++unique[i];//16.7%
        i = ids[j];//16.8%
    }

以上测试应清楚说明正在发生的情况。

您的代码没有任何地区友好性。 我会提出两个可能的想法。

  1. 分组uniquetotal在一起。

     struct Stuff { unsigned int unique, total; }; for(unsigned int i = 0; i < num; i++) { Stuff& s = stuffs[ids[i]]; s.unique++; s.total += main_list[id]; // <== is this supposed to be ids[i]? } 

这样可以确保您在内存中连续访问的内容实际上在内存中彼此相邻。 num ,假设num足够大,那么每一行都会丢失缓存。 那差不多是你能得到的。

  1. 排序ids 现在,您还在记忆中弹跳。 让我们确保我们可以按顺序进行:

     std::sort(ids, ids + num); // rest of loop as before 

这样,很可能在处理stuffs[ids[i]]会预stuffs[ids[i+1]] stuffs[ids[i]] 这样也可以节省您很多查找时间。

您可能会因别名而受到打击,因为它必须允许uniquetotalmain_list在内存中重叠的可能性,从而使编译器无法在此处优化循环。 这可能会更好:

const auto mainListId = main_list[id];
for (unsigned int i = 0; i < num; ++i) {
    const auto currId = ids[i];
    ++unique[currId];
    total[currId] += mainListId;
}

当然,假设实际上没有任何混淆。

这样一个简单的循环可以做的事情不多。 您可以确保将编译器优化设置设置为最大,如果编译器没有为您完成循环,则可以尝试展开循环。 除此之外,您可能需要对算法进行改进,使其超出此处显示的代码范围。

由于ids的顺序导致的非顺序内存访问,可能会限制内存。 可以通过在此循环之前对ids数组进行排序来解决此问题,但是如果您没有更多上下文信息可以尝试进行操作,那么很难说这是否有意义。

我对i = ids[j]; //16.8%感到惊讶i = ids[j]; //16.8% i = ids[j]; //16.8%应该会更快。 看来时机已到。 ++unique[i]; //2.0% ++unique[i]; //2.0%是一种非线性(不可预取)访问,应该更慢,而不是快8倍。 实际上, ids[]应该在高速缓存中,因此只有8分之1的访问会命中主内存。 声明应该 8倍。 您确定您有正确的时间进行正确的操作吗?

也就是说,您应该并行化循环。 不会有太大帮助; 主内存不会变快。 但是,您应该保持主内存繁忙。 如果没有显式访问,CPU预取器的想法是抛出一些预测的访问。 如果预测正确,则可以节省时间,否则只会浪费一点能量。

因为ids[]已排序,所以可以并行化循环。 即使存在重复的值,它们也是相邻的,因此您可以通过查找重复值的第一个出现点来找到分割点。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM