Enumerable.Range与for循环的性能对比

7
我想知道使用Enumerable.Range与使用foreach循环相比的性能开销如何。例如:
var stringArray = Enumerable.Range(0, 4).Select(i => string.Empty).ToArray();

对比。

var stringArray = new string[4];
for (int i = 0; i < formatted.Length; i++)
{
    stringArray[i] = string.Empty;
}

我看到了这些问题:
  1. 为什么 Enumerable.Range 比直接使用 yield 循环更快?
  2. Enumerable.Range 的实现
  3. 使用 Enumerable.Range 和传统的 for 循环进行 foreach 的想法
但我担心在最后使用了 Select 之后可能会出现循环两次的情况。但是我喜欢使用 Range 选项的优雅。

当你想知道哪个更快时,只需自己测试即可。 - juharr
第一种方法唯一的开销是实例化一些对象。ToArray将是最大的罪魁祸首。但这不是你需要担心的性能问题(通常情况下)。这是微观优化。 - Dennis_E
@Dennis_E 这只是出于好奇,谢谢你的帮助。 - jolySoft
2个回答

8

以下测试结果显示,for 循环更加高效:(时间差约为3毫秒,可以忽略不计...)

var watch = System.Diagnostics.Stopwatch.StartNew();
var stringArra1y = Enumerable.Range(0, 4).Select(i => string.Empty).ToArray();
watch.Stop();
Console.WriteLine(watch.ElapsedTicks); //3305

watch = System.Diagnostics.Stopwatch.StartNew();
var stringArray2 = new string[4];
for (int i = 0; i < stringArray2.Length; i++)
{
    stringArray2[i] = string.Empty;
}
watch.Stop();
Console.WriteLine(watch.ElapsedTicks); //1

但是你可以使用.Repeat代替使用Enumerable.Range().Select:

var watch = System.Diagnostics.Stopwatch.StartNew();
var stringArra1y = Enumerable.Repeat(string.Empty, 4).ToArray();
watch.Stop();
Console.WriteLine(watch.ElapsedTicks); //391

在上述通知中,您谈论的是非常小的集合(4个项目)。在更大的集合中,特别是如果您删除 .ToArray(),它的行为就不同了。
var watch = System.Diagnostics.Stopwatch.StartNew();
var stringArra1y = Enumerable.Repeat(string.Empty, 100000);
watch.Stop();
Console.WriteLine(watch.ElapsedTicks); //360


watch = System.Diagnostics.Stopwatch.StartNew();
var stringArray2 = new string[100000];
for (int i = 0; i < stringArray2.Length; i++)
{
    stringArray2[i] = string.Empty;
}
watch.Stop();
Console.WriteLine(watch.ElapsedTicks); //1335

但是我担心在结尾处使用Select会导致循环两次的效果。

通过查看参考源代码,可以发现.RangeRepeat都是使用yield return实现的:

static IEnumerable<int> RangeIterator(int start, int count) {
     for (int i = 0; i < count; i++) yield return start + i;
}

所以它也是延迟执行的,就像 .Select 一样,这意味着它不会重复循环两次。

注意使用 Stopwatch 每次运行返回的结果可能不同,但总体思路与上述相同

在我看来,特别是针对小集合,优先考虑可读性,而不是这些微小的性能改进。当你遇到性能问题时,只有在解决更大的问题后(例如嵌套的 for 循环在 List<> 上而不是使用 HashSet<> 时),才需要处理这些东西。


循环是迄今为止最快的,但非常丑陋(在我看来),但重复对性能影响不大,仍然优雅(在我看来)。非常感谢。 - jolySoft
非常出色,我看不到循环的延迟执行。 - jolySoft
优先选择可读性而非微小的性能改进。"Enumerable.Range"是否更易读?看起来社区更喜欢传统的for循环? - BornToCode

1
在我的非常简单的测试中,看起来对于1,000,000个字符串,for循环比较快,快了38毫秒。
static void Main(string[] args)
        {
            var start = DateTime.Now;
            EnTest();
            var end = DateTime.Now;

            var firstResult = end - start;
            Console.WriteLine("Difference for Enumerable: {0}ms", firstResult.Milliseconds);

            GC.Collect();
            Thread.Sleep(2000);

            var secondStart = DateTime.Now;
            ArTest();
            var secondEnd = DateTime.Now;

            var secondResult = secondEnd - secondStart;
            Console.WriteLine("Difference for loop: {0}ms", secondResult.Milliseconds);

            var globalResult = firstResult - secondResult;
            Console.WriteLine("Difference between tests: {0}ms", globalResult.Milliseconds);

            Console.ReadKey();
        }

        public static void EnTest()
        {
            var stringArray = Enumerable.Range(0, 1000000).Select(i => string.Empty).ToArray();
        }

        public static void ArTest()
        {
            var stringArray = new string[1000000];
            for (int i = 0; i < stringArray.Length; i++)
            {
                stringArray[i] = string.Empty;
            }
        }

循环虽然快,但不够优雅,“重复”正是我想要的,感谢。 - jolySoft

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