2013-06-19 2 views
0

현재 VS- 프로파일 링 도구로 .net 응용 프로그램을 최적화하려고합니다.if/else 조건을 최적화하려고하면 프로그램이 느려집니다.

if (someObjectContext.someObjectSet.Where(i => i.PNT_ATT_ID == tmp_ATT_ID).OrderByDescending(i => i.Position).Select(i => i.Position).Count() == 0) 
{ 
    lastPosition = 0; 
} 
else 
{ 
    lastPosition = someObjectContext.someObjectSet.Where(i => i.PNT_ATT_ID == tmp_ATT_ID).OrderByDescending(i => i.Position).Select(i => i.Position).Cast<int>().First(); 
} 

내가 이런 식으로 변경 : 매우 자주 호출되는

하나의 기능은 다음과 같은 코드를 포함 나는 변화가 가속화 할 것으로 기대했다

var relevantEntities = someObjectContext.someObjectSet.Where(i => i.PNT_ATT_ID == tmp_ATT_ID).OrderByDescending(i => i.Position).Select(i => i.Position); 
if (relevantEntities.Count() == 0) 
{ 
    lastPosition = 0; 
} 
else 
{ 
    lastPosition = relevantEntities.Cast<int>().First(); 
} 

컴파일러가 쿼리가 두 번 수행되고 결과를 캐시한다는 사실을 확신 할 수 없었기 때문에 약간의 방법이 필요했습니다.

놀랍게도

이 방법의 실행 시간 (inklusive 샘플링 수) 왜 이런 일

사람이 설명 할 수 (프로파일)에 따라 감소하지만,도 9 % 증가되지?

+0

성능에 관계없이 두 번째 예는 DRY 규칙에 따라 작성되고 유지 보수가 용이 한 코드입니다. – JohnFx

+0

각 버전별로 생성 된 IL을 비교하십시오. – harpo

+0

어떻게하면됩니까? – ChNissen

답변

3

컴파일러가 쿼리가 두 번 수행되고 결과가 캐시되는지 여부를 확신 할 수 없으므로 변경 사항이 약간 빨라지기를 바랬습니다.

그렇지 않습니다. 사실 그것은 불가능합니다. 데이터베이스는 두 쿼리에 대해 동일한 결과를 반환하지 않을 수 있습니다. 결과가 첫 번째 쿼리 이후와 두 번째 쿼리 전에 추가되거나 제거되는 것은 전적으로 가능합니다. (이 코드를 비효율적 일뿐만 아니라 잠재적으로 오류가 발생하는 경우가 발생합니다.) 은 실행될 두 개의 쿼리가 모두이므로 결과가 다를 수 있으므로 의 결과가 중요하지 않습니다.을 다시 사용하십시오.

여기서 중요한 점은 지연된 실행의 아이디어입니다. relevantEntities은 쿼리 결과가 아니며 쿼리 자체입니다. IQueryable이 반복 될 때까지 (Count, First, foreach 루프 등의 방법으로) 데이터베이스를 쿼리해야하며 쿼리를 반복 할 때마다 데이터베이스에 대해 다른 쿼리가 수행됩니다.

귀하의 경우에 당신은이 작업을 수행 할 수 있습니다

var lastPosition = someObjectContext.someObjectSet 
    .Where(i => i.PNT_ATT_ID == tmp_ATT_ID) 
    .OrderByDescending(i => i.Position) 
    .Select(i => i.Position) 
    .Cast<int>() 
    .FirstOrDefault(); 

이 제품은 int의 기본 값은 당신이없는 것을시에 값을 설정 한 것 인 0이라는 사실을 활용 전에 일치.

이 쿼리는 기능적으로 동일한 쿼리이므로 두 번 실행하지 않아도됩니다.더 나은 쿼리는 lazyberezovsky에 의해 제안 된 것으로, 먼저 주문하고 복용하는 대신 Max을 사용합니다. 해당 열에 인덱스가있는 경우 차이가 크지 않지만 이 아닐 경우이 아닌 인덱스 순서가 훨씬 더 비쌉니다.

+0

이것은 실제로 정답입니다. 나는 비슷한 것을 쓰는 과정에 있었지만, 당신은 그것을 먼저했습니다. –

+0

+1 저에게'FirstOrDefault()'도이 경우에 작업을 수행 할 것입니다 :) –

+1

@lazyberezovsky 그래, 여기서 중요한 점은 열에 인덱스가 있는지 여부입니다. 주문과 성능에 눈에 띄는 차이가 있을지 여부가 결정됩니다. – Servy

3

Max()을 사용하면 첫 번째 항목을 주문하고 가져가는 대신 최대 위치를 얻고 DefaultIfEmpty()을 사용하면 조건에 일치하는 항목이없는 경우 기본값 인 (int의 경우 0)을 제공 할 수 있습니다. Btw는 시퀀스가 ​​비어 있으면 반환 할 사용자 정의 기본값을 제공 할 수 있습니다.

lastPosition = someObjectContext.someObjectSet 
           .Where(i => i.PNT_ATT_ID == tmp_ATT_ID) 
           .Select(i => i.Position) 
           .Cast<int>() 
           .DefaultIfEmpty() 
           .Max(); 

따라서 당신은 쿼리 실행을 방지 할 수 있습니다 - 어떤 위치에있을 경우 정의 하나를 최신 위치를 얻기위한 또 다른.

관련 문제