LINQ 함수의 순서가 중요합니까?


114

기본적으로 질문에서 알 수 있듯이 ... LINQ 함수의 순서가 성능 측면에서 중요 합니까? 분명히 결과는 여전히 동일해야합니다 ...

예:

myCollection.OrderBy(item => item.CreatedDate).Where(item => item.Code > 3);
myCollection.Where(item => item.Code > 3).OrderBy(item => item.CreatedDate);

둘 다 동일한 결과를 반환하지만 LINQ 순서가 다릅니다. 일부 항목을 다시 주문하면 결과가 달라질 수 있음을 알고 있으며 이에 대해 걱정하지 않습니다. 내 주요 관심사는 동일한 결과를 얻을 때 주문이 성능에 영향을 미칠 수 있는지 아는 것입니다. 그리고 내가 만든 2 개의 LINQ 호출 (OrderBy, Where)뿐만 아니라 모든 LINQ 호출에서도 마찬가지입니다.


9
멋진 질문입니다.
Robert S.

공급자의 최적화가 var query = myCollection.OrderBy(item => item.Code).Where(item => item.Code == 3);.
Mark Hurd

1
찬성 투표 :), 흥미로운 질문이 있습니다. Linq를 EF의 Entities에 쓸 때 고려할 것입니다.
GibboK 2011 년

1
@GibboK : LINQ 쿼리를 "최적화"하려고 할 때주의하십시오 (아래 답변 참조). 때때로 당신은 실제로 아무것도 최적화하지 않습니다. 최적화를 시도 할 때 프로파일 러 도구를 사용하는 것이 가장 좋습니다.
myermian

답변:


147

사용중인 LINQ 공급자에 따라 다릅니다. LINQ to Objects의 경우 확실히 차이를 만들 수 있습니다. 실제로 다음이 있다고 가정합니다.

var query = myCollection.OrderBy(item => item.CreatedDate)
                        .Where(item => item.Code > 3);

var result = query.Last();

이를 위해서는 전체 컬렉션을 정렬 한 다음 필터링해야합니다. 백만 개의 항목이 있고 그중 하나에 만 3보다 큰 코드가있는 경우 버려 질 결과를 주문하는 데 많은 시간을 낭비하게됩니다.

반전 된 작업과 비교하여 먼저 필터링합니다.

var query = myCollection.Where(item => item.Code > 3)
                        .OrderBy(item => item.CreatedDate);

var result = query.Last();

이번에는 필터링 된 결과 만 정렬합니다. "필터와 일치하는 단일 항목"의 샘플 경우 시간과 공간 모두에서 훨씬 더 효율적입니다.

또한 쿼리가 올바르게 실행되는지 여부에 차이를 만들 있습니다. 치다:

var query = myCollection.Where(item => item.Code != 0)
                        .OrderBy(item => 10 / item.Code);

var result = query.Last();

괜찮습니다. 0으로 나누지 않을 것임을 알고 있습니다. 그러나 필터링 전에 순서를 지정 하면 쿼리에서 예외가 발생합니다.


2
@Jon Skeet, 각 LINQ 공급자 및 함수에 대한 Big-O에 대한 문서가 있습니까? 아니면 단순히 "각 표현이 상황에 따라 다르다"는 경우 일뿐입니다.
michael

1
@michael : 명확하게 문서화되어 있지는 않지만 제 "Edulinq"블로그 시리즈를 읽으면 합리적으로 자세히 이야기한다고 생각합니다.
Jon Skeet 2011 년

3
@michael : 당신은 여기에서 찾을 수 있습니다 msmvps.com/blogs/jon_skeet/archive/tags/Edulinq/default.aspx
VoodooChild

3
@gdoron : 솔직히 말해서 당신이 의미하는 바가 명확하지 않습니다. 새 질문을 작성하고 싶은 것 같습니다. 마음에 곰 Queryable에서이 시도되지 않는다는 해석 전혀 쿼리를 - 그 작업은 전적으로 다른 무언가가 그것을 해석 할 수 있도록 쿼리를 보존 할 수 있습니다. 또한 LINQ to Objects는 식 트리도 사용하지 않습니다.
Jon Skeet

1
@gdoron : 요점은 그것이 Queryable의 직업이 아니라 공급자의 직업이라는 것입니다. Entity Framework를 사용할 때도 중요하지 않습니다. 그것은 수행 하지만 객체에 LINQ에 대한 문제. 그러나 네, 꼭 다른 질문을하십시오.
Jon Skeet

17

예.

그러나 성능 차이가 정확히 무엇인지 는 LINQ 공급자가 기본 식 트리를 평가하는 방법에 따라 다릅니다.

예를 들어 쿼리는 LINQ-to-XML의 경우 두 번째 (WHERE 절을 먼저 사용) 더 빠르게 실행되지만 LINQ-to-SQL의 경우 처음에는 더 빠르게 실행될 수 있습니다.

성능 차이가 무엇인지 정확히 알아 보려면 애플리케이션을 프로파일 링해야 할 것입니다. 하지만 그런 것과 마찬가지로 조기 최적화는 일반적으로 노력할 가치가 없습니다. LINQ 성능 이외의 문제가 더 중요하다는 것을 알 수 있습니다.


5

특정 예 에서는 성능에 차이를 만들 수 있습니다 .

첫 번째 쿼리 : OrderBy호출 이 3 이하인 항목을 포함 하여 전체 소스 시퀀스 를 반복해야합니다 Code. Where절은 또한 반복 할 필요가 전체가 순서를 명령했다.

두 번째 쿼리 : Where호출 Code은 3보다 큰 항목으로 만 시퀀스를 제한합니다 . 그러면 호출에서 OrderBy반환 된 축소 시퀀스 만 통과하면됩니다 Where.


3

Linq-To-Objects에서 :

정렬은 다소 느리고 O(n)메모리를 사용 합니다. Where반면에 비교적 빠르며 일정한 메모리를 사용합니다. 따라서 Where먼저 수행하는 것이 더 빠르며 대규모 컬렉션의 경우 훨씬 더 빠릅니다.

큰 개체 힙 (컬렉션과 함께)에 대한 할당은 내 경험상 상대적으로 비용이 많이 들기 때문에 감소 된 메모리 압력도 중요 할 수 있습니다.


1

분명히 결과는 여전히 동일해야합니다 ...

이것은 실제로 사실이 아닙니다. 특히 다음 두 줄은 다른 결과를 제공합니다 (대부분의 공급자 / 데이터 세트에 대해).

myCollection.OrderBy(o => o).Distinct();
myCollection.Distinct().OrderBy(o => o);

1
아니요, 최적화를 고려하려면 결과가 동일해야합니다. 무언가를 "최적화"하고 다른 결과를 얻는 것은 의미가 없습니다.
michael

1

LINQ 쿼리를 최적화하는 방법 을 고려할 때주의해야한다는 점은 주목할 가치가 있습니다. 예를 들어 LINQ의 선언적 버전을 사용하여 다음을 수행하는 경우 :

public class Record
{
    public string Name { get; set; }
    public double Score1 { get; set; }
    public double Score2 { get; set; }
}


var query = from record in Records
            order by ((record.Score1 + record.Score2) / 2) descending
            select new
                   {
                       Name = record.Name,
                       Average = ((record.Score1 + record.Score2) / 2)
                   };

어떤 이유로 든 평균을 변수에 먼저 저장하여 쿼리를 "최적화"하기로 결정했다면 원하는 결과를 얻지 못할 것입니다.

// The following two queries actually takes up more space and are slower
var query = from record in Records
            let average = ((record.Score1 + record.Score2) / 2)
            order by average descending
            select new
                   {
                       Name = record.Name,
                       Average = average
                   };

var query = from record in Records
            let average = ((record.Score1 + record.Score2) / 2)
            select new
                   {
                       Name = record.Name,
                       Average = average
                   }
            order by average descending;

많은 사람들이 개체에 선언적 LINQ를 사용하는 것은 아니지만 생각하기에 좋은 음식입니다.


0

관련성에 따라 다릅니다. 코드 = 3 인 항목이 매우 적다고 가정하면 다음 주문은 작은 컬렉션 집합에서 작동하여 날짜별로 주문을받습니다.

동일한 CreatedDate를 가진 항목이 많은 경우 다음 주문은 더 큰 컬렉션 집합에서 작동하여 날짜별로 주문을 가져옵니다.

따라서 두 경우 모두 성능에 차이가 있습니다.

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