7

I wondered what the performance overhead is of using Enumerable.Range was against using a foreach loop. For example:

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;
}

I spotted these question:

  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

But I fear with the Select at the end then I might be, in effect, loop twice. However I like the elegance of using the Range option.

Community
  • 1
  • 1
jolySoft
  • 2,948
  • 2
  • 30
  • 34
  • When you want to know [which is faster](https://ericlippert.com/2012/12/17/performance-rant/) just test it yourself. – juharr Oct 19 '16 at 11:37
  • The only overhead the 1st method has is the instantiation of a few objects. `ToArray` will be the biggest culprit. But this is not a performance issue you need to be concerned about (usually). This is micro-optimization. – Dennis_E Oct 19 '16 at 11:40
  • @Dennis_E it was more curiosity than anything else. Thanks for the help – jolySoft Oct 19 '16 at 11:47

2 Answers2

7

From the following test the for is more efficient: (in milliseconds it is +-3 ms difference - which is insignificant..)

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

But you can instead of using Enumerable.Range().Select use .Repeat:

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

After saying the above notice that you are talking here about very small collections (4 items). In larger collections, and especially if you remove the .ToArray() it doesn't behave the same:

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

But I fear with the Select at the end then I might be, in effect, loop twice

Looking though the Reference Source both the .Range and Repeat are implemented with a yield return:

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

So it too id deffered executed, just like the .Select meaning it does not loop twice.

Not that the use of the Stopwatch returns different results each run but the overall idea is as presented above

IMO, especially in the case of small collections go for readability over hese minor performance improvements. When you already hit performance issues, only after getting the bigger fish (for instance nested for loops on List<> instead of using a HashSet<>), deal with stuff like this.

Community
  • 1
  • 1
Gilad Green
  • 36,708
  • 7
  • 61
  • 95
  • A loop is by far the fastest but very ugly (IMHO) but repeat isn't too bad on performance and is still elegant (IMHO). Many thank – jolySoft Oct 19 '16 at 11:30
  • Outstanding, it was the deferred execution of the loop I couldn't see – jolySoft Oct 19 '16 at 11:44
  • "go for readability over hese minor performance improvements" - is the "Enumerable.Range" even more readable? looks like the [community](https://stackoverflow.com/questions/915745/thoughts-on-foreach-with-enumerable-range-vs-traditional-for-loop/36591435) prefers the traditional for loop? – BornToCode Nov 21 '18 at 08:01
1

In my very simple test it looks like for loop is faster by 38ms for 1 000 000 strings.

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;
            }
        }
FCin
  • 3,804
  • 4
  • 20
  • 49