4

我正在重构我的应用程序以使其更快。我正在寻找这样做的提示,并找到了以下声明:

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

真的吗?如果它在写的时候是真的,那么今天仍然是真的,还是 foreach 本身已经被重构以提高性能?

我对来自同一来源的这个提示有同样的问题:

“在可能的情况下使用数组而不是集合。数组通常更有效,尤其是对于值类型。此外,尽可能将集合初始化为所需的大小。”

更新

我正在寻找性能提示,因为我有一个数据库操作需要几秒钟。

我发现“使用”语句很耗时。

我通过反转 for 循环和“使用”完全解决了我的性能问题(当然,重构是必要的)。

比糖蜜慢的代码是:

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();
        . . .
4

4 回答 4

9

简短的回答:

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

长答案:

在早期的 .NET 中有一种微优化建议的文化。部分原因是微软的一些内部工具(如 FxCop)在公众中广受欢迎。部分原因是 C# 已经并且渴望成为汇编、C 和 C++ 的继任者,因为它可以在性能关键应用程序的少数最热门代码路径中不受阻碍地访问原始硬件性能。当然,这确实需要比典型应用程序更多的知识和纪律。框架代码和应用程​​序代码中与性能相关的决策的后果也大不相同。

当然,这对 C# 编码文化的净影响是积极的;但是停止使用foreachoris""仅仅为了保存一些 CIL 指令是荒谬的,如果你愿意的话,你最近的抖动可能会完全优化掉。

您的应用程序中可能有很多循环,并且可能最多其中一个可能是当前的性能瓶颈。以牺牲可读性为代价“优化”非瓶颈性能是一件非常糟糕的事情。

于 2012-05-21T16:47:40.753 回答
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”的性能差异

您是否进行了任何分析以确定应用程序的热点?如果循环管理开销是您应该关注的地方,我会感到惊讶。

于 2012-05-21T16:42:45.557 回答
4

您应该尝试使用 Red Gate ANTS 或类似的东西来分析您的代码 - 您会感到惊讶。

我发现在我正在编写的应用程序中,SQL 中的参数嗅探占用了 25% 的处理时间。在编写了一个在应用程序启动时嗅探参数的命令缓存之后,速度有了很大的提升。

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

于 2012-05-21T16:46:53.293 回答
2

是的。经典for比 a 快一点,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


}
于 2012-05-21T16:44:33.337 回答