Why is AddRange faster than using a foreach loop?

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 accept 4 results from stopwach1 and stopwach2 , stopwatch1 always has a lower value than stopwatch2 . This means addrange always faster than foreach . Does anyone know why?

+56
Mar 23 '12 at 9:08
source share
10 answers

Potentially, AddRange can check where the passed value implements IList or IList<T> . If so, he can find out how many values ​​are in the range, and therefore how much space he needs to allocate ... whereas the foreach may need to be reallocated several times.

In addition, even after allocation, List<T> can use IList<T>.CopyTo to execute a bulk copy in the base array (for ranges that implement IList<T> , of course.)

I suspect you will find that if you try your test again, but use Enumerable.Range(0, 100000) for fillData instead of List<T> , then the two will take about the same time.

+85
Mar 23 '12 at 9:11
source share

If you use Add , it gradually resizes the internal array as needed (doubling), starting with the default initial size of 10 (IIRC). If you use:

 var manualFill = new List<int>(fillData.Count); 

I expect it to change radically (will no longer change / copy data).

From a 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 ^^^ 
+57
Mar 23 '12 at 9:10
source share

Because AddRange checks the size of the added elements and increases the size of the internal array only once.

+18
Mar 23 '12 at 9:10
source share

Disassembling from the reflector for the List AddRange method has the following code

 ICollection<T> is2 = collection as ICollection<T>; if (is2 != null) { int count = is2.Count; if (count > 0) { this.EnsureCapacity(this._size + count); if (index < this._size) { Array.Copy(this._items, index, this._items, index + count, this._size - index); } if (this == is2) { Array.Copy(this._items, 0, this._items, index, index); Array.Copy(this._items, (int) (index + count), this._items, (int) (index * 2), (int) (this._size - index)); } else { T[] array = new T[count]; is2.CopyTo(array, 0); array.CopyTo(this._items, index); } this._size += count; } } 

As you can see, there are some optimizations like calling EnsureCapacity () and using Array.Copy ().

+6
Mar 23 '12 at 9:16
source share

When using AddRange collection can increase the size of the array once, and then copy the values ​​into it.

Using the foreach operator, collections need to increase the size of the collection more than once.

Increasing thr size means copying a full array that takes time.

+5
Mar 23 '12 at 9:12
source share

It is like asking the waiter to drink beer ten times and asking him to bring you 10 beers immediately.

What do you think faster?)

+4
Mar 23 2018-12-12T00:
source share

I guess this is the result of optimizing memory allocation. AddRange is allocated only once for memory, and while foreach is redistributed at each iteration.

there may also be some optimizations in the implementation of AddRange (e.g. memcpy)

+2
Mar 23 '12 at 9:14
source share

Try initializing the capacity of the internal list before manually adding items:

 var manualFill = new List<int>(fillData.Count); 
+1
Mar 23 '12 at 9:13
source share

This is because the Foreach loop will add all the values ​​that the loop receives once, and
the AddRange () method will collect all the values ​​that it receives as a β€œpiece”, and immediately add this fragment to the specified location.

Just understand, it looks like you have a list of 10 items that you can withdraw from the market, which will be faster to bring it all in one or all at a time.

+1
Jul 15 '14 at 9:30
source share

The AddRange extension does not iterate over each element, but applies each element as a whole. Both foreach and .AddRange have a purpose. Addrange will win a speed contest for your current situation.

More on this here:

Addrange Vs Foreach

-one
Mar 23 '12 at 9:13
source share



All Articles