var fillData = new List<int>(); for (var i = 0; i < 100000; i++) fillData.Add(i); var stopwatch1 = new Stopwatch(); stopwatch1.Start(); var autoFill = new List<int>(); autoFill.AddRange(fillData); stopwatch1.Stop(); var stopwatch2 = new Stopwatch(); stopwatch2.Start(); var manualFill = new List<int>(); foreach (var i in fillData) manualFill.Add(i); stopwatch2.Stop();
When I take 4 results from stopwach1
and stopwach2
, stopwatch1
has always lower value than stopwatch2
. That means addrange
is always faster than foreach
. Does anyone know why?
This foreach loop is faster because the local variable that stores the value of the element in the array is faster to access than an element in the array. The forloop is faster than the foreach loop if the array must only be accessed once per iteration.
As it turned out, FOREACH is faster on arrays than FOR with length chasing. On list structures, FOREACH is slower than FOR. The code looks better when using FOREACH, and modern processors allow using it. However, if you need to highly optimize your codebase, it is better to use FOR.
The difference between for and foreach in C# is that for loop is used as a general purpose control structure while foreach loop is specifically used for arrays and collections. In brief, both helps to execute code repeatedly but foreach loop is more specific to arrays and collections.
In cases where you work with a collection of objects, foreach is better, but if you increment a number, a for loop is better.
Potentially, AddRange
can check where the value passed to it implements IList
or IList<T>
. If it does, it can find out how many values are in the range, and thus how much space it needs to allocate... whereas the foreach
loop may need to reallocate several times.
Additionally, even after allocation, List<T>
can use IList<T>.CopyTo
to perform a bulk copy into the underlying array (for ranges which implement IList<T>
, of course.)
I suspect you'll find that if you try your test again but using Enumerable.Range(0, 100000)
for fillData
instead of a List<T>
, the two will take about the same time.
If you are using Add
, it is resizing the inner array gradually as needed (doubling), from the default starting size of 10 (IIRC). If you use:
var manualFill = new List<int>(fillData.Count);
I expect it'll change radically (no more resizes / data copy).
From reflector, AddRange
does this internally, rather than growing in doubling:
ICollection<T> is2 = collection as ICollection<T>; if (is2 != null) { int count = is2.Count; if (count > 0) { this.EnsureCapacity(this._size + count); // ^^^ this the key bit, and prevents slow growth when possible ^^^
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