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:
Why is Enumerable.Range faster than a direct yield loop?
Enumerable.Range implementation
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.
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.
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;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With