使用foreach和集合会使程序变慢吗?

5

我正在重构我的应用程序以使其更快。我正在寻找关于这方面的技巧,并发现了以下声明:

"ForEach可以简化For循环中的代码,但它是一个沉重的对象,比使用For编写的循环慢。"

这是真的吗?如果这是在编写时是真的,今天还是这样吗?或者foreach本身已经进行了重构以提高性能?

我对来自同一来源的以下提示也有同样的问题:

"在可能的情况下,使用数组而不是集合。数组通常更有效率,特别是对于值类型。此外,在可能的情况下初始化集合到所需的大小。"

更新

我正在寻找性能技巧,因为我的数据库操作需要几秒钟的时间。

我发现"using"语句会占用大量时间。

通过反转for循环和"using"(当然,需要进行重构才能使其正常工作),我完全解决了我的性能问题。

原本速度极慢的代码如下:

for (int i = 1; i <= googlePlex; i++) {
    . . .
    using (OracleCommand ocmd = new OracleCommand(insert, oc)) {
    . . .
    InsertRecord();
    . . .

更快于子弹的代码如下:

using (OracleCommand ocmd = new OracleCommand(insert, oc)) {
    for (int i = 1; i <= googlePlex; i++) {
        . . .
        InsertRecord();
        . . .

3
你的信息来源是谁?这些是微小的优化 - 在大多数应用程序中,像锁定争用、网络或文件I/O之类的设计层面的问题比这些东西更有用。使用分析工具并修复有问题的内容,而不是可能存在问题的内容。 - Steve Townsend
2
“ForEach”是一个“重对象”?我不知道。请告诉我更多。 - Tudor
3
听起来对我来说是一个完全破损的来源。请发布更多细节,以便我们知道要避免它。 - Jon Skeet
没错,就是那个(csharphelp链接)。 - B. Clay Shannon-B. Crow Raven
5
我正在重构我的应用程序以使其更快。- 首先找出慢的部分,不要随意优化。 - H H
显示剩余4条评论
4个回答

10

简短回答:

代码难以阅读最终会导致软件行为和性能不佳。

详细回答:

在 .NET 初期存在一种微优化建议的文化。部分原因是由于 Microsoft 的一些内部工具(例如 FxCop)在普通大众中获得了流行。部分原因是 C# 有并且一直有着成为汇编语言、C 和 C++ 的继任者的愿望,关注于在性能关键的应用程序的几个最热门的代码路径中对原始硬件性能进行无限制访问。当然,这需要比典型应用程序更多的知识和纪律。框架代码和应用程序代码中与性能相关决策的后果也相当不同。

这对 C# 编码文化的净影响当然是积极的;但是只为了节省可能会被最近的 jitter 完全优化掉的几个 CIL 指令而停止使用foreachis""是荒谬的。

你的应用程序中可能有非常多的循环,并且其中最多只有一个是当前的性能瓶颈。以可读性为代价“优化”非瓶颈循环的性能是非常不划算的。


6
在许多情况下,foreach往往比等效的for更慢。同时也是真的。
for (int i = 0; i < myCollection.Length; i++) // Compiler must re-evaluate getter because value may have changed

比...慢
int max = myCollection.Length;
for (int i = 0; i < max; i++) 

但这可能完全没有关系。有关详细讨论,请参见C#中控制结构“for”和“foreach”的性能差异
您是否进行过任何分析以确定应用程序的热点?我会感到惊讶,如果循环管理开销是您应该关注的地方。

1
你如何将称呼 ForEach 为“重对象”的语句归类为“在许多情况下是正确的”? - Tudor
ForEachпјҲд»–еңЁй—®йўҳдёӯжҸҗеҲ°зҡ„пјүе’ҢforeachжҳҜдёҚеҗҢзҡ„дёңиҘҝгҖӮ - Tudor
@Tudor foreach需要在内存中保留一个迭代器对象,并且需要通过迭代器对每个对象进行函数调用,而for不需要。除此之外,我无法想象在速度上会有任何区别,因为在绝大多数情况下,迭代器只会存储索引。 - Chris Shain
他引用了一个同样将For大写的源代码。很不清楚他是在谈论TPL还是Linq,所以我认为大小写书写有误。也许我错了。 - Eric J.
@Chris Shain:无论如何,foreach是一个循环结构,而ForEach是一个方法,它们都不是对象。 - Tudor
显示剩余4条评论

4

您应该尝试使用Red Gate ANTS或类似的工具对代码进行性能分析 - 您会感到惊讶。

我发现在我编写的一个应用程序中,SQL中的参数嗅探占用了25%的处理时间。编写一个命令缓存,在应用程序开始时进行参数嗅探后,可以获得很大的速度提升。

除非您正在执行大量嵌套的for循环,否则我认为您不会从更改循环中看到太多的性能优化。我无法想象除了实时应用程序(例如游戏或大量数据计算或科学应用程序)之外,会需要那种类型的优化。


1
VS2010内置的性能分析功能出奇地不错。我同意ANTS仍然是黄金标准,但它也不便宜。 - Eric J.
免费试用只有一小段时间很便宜(£0.00):( - Charleh
VS2010内置了哪些性能分析工具?我使用的是专业版,但在“工具”菜单(或“项目”、“重构”或其他任何地方)中都没有找到这样的选项。 - B. Clay Shannon-B. Crow Raven
我之前并不知道,但是我也在使用专业版,相当奇怪的是它是最低版本之一,高级版和终极版拥有更多功能,所以可能是在更昂贵的版本中。 - Charleh

2

是的,经典的for循环比foreach循环稍微快一些,因为迭代是基于索引而不是通过枚举器访问集合元素。

       static void Main()
    {
        const int m = 100000000;
        //just to create an array
        int[] array = new int[100000000];
        for (int x = 0; x < array.Length; x++) {
            array[x] = x;
        }


        var s1 = Stopwatch.StartNew();           
        var upperBound = array.Length;
        for (int i = 0; i < upperBound; i++)
        {

        }
        s1.Stop();
        GC.Collect();
        var s2 = Stopwatch.StartNew();
        foreach (var item in array) { 

        }
        s2.Stop();
        Console.WriteLine(((double)(s1.Elapsed.TotalMilliseconds *
            1000000) / m).ToString("0.00 ns"));
        Console.WriteLine(((double)(s2.Elapsed.TotalMilliseconds *
            1000000) / m).ToString("0.00 ns"));
        Console.Read();

        //2.49 ns
        //4.68 ns

        // In Release Mode

        //0.39 ns
        //1.05 ns


}

2
请注意,foreach不一定要使用IEnumerable。它完全可以通过模式工作,而无需实现任何接口。 - Jon Skeet
请使用LinkedList再试一次 :) - Bryan Boettcher
我已经编辑了我的答案。我的意思是,foreach仍然需要一个枚举器来获取下一个元素,而经典的for循环则通过索引访问集合。 经典的for循环比foreach循环“始终”更快。 - Massimiliano Peluso
@MassimilianoPeluso 我不会说“总是”。想出一个刻意制造速度变慢的例子并不难。例如,如果集合的 CountLength 方法是一种非常昂贵的操作,那么传统的 for 将非常低效。此外,如果使用像 LinkedList 这样的东西,其中索引访问很费时,这也不是一个好主意。 - Servy
@EricJ。我不认为那是“经典”的for循环。虽然这只是一个简单的修改,但它仍然是一种修改。您还假设它是常数,尽管它很昂贵。它的结果并不总是恒定的(有时为了for循环的缘故使用当前计数而不是缓存计数是很重要的)。@Massimiliano Peluso-我认为引号是为了强调,而不是说你实际上是“通常”。 - Servy
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接