对于一些微积分密集型代码,我怎样才能获得最快的迭代?
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->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->begin(), end = parts->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。我怎样才能重写它以便它识别两个前缀?