Why is Enumerable.Range faster than a direct yield loop?
c#, enumerable, ienumerable, performance, range
Solution
Why should `Enumerable.Range` be any slower than your self-made `GetIntRange`? In fact, if `Enumerable.Range` were defined as
public static class Enumerable {
public static IEnumerable<int> Range(int start, int count) {
var end = start + count;
for(var current = start; current < end; ++current) {
yield return current;
}
}
}
then it should be exactly as fast as your self-made `GetIntRange`. This is in fact the reference implementation for `Enumerable.Range`, absent any tricks on the part of the compiler or programmer.
You may want to compare your `GetIntRange` and `System.Linq.Enumerable.Range` with the following implementation (of course, compile in release mode, as Rob points out). This implementation may be slightly optimized with respect to what a compiler would generate from an iterator block.
public static class Enumerable {
public static IEnumerable<int> Range(int start, int count) {
return new RangeEnumerable(start, count);
}
private class RangeEnumerable : IEnumerable<int> {
private int _Start;
private int _Count;
public RangeEnumerable(int start, int count) {
_Start = start;
_Count = count;
}
public virtual IEnumerator<int> GetEnumerator() {
return new RangeEnumerator(_Start, _Count);
}
IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator();
}
}
private class RangeEnumerator : IEnumerator<int> {
private int _Current;
private int _End;
public RangeEnumerator(int start, int count) {
_Current = start - 1;
_End = start + count;
}
public virtual void Dispose() {
_Current = _End;
}
public virtual void Reset() {
throw new NotImplementedException();
}
public virtual bool MoveNext() {
++_Current;
return _Current < _End;
}
public virtual int Current { get { return _Current; } }
object IEnumerator.Current { get { return Current; } }
}
}
Problem
The code below is checking performance of three different ways to do same solution. ``` public static void Main(string[] args) { // for loop { Stopwatch sw = Stopwatch.StartNew(); int accumulator = 0; for (int i = 1; i <= 100000000; ++i) { accumulator += i; } sw.Stop(); Console.WriteLine("time = {0}; result = {1}", sw.ElapsedMilliseconds, accumulator); } //Enumerable.Range { Stopwatch sw = Stopwatch.StartNew(); var ret = Enumerable.Range(1, 100000000).Aggregate(0, (accumulator, n) => accumulator + n); sw.Stop(); Console.WriteLine("time = {0}; result = {1}", sw.ElapsedMilliseconds, ret); } //self-made IEnumerable<int> { Stopwatch sw = Stopwatch.StartNew(); var ret = GetIntRange(1, 100000000).Aggregate(0, (accumulator, n) => accumulator + n); sw.Stop(); Console.WriteLine("time = {0}; result = {1}", sw.ElapsedMilliseconds, ret); } } private static IEnumerable<int> GetIntRange(int start, int count) { int end = start + count; for (int i = start; i < end; ++i) { yield return i; } } } ``` The results are: ``` time = 306; result = 987459712 time = 1301; result = 987459712 time = 2860; result = 987459712 ``` It is not surprising that the "for loop" is faster than the other two solutions, because Enumerable.Aggregate takes more method invocations. However, it really surprises me that "Enumerable.Range" is faster than the "self-made IEnumerable". I thought that Enumerable.Range would have more overhead than the simple GetIntRange method. What are the possible reasons for this?