【问题标题】:Performance of Enumerable.Range vs for loopEnumerable.Range 与 for 循环的性能
【发布时间】:2017-03-01 00:00:54
【问题描述】:

我想知道使用Enumerable.Range 与使用foreach 循环相比,性能开销是多少。例如:

var stringArray = Enumerable.Range(0, 4).Select(i => string.Empty).ToArray();

VS.

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

我发现了这些问题:

  1. Why is Enumerable.Range faster than a direct yield loop?

  2. Enumerable.Range implementation

  3. Thoughts on foreach with Enumerable.Range vs traditional for loop

但我担心最后会出现Select,那么实际上我可能会循环两次。不过我喜欢使用Range 选项的优雅。

【问题讨论】:

  • 当你想知道which is faster自己测试一下。
  • 第一种方法的唯一开销是几个对象的实例化。 ToArray 将是最大的罪魁祸首。但这不是您(通常)需要关注的性能问题。这是微优化。
  • @Dennis_E 这比其他任何事情都更加好奇。感谢您的帮助

标签: c# .net performance for-loop enumerable


【解决方案1】:

从以下测试来看,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 可能会循环两次

查看Reference Source.RangeRepeat 都是用yield return 实现的:

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

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

并不是说使用Stopwatch 每次运行都会返回不同的结果,但总体思路如上所示

IMO,尤其是在小型集合的情况下,为了提高可读性而不是这些微小的性能改进。当您已经遇到性能问题时,只有在获得更大的鱼之后(例如在List&lt;&gt; 上嵌套for 循环而不是使用HashSet&lt;&gt;),才可以处理这样的事情。

【讨论】:

  • 循环是迄今为止最快但非常丑陋的(恕我直言),但重复在性能上并不算太差,而且仍然很优雅(恕我直言)。非常感谢
  • 太棒了,这是我看不到的循环的延迟执行
  • “在这些小的性能改进上追求可读性” - “Enumerable.Range”是否更具可读性?看起来 community 更喜欢传统的 for 循环?
【解决方案2】:

在我非常简单的测试中,对于 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;
            }
        }

【讨论】:

  • Lopp 速度很快但很丑,Repeat 正是我想要的,谢谢
猜你喜欢
  • 2015-08-28
  • 2010-10-29
  • 2011-04-28
  • 2012-11-18
  • 2017-09-19
  • 2012-08-06
  • 2010-11-13
  • 2018-01-20
相关资源
最近更新 更多