对于一些微积分密集型代码,我怎样才能获得最快的迭代?

Posted

技术标签:

【中文标题】对于一些微积分密集型代码,我怎样才能获得最快的迭代?【英文标题】:how can I get the fastest iteration possible for some calculus intensive code? 【发布时间】:2011-10-28 03:25:22 【问题描述】:

上下文

我正在使用 QLinkedList 来存储我编写的一些类。 事实上,我必须对这个列表进行很多的迭代。 我的意思是我编写的程序会进行无限演算(好吧,您仍然可以手动停止它),并且每次迭代我都需要通过那个 QLinkedList

问题

问题不在于我是否对这个列表进行了太多迭代。

我正在分析我的代码,我发现有 1/4 的时间花在 QLinkedList::end()QLinkedList::begin() 功能。

示例代码

我的代码如下:

typedef QLinkedList<Particle*> ParticlesList;  // Particle is a custom class

ParticlesList* parts = // assign a QLinkedList

for (ParticlesList::const_iterator itp = parts->begin(); itp != parts->end(); ++itp)

    //make some calculus

就像我说的,这段代码被频繁调用,以至于它在 parts->begin()parts->end() 上花费了大量时间。

问题

那么,问题是如何减少迭代这个列表所花费的时间?

可能的解决方案

这里有一些我想到的解决方案,请帮我选择最好的或者给我另一个建议:)

使用经典的 C 数组://抱歉这个错误
Particle** parts = // assing it something
for (int n = 0; n < LENGTH; n++)

    //access by index
    //make some calculus

这应该很快吧?

也许使用 Java 风格的迭代器? 也许使用另一个容器? 组装?开个玩笑……或者也许?

感谢您未来的回答!

PS:我已经阅读了有关何时进行分析的 *** 帖子,所以不用担心;)

编辑:

列表修改

对不起,我想我忘记了最重要的,我会写整个函数而不剥离:

typedef std::vector<Cell*> Neighbours;
typedef QLinkedList<Particle*> ParticlesList;

Neighbours neighbours = m_cell->getNeighbourhood();
Neighbours::const_iterator it;

for (it = neighbours.begin(); it != neighbours.end(); ++it) 

    ParticlesList* parts = (*it)->getParticles();
    for (ParticlesList::const_iterator itp = parts->begin(); itp != parts->end(); ++itp)
    
        double d = distanceTo(*itp); // computes sqrt(x^2 + y^2)
        if(d>=0 && d<=m_maxForceRange)
        
            particleIsClose(d, *itp); // just changes 
        
    

为了确保我完整,整个代码都在循环中调用^^。

所以是的,列表已被修改,并且处于内部循环中。所以没有办法预先计算它的开始和结束。

而且,列表需要在每次大迭代时(我的意思是在最顶层的循环中)通过一个一个地插入来构建。

调试模式

是的,我确实在调试模式下进行了分析。而且我认为这句话是明智的,因为代码在 Release 中的速度提高了 2 倍。列表的问题消失了。

感谢大家的回答,对此深表歉意^^

【问题讨论】:

您是否总是需要处理列表中的所有项目?你在循环内部做了一些决定吗? 这不是一个“经典的 C++ 数组”,而是一个经典的 C 数组,如果有的话。在 C++ 中使用 std::vector 无论如何,一旦你开始循环,列表是否会改变,或者可以在循环期间插入元素? 列表中有多少项?例如,如果您有一个项目,您将在循环的每次迭代中调用begin()、2 个end() 和一个QLinkedList::iterator::operator++...如果列表实际上是 long 那么我不要真的相信begin()end() 会是最高的成本(除非循环体几乎是空的)。考虑将std::vector 用于连续内存,在发布模式下编译并进行优化...通常 你可能想see this。 【参考方案1】:

如果您在调试模式下进行分析,许多编译器会禁用内联。 begin() 和 end() 时间很高可能不是“真实的”。方法调用时间将远高于等效的内联操作。

我在完整代码中注意到的其他内容是,您正在内部循环中执行 sqrt。根据硬件架构,它们可能相当昂贵。我会考虑替换以下代码:

 double d = distanceTo(*itp); // computes sqrt(x^2 + y^2) 
 if(d >= 0 && d <= m_maxForceRange) 

与:

 double d = distanceToSquared(*itp); // computes x^2 + y^2
 if(d >= 0 && d <= m_maxForceRangeSquared)

我已经在我进行碰撞检测的代码中做到了这一点,它有时会带来明显的改进。这些测试是等效的,并且节省了大量对 sqrt 的调用。与优化一样,测量以验证它是否提高了速度。

【讨论】:

【参考方案2】:

如果您的编译器不够聪明,无法意识到它是 const,则预先计算结束迭代器会有所帮助,因此每次都通过循环计算它。你可以这样做:

const ParticlesList::const_iterator itp_end = parts->end();

for (ParticlesList::const_iterator itp = parts->begin(); itp != itp_end; ++itp)

    //make some calculus

我不明白为什么parts-&gt;begin(); 需要这么长时间,它应该只使用一次。但是,如果这个循环在另一个循环中,你可以这样做:

const ParticlesList::const_iterator itp_begin = parts->begin();
const ParticlesList::const_iterator itp_end = parts->end();

for (...)

  for (ParticlesList::const_iterator itp = itp_begin; itp != itp_end; ++itp)
  
      //make some calculus
  

但我无法想象这会产生太大的影响(除非你的内心清单真的很短),但它也不应该有太大的伤害。

进一步说明,对于您的目的,链表可能不是最快的数据结构。当您经常需要在列表中间插入项目时,链接列表最有用。如果列表已构建然后修复,您可能最好使用std::vector。即使您偶尔只需要从末尾(而不是开头或中间)添加/删除项目,std::vector 也可能会更好。如果您必须从开头/结尾(但不是中间)添加/删除,请考虑使用std::deque

【讨论】:

好答案。此外,提升的迭代器应自己制作为const(即const ParticlesList::const_iterator itp_end = ...)。【参考方案3】:

如果你绝对需要原始速度,你应该衡量你遇到的每一个可能的选择,并保持最快。

听起来列表在您迭代时保持不变。我会尝试将列表的末尾存储在局部变量中。

typedef QLinkedList<Particle*> ParticlesList;  // Particle is a custom class

ParticlesList* parts = // assign a QLinkedList
ParticlesList::const_iterator end = parts->end();

for (ParticlesList::const_iterator itp = parts->begin(); itp != end; ++itp)

    // make some calculus

【讨论】:

不妨这样做:for (ParticlesList::const_iterator itp = parts-&gt;begin(), end = parts-&gt;end(); ...。它更短,并将变量保持在 for 循环范围内。【参考方案4】:

Qt 容器与 std::for_each 等 STL 算法兼容。

试试这样的:

std::for_each( parts->begin(), parts->end(), MyParticleCalculus );

其中MyParticleCalculus 是一个包含您的微积分的函子。

Qt 也有自己的foreach,但它显然只是一个隐藏迭代器的宏,因此它可能不会给您带来任何性能优势。

(编辑:根据 Scott Meyer 在“有效 STL”中的建议,我推荐 std::for_each:"Prefer algorithm calls to hand-written loops.")

【讨论】:

以上是关于对于一些微积分密集型代码,我怎样才能获得最快的迭代?的主要内容,如果未能解决你的问题,请参考以下文章

无法获得多个前缀来使用 discord.js。我怎样才能重写它以便它识别两个前缀?

我的 setInterval 函数减慢了我的代码速度——我怎样才能修改它以获得更好的性能?

怎样才能在不更换电脑硬件的前提下,使电脑达到最快速度

与子进程通信的最快方式

BGL 顶点上的随机顺序迭代

在向量中分配临时元素(知道最大数量)的最快方法?