![]() |
1
5
如果新版本和旧版本都在同一个数据数组上运行,那么是的,最后一次运行几乎肯定会由于缓存而减速。即使代码不同,它也将访问先前版本已经接触过的数据,因此根据数据大小, 可以 在一级缓存中,可能在二级缓存中,如果存在三级缓存,几乎可以肯定。代码中可能还会有一些重叠,这意味着指令缓存将进一步提高第二个版本的性能。 基准测试的一种常见方法是先运行算法一次,而不对其进行计时,只需确保该算法将被缓存、缓存,然后在启用计时的情况下再次运行大量次。(不要相信一次执行,除非至少需要一两秒钟。否则,系统负载、缓存、操作系统中断或页面错误中的微小变化会导致测量的时间变化)。为了消除噪声,测量算法运行几次所用的组合时间,显然在这两次运行之间没有输出。事实上,你看到的峰值是平时的3倍,这意味着你测量的是 方式 粒度太细。这基本上让你的计时没用。
命名无关紧要。当代码被编译时,变量被转换成内存地址或寄存器ID。但是当您运行图像数组时,您将把它全部加载到CPU缓存中,这样下次运行它时可以更快地读取它。 是的,你可以也应该利用它。 计算机很难利用空间和时间位置——也就是说,如果你在时间t访问一个内存地址x,它假设你很快就会需要地址x+1(空间位置),而且你可能还会再次需要x,时间t+1(时间位置)。它试图以各种可能的方式(主要是通过缓存)加速这些案例,因此您应该尝试利用它。
我不知道您将if语句放在哪里,但是如果它放在频繁评估的代码块中,那么分支的成本可能会比您节省的成本对您造成更大的伤害。分支机构 可以 价格昂贵,而且它们抑制了编译器和CPU重新排序和调度指令的能力。所以没有它你可能会过得更好。您可能应该将其作为一个单独的优化来完成,该优化可以单独进行基准测试。
我不知道你要实现哪种算法,但我猜你需要对每个像素都这样做?
如果是这样,您应该尝试缓存查找。一旦你得到
如果您正在更新每个像素的值作为其相邻值的结果,一个更好的方法可能是以棋盘模式运行算法。在第一次迭代中,只使用邻居的值(您不更新这些值),更新其他像素,然后运行第二个步骤,根据之前更新的像素值更新之前读取的像素。这样可以消除相邻像素之间的依赖关系,从而有效地对其评估进行流水线和并行处理。 在执行所有查找的循环中,将其展开几次,并尝试将所有内存读取放在顶部,并将所有计算放在较低的位置,以使CPU有机会重叠这两个位置(由于数据读取速度较慢,请启动它们,当它们运行时,CPU将尝试查找它可以评估的其他指令)。
对于任何常量值,请尝试尽可能地预计算它们。(而不是
另一件事是 可以 帮助是首选局部变量而不是全局变量或类成员。在函数开始时,只需制作您需要的类成员的本地副本,就可以获得一些东西。如果编译器愿意的话,它总是可以再次优化多余的变量,但是您要清楚地表明,它不应该担心对象状态的底层更改(否则,每次访问成员时都会强制它重新加载成员)。 最后,对生成的装配进行了详细的研究。查看它在何处执行不必要的存储/加载,在何处重复操作,即使它们可以被缓存,以及指令的排序效率低下,或者编译器未能如您所希望的那样内联。
不过,老实说,我不希望您对查找函数所做的更改有多大影响。数组访问
通常,低级优化的关键是,有点讽刺的是, 不 查看单独的代码行,但查看整个函数和循环。您需要在一个块中有一定数量的指令,这样您就可以处理一些事情,因为许多优化处理打破指令链之间的依赖关系,重新排序以隐藏指令延迟,以及缓存单个值以避免内存加载/存储。这在单个数组查找中几乎是不可能做到的,但是如果一次考虑几个像素,几乎可以肯定会得到很多。 当然,就像几乎所有的微优化一样,没有 永远真实 答案。上面的一些可能对您有用,或者它们可能不有用。 如果您告诉我们更多关于访问模式的信息(您正在访问哪些像素,是否有任何所需的顺序,您只是在读还是在写?如果正在写入,何时何地使用更新的值?) 如果你给我们更多的信息,我们将能够提供更具体的(并且可能是有效的)建议。 |
![]() |
2
8
你 可以 (可能)遇到某种预读或缓存线边界问题。一般来说,当您加载一个值并且它不是“热”(在缓存中)时,CPU将拉入一条缓存线(32、64或128字节非常典型,取决于处理器)。对同一行的后续读取将快得多。 如果更改操作顺序,可能会看到由于如何加载和收回行而暂停。 解决这类问题的最佳方法是打开“反汇编”视图,并在处理器的参考手册上花费一些时间。 如果幸运的话,代码重新排序导致的更改将是显而易见的(编译器可能会生成额外的指令或分支)。不太幸运的是,它会在处理器的某个地方暂停——在解码过程中或由于内存获取… 一个可以计算暂停和缓存未命中的好的探查器在这里也有帮助(AMD已经 CodeAnalyst 例如。 如果你不在时间紧迫的情况下,那就真的值得陷入混乱之中——至少,你最终可能会学到一些你以前不知道的东西,比如你的CPU、机器体系结构、编译器、库等等是如何工作的。(我在学习歧义时几乎总是“啊”的。) |
![]() |
3
4
在优化时,检查数据访问模式至关重要。 例如: 假设宽度为240
像素为
使用原始访问模式,您可以:
注意索引的顺序是任意的。
内存控制器对主内存进行对齐访问以填充缓存线。
如果您对操作进行排序,以便访问到递增的内存地址
(例如)
读取时丢失缓存会很昂贵,因为它必须搜索缓存 到主内存的层次结构。主内存访问速度可能比 隐藏物。最小化主内存访问将提高操作速度。 |
![]() |
4
3
你真的测量过吗?因为如果那是真的,我会很惊讶的。安
什么操作的顺序?我不清楚你在这里重新订购什么。请再给我一些你想做的事情的片段。 |
![]() |
Hatsune Miku · 比较或if语句是否更快[已关闭] 1 年前 |
![]() |
Black Swan · 无法解压缩的值太多(应为2)错误 1 年前 |
![]() |
Kai · 有什么方法可以轻松优化VSCode中的锈迹? 2 年前 |
![]() |
Balfar · 处理NumPy阵列上的循环最有效的方法是什么? 2 年前 |
![]() |
Daniel · C#轻松存储快速访问的大型位矩阵 6 年前 |
|
halbe · 优化音频DSP程序的numpy计算 6 年前 |
![]() |
Afsara · 是否有任何方法不能优化我们的应用程序? 6 年前 |