如何删除嵌套的foreach循环以提高性能

皮埃尔·乔治·米斯利(Pier Giorgio Misley)

我有一个基于性能的问题。有没有办法删除嵌套的foreach循环,将其替换为性能更高的东西?这是一个例子:

List<foo> foos = SelectAllfoos();

foreach(foo f in foos){
    //dosomething

    foreach(foo2 f2 in foo.GetFoos2()){
        //dosomething
    }

    foreach(foo3 f3 in foo.GetFoos3()){
        //dosomething
    }

    foreach(foo4 f4 in foo.GetFoos4()){
        //dosomething

        foreach(foo4_1 f4_1 in f4.GetFoos4_1()){
            //dosomething
        }
    }
}

毫无疑问,这是我为该示例发明的伪代码。但是想象一下你有这样的事情。您应该如何改善此方法的性能?

PS:我已经尝试使用System.Threading.Task.Parallel.ForEach它并提高了性能,但是我的意思是写此代码的一种更好的方法。

PPS:这是用C#编写的,但是我的问题涉及范围更广,在所有语言中都有用。

用户名

由于该问题相当笼统,并且只针对没有提供有关实际工作信息的循环,因此我只能提供一个一般性的答案。

您通常要关注的最后一件事是循环机制本身。这些影响通常很小,甚至没有影响。

通常,如果您遇到这种情况,则无法进行算法上的改进(例如:顺序循环不能比线性时间复杂度更好,因为它们需要遍历并且无论如何都需要对每个单个元素进行处理),那么通常会有两个最大的改进来自并行化和内存优化。

遗憾的是,后一种语言的讨论较少,尤其是在高级语言中,但经常会产生相同或更多的影响。它可以将执行时间缩短几个数量级,并且无论哪种语言都适用。诸如缓存效率之类的概念不是与语言相关的概念,因为无论我们使用哪种编程语言,硬件都保持不变(尽管我们如何实现它在不同语言之间可能会有很大差异)。

内存访问模式

例如,采用图像处理算法。在那种情况下,如果给定两个其他相同的机器指令(它们被交换的事实除外),则在外循环中一次访问一条水平扫描线的像素的存储器访问模式可以明显胜过访问一个垂直列的像素的存储器访问模式。一次像素数。即使在其他方面相同的机器指令具有相同的总指令级成本(尽管指令成本是可变的),但仅以交换顺序访问内存时,也是如此。

简而言之,这是因为计算机将数据从较慢形式的内存中提取到连续块(页面,缓存行)中的较快形式的内存中。当您水平访问图像的像素时,相邻的水平像素块可能会从较慢形式的内存中提取到较快形式,最终您会从较快形式的存储器中访问所有相邻像素,然后再继续进行操作。下一个像素系列。当您以垂直方式访问图像的像素时,最终将水平的相邻像素加载到一种更快的内存中,而仅使用该列中的一个像素。由于缓存未命中,结果会大大减慢生成的图像算法的速度,因为我们无法使用所有可用的数据。

因此,通常,如果您想使循环运行得更快并且算法得到了改进,则需要分析内存的访问方式,甚至可能更改所涉及数据结构的内存布局。当您访问内存中的连续数据时,计算机会喜欢它,而当您以一种无处不在的混乱方式访问内存时,计算机就不会那么喜欢它。他们喜欢将内存内容紧密打包在一起的数组,而不是将内存分散到各处的链接结构(除非精心设计链接结构或其内存分配器不要这样做)。快速循环并非来自改变循环机制的程度,而不仅仅是改变循环的作用,但是比算法改进甚至并行化还要深的是那些来自面向数据的设计思想的与内存相关的优化。在像C#这样的语言中,从数据结构中获得更好的引用局部性的一种技术是对象池。

循环平铺/阻止

有时,您可以通过简单地更改循环数据的方式而不实际更改数据表示方式的方式来改善内存访问模式。这样的示例之一是循环切片(也称为循环阻止):https : //software.intel.com/zh-cn/articles/how-to-use-loop-blocking-to-optimize-memory-use-on-32-位智能架构但同样,这里的提速并非来自优化循环本身的方式,而是优化了利用引用局部性遍历数据的方式。它仍然完全与内存访问有关。

剖析

所有这些微级优化技术都有使您的代码更难以维护的趋势,因此,在事后看来,它们总是总是可以最好地应用于您的大量性能分析测量。通常,要了解优化的第一件事是如何基于硬数据而不是直觉来进行测量。初学者往往希望进行更多而不是更少的优化,因为他们基于对效率低下的猜测而不是硬数据和适当的测量来进行优化。对于明显的算法瓶颈,这样做很容易,但是其他任何事情通常都需要在您的分析器中进行。一个好的优化器是狙击手来调度热点,而不是一个手榴弹兵对任何可能发生的事情盲目投掷手榴弹放慢脚步。实际上,知道如何正确地优化优先级并进行正确的测量可能比了解机器的内部工作原理更为重要。因此,可能要超出所有这些内容,如果您想使循环运行得更快,请首先抓住一个探查器,然后学习如何正确测量效率低下的问题。首先要问的不是如何使事情变得更快,而实际需要更快的速度(同样重要的是,如果没有,什么也没有)。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

替换嵌套的foreach以提高性能

来自分类Dev

如何提高foreach循环的性能

来自分类Dev

迭代两个嵌套循环时提高性能

来自分类Dev

避免for循环以提高性能

来自分类Dev

细分如何提高性能?

来自分类Dev

捆绑如何提高性能?

来自分类Dev

提高性能异步 Parallel.Foreach

来自分类Dev

替代复杂的for循环以提高性能

来自分类Dev

替代基本for循环以提高性能

来自分类Dev

输入范围上的回归循环-如何避免for循环并提高性能?

来自分类Dev

OpenGL / DirectX:Mipmapping如何提高性能?

来自分类Dev

Oracle:如何重写UNION以提高性能

来自分类Dev

如何使用缓存技术提高性能

来自分类Dev

异步Servlet处理如何提高性能

来自分类Dev

如何使用WebGL提高性能?

来自分类Dev

Unity运行缓慢,如何提高性能?

来自分类Dev

springMVC异步如何提高性能

来自分类Dev

Unity运行缓慢,如何提高性能?

来自分类Dev

如何使用字典提高性能?

来自分类Dev

如何调试 MongoDB 查询以提高性能

来自分类Dev

在循环C#时插入列表时如何提高性能

来自分类Dev

如何永久禁用和删除Windows Defender服务-缩短电池寿命并提高性能?

来自分类Dev

SQL提高性能

来自分类Dev

skSpriteKit提高性能

来自分类Dev

提高性能-Java

来自分类Dev

CLS 提高性能

来自分类Dev

通过使用yield与foreach可以提高性能吗?

来自分类Dev

避免循环生成可为空的集合,提高性能

来自分类Dev

通过远离for循环来提高性能