your programing

foreach 루프를 사용하는 것보다 AddRange가 더 빠른 이유는 무엇입니까?

lovepro 2020. 12. 26. 16:14
반응형

foreach 루프를 사용하는 것보다 AddRange가 더 빠른 이유는 무엇입니까?


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();

내가 걸릴 때 4 개 에서 결과를 stopwach1하고 stopwach2, stopwatch1항상보다 낮은 값을 갖는다 stopwatch2. 즉, addrange항상보다 빠릅니다 foreach. 이유를 아는 사람이 있습니까?


잠재적으로 AddRange전달 된 값이 IList또는 을 구현하는 위치를 확인할 수 있습니다 IList<T>. 그럴 경우 범위에 얼마나 많은 값이 있는지, 따라서 할당해야하는 공간을 알아낼 수 있습니다. foreach루프는 여러 번 재 할당해야 할 수 있습니다.

또한 할당 후에도 기본 배열로 대량 복사를 수행하는 데 List<T>사용할 수 있습니다 IList<T>.CopyTo( IList<T>물론 을 구현하는 범위의 경우 ).

테스트를 다시 시도하지만 a 대신 Enumerable.Range(0, 100000)for fillData사용 List<T>하면 두 가지가 거의 같은 시간이 소요될 것입니다.


를 사용하는 경우 Add기본 시작 크기 인 10 (IIRC)에서 필요에 따라 내부 배열의 크기를 점진적으로 조정 (두 배)합니다. 사용하는 경우 :

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

근본적으로 변경 될 것으로 예상합니다 (더 이상 크기 조정 / 데이터 복사 없음).

리플렉터 AddRange에서 두 배로 증가하지 않고 내부적으로 수행합니다.

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 ^^^

때문에 AddRange번만 내부 배열의 추가 항목의 크기가 증가 검사 크기.


List AddRange 메서드에 대한 리플렉터에서 분해에는 다음 코드가 있습니다.

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

보시다시피 ensureCapacity () 호출 및 Array.Copy () 사용과 같은 최적화가 있습니다.


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

Using a foreach statement the collection needs to increase size of the collection more than once.

Increasing thr size means copying the complete array which takes time.


This is like asking the waiter to bring you one beer ten times and asking him to bring you 10 beers at once.

What do you think is faster :)


i suppose this is the result of optimisation of memory allocation. for AddRange memory allocates only once, and while foreach on each iteration reallocation is done.

also may be there are some optimisations in AddRange implementation (memcpy for example)


Try out initialize intiial list capacity before manually adding items:

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

It is because the Foreach loop will add all the values that the loop is getting one a time and
the AddRange() method will gather all the values it is getting as a "chunk" and add that chunk at once to the specified location.

Simply understanding, it is just like you have a list of 10 items to bring from the market, which would be faster bringing all that one by one or all at single time.


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

ReferenceURL : https://stackoverflow.com/questions/9836471/why-is-addrange-faster-than-using-a-foreach-loop

반응형