【问题标题】:How does Array.ForEach() compare to standard for loop in C#?Array.ForEach() 与 C# 中的标准 for 循环相比如何?
【发布时间】:2011-01-31 21:21:05
【问题描述】:

我怀念作为 C 程序员可以打字的日子:

memset( byte_array, '0xFF' );

并获得一个填充了“FF”字符的字节数组。所以,我一直在寻找替代品:

for (int i=0; i < byteArray.Length; i++)
{
    byteArray[i] = 0xFF;
}

最近,我一直在使用一些新的 C# 功能,并且一直在使用这种方法:

Array.ForEach<byte>(byteArray, b => b = 0xFF);

当然,第二种方法看起来更干净,更容易看,但与第一种方法相比,性能如何?我是否通过使用 Linq 和泛型引入了不必要的开销?

谢谢, 戴夫

【问题讨论】:

  • 在第二种方法中仅供参考,您没有使用 LINQ。 LINQ 没有ForEach 方法。您正在使用 Array 类中的静态方法:Array.ForEach。 lambda 表达式是委托的语法糖,不算作 LINQ。
  • 感谢大家的回复。我学到了一些东西,希望其他人也能学到一些东西。

标签: c# .net performance


【解决方案1】:
Array.ForEach<byte>(byteArray, b => b = 0xFF);

这没有任何作用。它将每个字节的副本设置为 0xFF,它永远不会在数组中设置。

为了更简单的方法,您可以尝试

Enumerable.Repeat((byte)0xFF, someCount).ToArray();

初始化你的数组

Repeat 肯定会比你的 for 循环慢。根据 CAbbott 在评论中发布的链接,在一个拥有超过一百万个项目的数组上,它慢了大约 10.5 秒(12.38 对 1.7),但如果你只用小项目做几次,这没什么大不了的数组。

您可以编写一个比 Repeat 和 ToArray 更快的简单方法,因为您可以在开始填充之前知道数组的长度。

  public static T[] GetPreFilledArray<T>(T fillItem, int count)
  {
       var result = new T[count];
       for(int i =0; i < count; i++)
       {
           result[i] = fillItem;
       }
       return result;
  }

  byte[] byteArray = GetPreFilledArray((byte)0xFF, 1000);

这应该是一个非常快速的选择,因为这基本上就是您现在正在做的事情。

【讨论】:

  • 糟糕。我真的应该在发布之前尝试我编写的代码。当然,您是对的, ForEach 循环将不起作用,而 Enumerable.Repeat 确实可以很好地工作。它没有回答我关于性能问题的问题,但绝对是很好的信息......
  • 再想一想,它确实回答了这个问题——您解释了这两种方法如何相互比较,并提供了另一种选择。你得到这个点......
【解决方案2】:

如果您像发布 CAbott 提到的问题的那个人一样追求性能,您可能想看看my answer I just posted there

【讨论】:

    【解决方案3】:

    如果您将 Array.ForEach 置于关键执行路径中,我发现它们比 for 或 foreach 循环要慢得多。但是,除非您是框架/库开发人员或实际上需要初始化数百万个数组,否则我根本不会担心性能。通过在其他地方进行优化,您很可能会获得更多收益。

    【讨论】:

      【解决方案4】:

      Buffer.BlockCopy 是我想要 memset/memcpy 类型行为时倾向于使用的。我会测量性能并使用反射器之类的东西。可能是语言在内部调用了内置类,而这些类又是 MEMCPY 和 MEMSET 的薄包装。

      【讨论】:

        【解决方案5】:

        第二种方法使用委托来设置每个字节,这意味着数组中的每个字节都有一个方法调用。仅设置一个字节就需要很多开销。

        另一方面,编译器对普通循环进行了很好的优化。它将确定索引不能在数组之外,因此将跳过边界检查。

        澄清一下:您根本没有使用 LINQ。 ForEach 方法是 Array 类中的方法,它早于 LINQ 的添加。

        【讨论】:

          【解决方案6】:

          我认为泛型永远不会损害性能。它们主要在编译时处理,它们的一般目的是消除在对象和所需类型之间进行强制转换的需要,这在最坏的情况下会产生可忽略不计的影响,而在最好的情况下会产生可衡量的性能提升。

          就 LINQ 而言,我不确定它会产生什么性能影响。

          但最终,初始化是一项微不足道的任务,性能影响将不值得您关注。

          【讨论】:

            【解决方案7】:

            除非您在性能非常关键的操作期间执行此操作,否则您不会有问题。有许多关于 foreach 调用与索引迭代的基准,差异很小。

            当然,您总是可以自己进行基准测试...

            【讨论】:

              猜你喜欢
              • 2017-10-04
              • 2017-10-24
              • 2022-10-20
              • 1970-01-01
              • 1970-01-01
              • 2015-08-28
              • 2021-01-21
              • 1970-01-01
              相关资源
              最近更新 更多