2012-10-05 5 views
4

일반 목록 산술 연산을 최적화하려고합니다. 아래에 정의 된대로 nullable double의 3 개 목록이 있습니다.일반 목록 성능 최적화

List<double?> list1 = new List<double?>(); 
List<double?> list2 = new List<double?>(); 
List<double?> listResult = new List<double?>(); 

int recordCount = list1.Count > list2.Count ? list2.Count : list1.Count; 

for (int index = 0; index < recordCount; index++) 
{ 
     double? result = list1[index] + list2[index]; 
     listResult.Add(result); 
} 

거대한 목록이있는 경우이 작업을 더 빠르게 실행할 수있는 방법이 있습니까?

입력 해 주셔서 감사합니다.

+0

목록을 추가하기 전에 목록을 채우는 방법은 무엇입니까? 데이터가 데이터베이스에서 나온 것이라면 결과를 dB에서 합산하는 것이 더 빠릅니다. –

+0

2 개의 목록의 크기가 다른 경우이 코드는 ArrayOutOfBoundsException을 생성합니다. –

+1

@juergend 아니요, 그는 처음에는 어떤 목록이 더 짧은 지 찾아 보았습니다. 5 – Jay

답변

9

내가 거대한 목록이있는 경우이 작업이 빠르게 실행 할 수 있도록 어떤 방법이 있나요?

당신은 당신의 계산이 끝날 때까지 결과에 대한 목록 작성을 움직일 수 :

List<double?> list1 = new List<double?>(); 
List<double?> list2 = new List<double?>(); 

int recordCount = list1.Count > list2.Count ? list2.Count : list1.Count; 
List<double?> listResult = new List<double?>(recordCount); 

이 당신이 결과를 위해 필요한 정확한 용량을 지정하고 목록 자체 내에서 재 할당을 방지 할 수있다. "거대한 목록"의 경우 이것은 가장 느린 부분 중 하나 일 가능성이 높습니다. 목록이 커질수록 메모리 할당 및 복사본이 가장 느린 작업이 될 것이기 때문입니다. 계산은 간단 경우

또한, 당신은 잠재적으로 여러 개의 코어를 사용할 수 있습니다

List<double?> list1 = new List<double?>(); 
List<double?> list2 = new List<double?>(); 

int recordCount = list1.Count > list2.Count ? list2.Count : list1.Count; 

var results = new double?[recordCount]; // Use an array here 

Parallel.For(0, recordCount, index => 
    { 
     double? result = list1[index] + list2[index]; 
     results[index] = result; 
      }); 

은 "작업"여기 너무 간단 감안할를, 당신은 아마 실제로 가장 나가 사용자 정의 파티션 프로그램을 필요 병렬 처리의 (자세한 내용은 How to: Speed Up Small Loop Bodies 참조)

var results = new double?[recordCount]; // Use an array here 
var rangePartitioner = Partitioner.Create(0, recordCount); 

Parallel.ForEach(rangePartitioner, range => 
    { 
     for (int index = range.Item1; index < range.Item2; index++) 
     { 
      results[index] = list1[index] + list2[index]; 
     } 
      }); 

를이하지만, 병목 현상이 아닌 경우에는 한 줄로이 작업을 수행하기 위해 LINQ를 사용할 수 있습니다

var results = list1.Zip(list2, (one, two) => one + two).ToList(); 

그러나 성능이 실제로 병목 현상 인 경우 루프 처리보다 성능이 약간 떨어집니다. 당신이 할 수

+0

목록 realloc에서 성장/지수/fibonnaci/기타 확장 성장 시스템의 일부 유형을 사용할 것이라고 확신합니다. 네가 생각하는대로 나쁘다. 그러나 나는 한 번 할당하는 것이 좋은 최적화라고 쉽게 동의합니다. – mattypiper

+0

@mattypiper - 그것은 4 개의 요소로 시작하고 매번 두 배가됩니다. 그러나 매우 큰 목록의 경우 각 재 할당에 기존 목록의 사본이 필요하므로 값이 비쌀 수 있습니다. –

+0

+1 'Zip' 팁의 경우, –

0

미리 목록의 크기를 알고 있다면 간단한 배열 이 더 빨리 실행되어야합니다. 다음과 같이 만든 :

double?[] Array1 = new double?[10]; 
0

우선)를 호출합니다 (이것은에는 list.add의 각 시간을 절약 결과에 대한 공간을 미리 할당합니다

List<double?> listResult = new List<double?>(recordCount); 

당신의 결과 목록의 용량을 지정합니다.

성능이 정말로 걱정된다면 목록을 청크로 분해하고 여러 스레드를 실행하여 부분 결과 집합을 수행 한 다음 완료되면 전체 집합을 다시 병합 할 수 있습니다.

0
var result = from i in 
      Enumerable.Range(0, Math.Min(list1.Count, list2.Count)) 
      select list1.ElementAtOrDefault(i) + list2.ElementAtOrDefault(i); 
foreach (var item in result) 
{ 
Debug.Write(item.Value); 
} 
0

대신 목록의 원시 배열을 사용할 수있는 능력이 있다면, 당신은 확실히 할 수있는이 빠르게 - 낮은 수준의 당신이 가고 싶은 방법에 따라 달라집니다 얼마나. 당신의 근원에있는 약간 버그를 정정해서, 나는 3 개의 다른 버전을 썼다. 하나는 결과에 대한 새 목록을 작성하여 코드가 수행하는 방식입니다 (중간 용량 할당을 방지하면서 용량을 사용하는 생성자를 자유롭게 사용했습니다).

또한 List를 제거하면 효율성이 향상된다는 관점에서 두 배열을 세 번째로 합한 버전을 작성했습니다.

마지막으로 안전하지 않은 코드를 사용하여 포인터를 사용하여 두 번째 배열에 첫 번째 배열을 추가하는 버전을 작성했습니다.

비교의 결과는 아래와 같다 : I 사용

Timings: 500000 iterations of 10000-item lists 
    Lists:   00:00:13.9184166 
    Arrays (safe): 00:00:08.4868231 
    Arrays (unsafe): 00:00:03.0901603 

Press any key to continue... 

코드는 this Github gist에서 찾을 수있다.

안전하지 않은 코드는 너무 많은 최적화가 될 수 있지만이 세 가지 샘플의 차이점을 확인하는 것은 매우 놀라운 방법입니다. 명확성을 위해 안전한 코드를 사용하고 배열을 사용하는 것이 좋습니다.