ToList ()를 호출 할 때 성능에 영향이 있습니까?


139

를 사용할 때 ToList()고려해야 할 성능 영향이 있습니까?

디렉토리에서 파일을 검색하는 쿼리를 작성했습니다. 이는 쿼리입니다.

string[] imageArray = Directory.GetFiles(directory);

그러나 List<>대신 작업을 좋아하기 때문에 ...

List<string> imageList = Directory.GetFiles(directory).ToList();

따라서 이와 같은 변환을 결정할 때 고려해야 할 일종의 성능 영향이 있습니까? 아니면 많은 파일을 처리 할 때만 고려해야합니까? 무시할만한 전환입니까?


+1도 여기에 대한 답변을 알고 싶습니다. 앱이 성능이 중요하지 않은 경우 IMHO는 항상 코드를보다 논리적 / 읽기 / 유지 가능하게 만드는 경우 List<T>를 선호 T[]합니다 (물론 변환 으로 인해 눈에 띄는 성능 문제 발생 하지 않는 한) 나는 그것을 방문한다).
Sepster

배열에서 목록을 만드는 것은 매우 저렴합니다.
leppie

2
@Sepster 작업을 수행하는 데 필요한 데이터 유형 만 지정합니다. Add또는 전화 할 필요가없는 경우 Remove다음과 같이 남겨 두십시오 IEnumerable<T>(또는 더 나은 방법 var)
pswg

4
이 경우에 EnumerateFiles대신 호출 하는 것이 좋습니다 GetFiles. 따라서 하나의 배열 만 만들어집니다.
tukaef

3
GetFiles(directory).NET에서 현재 구현되어 있으므로 거의 수행 new List<string>(EnumerateFiles(directory)).ToArray()합니다. 따라서 GetFiles(directory).ToList()리스트를 생성하고 그로부터 배열을 생성 한 다음리스트를 다시 생성합니다. 2kay가 말했듯이, 당신은 EnumerateFiles(directory).ToList()여기서 하는 것을 선호해야 합니다.
Joren

답변:


178

IEnumerable.ToList()

예, IEnumerable<T>.ToList()성능에 영향을 미치지 만 성능에 중요한 작업에만주의를 기울여야 하는 O (n) 작업입니다.

ToList()작업은 사용 List(IEnumerable<T> collection)생성자를. 이 생성자는 배열의 사본을 만들어야합니다 (보다 일반적으로 IEnumerable<T>). 그렇지 않으면 원본 배열의 향후 수정 사항 T[]도 소스에서 변경되어 일반적으로 바람직하지 않습니다.

나는 이것을 거대한 목록으로 만 변화시킬 것이라고 반복하고 싶습니다. 메모리 덩어리를 복사하는 것은 매우 빠른 작업입니다.

편리한 팁 AsvsTo

LINQ에는 As(와 같은 AsEnumerable()) 및 To(과 같은 ToList()) 로 시작하는 몇 가지 방법이 있습니다. 로 시작하는 메소드는 To위와 같이 변환 이 필요하며 (즉, 성능에 영향을 줄 수 있음),로 시작하는 메소드는 As캐스트 또는 간단한 조작이 필요하지 않습니다.

추가 정보 List<T>

List<T>관심있는 경우 어떻게 작동 하는지에 대한 자세한 내용은 다음과 같습니다. :)

A는 List<T>또한 필요에 따라 크기를 조정해야하는 동적 배열라는 구조를 사용하여,이 크기 조정 이벤트 복사 새로운 배열로 기존 배열의 내용. 따라서 작은 크기로 시작하여 필요한 경우 크기가 늘어납니다 .

CapacityCount속성 의 차이점 입니다 List<T>. Capacity장면 뒤의 배열 크기를 나타내며, Count항목 수 List<T>는 항상 <= Capacity입니다. 따라서 항목을 목록에 추가하고을 지나서 늘리면 Capacity의 크기 List<T>가 두 배가되고 배열이 복사됩니다.


2
방금 생성 List(IEnumerable<T> collection)매개 변수가 컬렉션 매개 변수인지 확인한 ICollection<T>다음 필요한 크기의 새 내부 배열을 즉시 만듭니다. 매개 변수 콜렉션이 아닌 ICollection<T>경우 생성자는이를 반복하고 Add각 요소를 호출 합니다.
Justinas Simanavicius

ToList ()를 오해의 소지가있는 작업으로 간주 할 수 있다는 점에 유의해야합니다. LINQ 쿼리를 통해 IEnumerable <>을 만들 때 발생합니다. linq 쿼리가 구성되었지만 실행되지 않았습니다. ToList ()를 호출하면 쿼리가 실행되므로 리소스를 많이 사용하는 것 같습니다. 그러나 실제로는 막대한 목록이 아닌 한 집중적이면서 ToList () 작업이 아닌 쿼리입니다.
dancer42

36

toList ()를 호출 할 때 성능에 영향이 있습니까?

네 물론 이죠 이론적으로 i++는 성능에 영향을 미치므로 몇 번의 틱으로 인해 프로그램 속도가 느려집니다.

무엇을 .ToList합니까?

당신이 호출 할 때 .ToList, 코드 Enumerable.ToList()는 확장 메소드 인 호출 을한다 return new List<TSource>(source). 해당 생성자 에서 최악의 상황 에서 항목 컨테이너를 통해 하나씩 새 컨테이너에 추가합니다. 따라서 그 동작은 성능에 거의 영향을 미치지 않습니다. 응용 프로그램의 성능 병목이되는 것은 불가능합니다.

질문의 코드에 어떤 문제가 있습니까?

Directory.GetFiles폴더를 통해 모든 파일의 이름을 즉시 메모리에 반환 하면 string []에 많은 메모리가 소비되어 모든 것이 느려질 수 있습니다.

그때해야 할 일

때에 따라 다르지. 비즈니스 로직뿐만 아니라 폴더의 파일 크기가 항상 작다는 것을 보증하면 코드를 사용할 수 있습니다. 그러나 여전히 Directory.EnumerateFilesC # 4에서 게으른 버전을 사용하는 것이 좋습니다 . 이것은 쿼리와 훨씬 유사하며 즉시 실행되지 않으며 다음과 같이 쿼리를 더 추가 할 수 있습니다.

Directory.EnumerateFiles(myPath).Any(s => s.Contains("myfile"))

이름에 "myfile"이 포함 된 파일을 찾으면 경로 검색 이 중지 됩니다. 이것은 분명히 더 나은 성능을 가지고 .GetFiles있습니다.


19

toList ()를 호출 할 때 성능에 영향이 있습니까?

그렇습니다. 확장 메소드를 사용하면 소스 콜렉션 에서 Enumerable.ToList()List<T>오브젝트를 구성 IEnumerable<T>하고 성능에 영향을줍니다.

그러나 이해 List<T>하면 성능에 미치는 영향이 큰지 판단하는 데 도움이 될 수 있습니다.

List<T>배열 ( T[])을 사용하여 목록의 요소를 저장합니다. 배열은 할당 된 후에는 확장 할 수 없으므로 너무 List<T>큰 배열을 사용하여 목록의 요소를 저장합니다. (가) 때 List<T>크기 이상으로 기본 배열을 성장하는 새로운 배열을 할당해야하고 이전 배열의 내용은 목록이 성장하기 전에 새로운 큰 배열에 복사 할 수 있습니다.

새로운 List<T>것이 만들어 지면 IEnumerable<T>두 가지 경우가 있습니다.

  1. 소스 컬렉션 은 ICollection<T>다음을 구현합니다 . 그런 다음 소스 컬렉션 ICollection<T>.Count의 정확한 크기를 가져 오는 데 사용되고 소스 컬렉션의 모든 요소가를 사용하여 백업 배열에 복사되기 전에 일치하는 백업 배열이 할당됩니다 ICollection<T>.CopyTo(). 이 작업은 매우 효율적이며 메모리 블록 복사를위한 일부 CPU 명령에 매핑 될 수 있습니다. 그러나 성능면에서 새 어레이에는 메모리가 필요하고 모든 요소를 ​​복사하려면 CPU주기가 필요합니다.

  2. 그렇지 않으면 소스 콜렉션의 크기를 알 수 없으며 열거 IEnumerable<T>자는 각 소스 요소를 새 소스에 한 번에 하나씩 추가하는 데 사용됩니다 List<T>. 처음에 백업 배열이 비어 있고 크기가 4 인 배열이 만들어집니다. 그런 다음이 배열이 너무 작 으면 크기가 두 배가되므로 4, 8, 16, 32 등과 같이 백업 배열이 커집니다. 백업 배열이 커질 때마다 다시 할당해야하고 지금까지 저장된 모든 요소를 ​​복사해야합니다. 이 작업은 정확한 크기의 배열을 즉시 만들 수있는 첫 번째 경우와 비교하여 훨씬 비용이 많이 듭니다.

    또한 소스 컬렉션에 33 개의 요소가 포함되어 있으면 메모리를 낭비하는 64 개의 요소 배열이 목록에 표시됩니다.

귀하의 경우 소스 컬렉션은 구현하는 배열 ICollection<T>이므로 소스 배열이 너무 크지 않으면 성능에 영향을 미치지 않습니다. 호출 ToList()하면 단순히 소스 배열을 복사하여 List<T>객체로 래핑 합니다. 두 번째 경우의 성능조차도 작은 컬렉션에 대해 걱정할 것이 아닙니다.


5

"고려해야 할 성능 영향이 있습니까?"

정확한 시나리오의 문제는 무엇보다도 성능에 대한 실제 관심사가 드라이브 캐시의 하드 드라이브 속도와 효율성에 있다는 것입니다.

이러한 관점에서 볼 때 NO는 고려할 필요가 없다는 점에서 그 영향을 무시할 수 있습니다.

그러나 List<>생산성을 높이거나 알고리즘을 더 친숙하게 만들거나 다른 이점을 얻기 위해 구조 의 기능이 실제로 필요한 경우에만 해당됩니다 . 그렇지 않으면, 아무 이유없이 의도적으로 중요하지 않은 성능 적중을 추가하는 것입니다. 어떤 경우에는 당연히 그렇게해서는 안됩니다! :)


4

ToList()새로운 List를 만들고 그 안에 요소를 넣습니다. 이는 관련 비용이 있음을 의미합니다 ToList(). 작은 컬렉션의 경우 비용이 많이 들지 않지만 큰 컬렉션을 보유하면 ToList를 사용할 때 성능이 저하 될 수 있습니다.

콜렉션을 List로 변환하지 않고 수행 할 수없는 작업이 아니면 일반적으로 ToList ()를 사용하지 마십시오. 예를 들어 컬렉션을 반복하려는 경우 ToList를 수행 할 필요가 없습니다.

데이터 소스 (예 : LINQ to SQL을 사용하는 데이터베이스)에 대해 쿼리를 수행하는 경우 지연된 실행을 수행하는 대신 LINQ to SQL과 함께 ToList를 사용할 때 (예 : 필요할 때 항목로드) ToList를 수행하는 비용이 훨씬 더 큽니다. 많은 시나리오에서) 데이터베이스에서 메모리로 항목을 즉시로드합니다.


Haris : ToList ()를 호출 한 후 원본 소스에 어떤 일이 발생하는지 원본 소스에 대해 잘 모르겠습니다
TalentTuner

@Saurabh GC는 그것을 청소합니다
pswg

@Saurabh는 원본 소스에는 아무 일도 일어나지 않습니다. 원본 출처의 요소는 새로 생성 된 목록
Haris Hasan

"컬렉션을 반복하려면 ToList를 수행 할 필요가 없습니다"-어떻게 반복해야합니까?
SharpC

4

다음과 같이 비효율적입니다.

var list = new List<T>(items);

를 사용하는 생성자의 소스 코드를 디스 어셈블하면 IEnumerable<T>몇 가지 작업을 수행 할 수 있습니다.

  • 전화는 collection.Count, 그렇다면 collection이며 IEnumerable<T>, 그것은 실행을 강제 할 것이다. 경우 collection배열리스트 등이며 그되어야 O(1).

  • 경우 collection용구 ICollection<T>, 상기 사용 내부 배열 항목 저장할 ICollection<T>.CopyTo방법. 그것은 해야O(n)것, n컬렉션의 길이.

  • 경우 collection구현하지 않습니다 ICollection<T>, 그것은 컬렉션의 항목을 통해 반복되며, 내부 목록에 추가됩니다.

따라서 새로운 목록을 만들어야하기 때문에 더 많은 메모리를 소비 하며 최악의 경우O(n)collection 에는 각 요소의 복사본을 만들기 위해 반복됩니다 .


3
가까운 0(n)곳에 n원래 컬렉션에서 문자열 바이트 총합 요소하지 카운트 차지하다 (물론 더 엄밀하게 N = 바이트 / 워드 크기)
user1416420

@ user1416420 내가 틀렸을 수도 있지만 왜 그럴까요? 그것은 (예. 다른 유형의 모음 어떤 경우 bool, int등)? 컬렉션의 각 문자열을 실제로 복사 할 필요는 없습니다. 새 목록에 추가하기 만하면됩니다.
Oscar Mederos

여전히 새로운 메모리 할당 및 바이트 복사는 중요하지 않습니다.이 방법을 죽이는 것입니다. 부울도 .NET에서 4 바이트를 차지합니다. 실제로 .NET에서 객체의 각 참조 길이는 8 바이트 이상이므로 상당히 느립니다. 첫 번째 4 바이트는 유형 테이블을 가리키고 두 번째 4 바이트는 값을 찾을 수있는 값 또는 메모리 위치를 나타냅니다.
user1416420

3

파일 목록 검색 성능을 고려하면 ToList()무시할 수 있습니다. 그러나 실제로 다른 시나리오에는 해당되지 않습니다. 그것은 실제로 당신이 그것을 사용하는 곳에 달려 있습니다.

  • 배열, 목록 또는 다른 컬렉션을 호출 할 때 컬렉션의 복사본을로 만듭니다 List<T>. 여기에서의 성능은 목록의 크기에 따라 다릅니다. 정말로 필요할 때해야합니다.

    귀하의 예에서는 배열에서 호출합니다. 배열을 반복하고 항목을 하나씩 새로 만든 목록에 추가합니다. 따라서 성능 영향은 파일 수에 따라 다릅니다.

  • 를 호출 하면 (보통 쿼리) IEnumerable<T>구체화 합니다 IEnumerable<T>.


2

ToList 새 목록을 작성하고 원래 소스에서 새로 작성된 목록으로 요소를 복사하므로 원본 소스에서 요소를 복사하는 것만이며 소스 크기에 따라 다릅니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.