2010-04-13 2 views
7

int[]의 두 배열을 추가하는 것보다 C#이 두 개의 배열 UInt16[]을 더 빨리 추가하는 것 같습니다. 이것은 배열이 워드로 정렬 될 것이라고 가정했기 때문에 나에게 의미가 없으므로 int[]은 CPU에서 더 적은 작업이 필요합니까? UInt16 배열이 int 배열보다 빠르게 추가되는 이유는 무엇입니까?

나는 아래의 테스트 코드를 실행, 다음과 같은 결과를 얻었다 :

Int for 1000 took 9896625613 tick (4227 msec) 
UInt16 for 1000 took 6297688551 tick (2689 msec) 

테스트 코드는 다음을 수행합니다

  1. 한 번 ab을,라는 두 개의 배열을 작성합니다.
  2. 무작위 데이터로 한 번 채 웁니다.
  3. 스톱워치를 시작합니다.
  4. ab을 항목별로 추가합니다. 이것은 1000 번 수행됩니다.
  5. 스톱워치를 중지합니다.
  6. 소요 기간을보고합니다.

int[] a, bUInt16 a,b에 대해 수행됩니다. 그리고 마다 번 코드를 실행하면 UInt16 어레이의 테스트는 int 어레이보다 30 % -50 % 적은 시간이 소요됩니다. 이걸 나에게 설명해 줄 수 있니? 당신이 만약 자신을 위해 시도 할 경우

여기에 코드입니다 :

public static UInt16[] GenerateRandomDataUInt16(int length) 
{ 
    UInt16[] noise = new UInt16[length]; 
    Random random = new Random((int)DateTime.Now.Ticks); 
    for (int i = 0; i < length; ++i) 
    { 
     noise[i] = (UInt16)random.Next(); 
    } 

    return noise; 
} 

public static int[] GenerateRandomDataInt(int length) 
{ 
    int[] noise = new int[length]; 
    Random random = new Random((int)DateTime.Now.Ticks); 
    for (int i = 0; i < length; ++i) 
    { 
     noise[i] = (int)random.Next(); 
    } 

    return noise; 
} 

public static int[] AddInt(int[] a, int[] b) 
{ 
    int len = a.Length; 
    int[] result = new int[len]; 
    for (int i = 0; i < len; ++i) 
    { 
     result[i] = (int)(a[i] + b[i]); 
    } 
    return result; 
} 

public static UInt16[] AddUInt16(UInt16[] a, UInt16[] b) 
{ 
    int len = a.Length; 
    UInt16[] result = new UInt16[len]; 
    for (int i = 0; i < len; ++i) 
    { 
     result[i] = (ushort)(a[i] + b[i]); 
    } 
    return result; 
} 


public static void Main() 
{ 
    int count = 1000; 
    int len = 128 * 6000; 

    int[] aInt = GenerateRandomDataInt(len); 
    int[] bInt = GenerateRandomDataInt(len); 

    Stopwatch s = new Stopwatch(); 
    s.Start(); 
    for (int i=0; i<count; ++i) 
    { 
     int[] resultInt = AddInt(aInt, bInt); 
    } 
    s.Stop(); 
    Console.WriteLine("Int for " + count 
       + " took " + s.ElapsedTicks + " tick (" 
       + s.ElapsedMilliseconds + " msec)"); 

    UInt16[] aUInt16 = GenerateRandomDataUInt16(len); 
    UInt16[] bUInt16 = GenerateRandomDataUInt16(len); 

    s = new Stopwatch(); 
    s.Start(); 
    for (int i=0; i<count; ++i) 
    { 
     UInt16[] resultUInt16 = AddUInt16(aUInt16, bUInt16); 
    } 
    s.Stop(); 
    Console.WriteLine("UInt16 for " + count 
       + " took " + s.ElapsedTicks + " tick (" 
       + s.ElapsedMilliseconds + " msec)"); 


} 
+2

배열을 전달하고 반환하는 AddXXX 함수를 호출하지 않고도 인라인 요소를 추가하려고 했습니까? 다른 크기의 배열을 사용해 보셨습니까? –

+0

@ Grzegorz Gierlik : 참으로 좋은 질문입니다. 그대로,'int' 루틴은 아마도 두 배의 메모리를 할당해야 할 것입니다. –

+2

어떤 하드웨어가 있습니까? 나는 15650msec와 14657msec에 도착한다. (큰 차이는 없다.) 나는 microbenchmark가 당신을 쫓아 내고 있다고 의심합니다 - JIT 엔진과 최적화 VM은 그것에 대해 악명 높습니다. 현대 x86/x64 CPU에서 숫자 (16/32 비트) *를 추가하는 속도는 동일합니다 *. 그러나 더 큰 숫자는 더 많은 캐시 라인을 채우고 더 많은 버스를 전송해야하는 측면에서 작은 페널티를 나타낼 수 있습니다. –

답변

6

어떤 일이 벌어지는지는 새는 추상화입니다. UInt16은 int가 수행하는 메모리의 절반을 차지합니다 (16 비트 vs. 32 비트).

즉, int16 배열이 차지하는 메모리 영역은 int32가 차지하는 영역의 절반을 차지합니다. 따라서 더 많은 영역이 프로세서 캐시에 들어갈 수 있으므로 매우 빠르게 액세스 할 수 있습니다.

캐시가 더 많은 프로세서에서이 코드를 시도해 볼 수 있으며 그 차이는 더 작을 수 있습니다.

큰 배열로 시도해보십시오.

1)했다 얼마나 많은 시간을 보는 것도 흥미로울 것이다 array..so 또한 결과의 생성을 타이밍에 바로 전달되는 결과 배열을 생성 대 추가하는 요인

+0

반대로, 하나의 캐시 라인 내부에 들어 맞는 작은 배열로 시도하십시오. –

2

배열이 단어 정렬하지만, 배열의 항목이 단어를 정렬해야 할 이유가 없다.

1

SWAG : UInt16 어레이의 메모리 사용량이 적 으면 메모리 특성 (GC, 캐시, 기타 무엇을 알고 있는지)이 향상되었습니다. 할당이 너무 많지 않으므로 캐시가 주요 요소라고 생각합니다.

또한 벤치마킹은 까다로운 비즈니스 일 수 있습니다. 결과에 왜곡이 될 수있는 JIT 컴파일을 포함하는 것으로 보입니다. UInt16 배열을 사용하여 int 어레이를 테스트하는 순서를 반대로 시도해보고 타이밍이 따르는 지 확인하십시오.

Jon Skeet은 이러한 효과를 고려했을 때 위로 코딩 한 간단한 벤치 마크 프레임 워크를 보유하고 있습니다. 여전히 사용 가능한지 (또는 적용 가능할 지) 나는 모른다. 어쩌면 그는 논평 할 것이다.

1

커플 back

2) IL이 생성되는 것을 보는 것이 흥미로울 것입니다. 귀하의 코드가 매우 간단하기 때문에 (반복 및 추가) 컴파일러가이를 최적화 할 수 있습니다. 아마도 여러 개의 uint16을 더 큰 레지스터에 채우고 명령어 당 여러 번 추가 할 수 있습니다.

+1

리플 렉터에서 확인했는데 그런 일이 아닙니다. 코드는 알고리즘 적으로 사실상 동일합니다. 모든 작업은 동일하지만 적절한 데이터 유형에 맞게 조정됩니다. 유일하게 중요한 차이점은'UInt16'의'add '다음에'conv.u2' 연산을 추가 한 것입니다 ('add'는 int를 반환합니다.) - 그것이 뒷받침 할 문서를 찾을 수는 없습니다. 이것이 C#이 작동하는 방식이기 때문에 추론하기). 만약 차이가 일리노이했다면, 나는 그 여분의 변환 덕분에'UInt16' 버전이 더 느릴 것으로 기대한다. 캐시에 대한 내기가 이론을 놓쳤습니다. – Dathan

1

.NET 전문가는 아니지만 두 개를 확인합니다 두 개 일 :

  1. 큰 어레이 (int 타입의 N 원소)을 전달 ushort는 N 소자 어레이를 더 시간이 걸린다. 이것은 배열의 다양한 크기와 코딩 스타일을 사용하여 테스트 할 수 있습니다 - 내 질문에 대한 질문 참조). 귀하의 테스트에서 숫자 가이 이론에 맞게 :).가 넘쳐 을 확인하지 않고 -ushort 개의 변수가 추가
  2. int 유형의 결과 두 int을 추가로 구현 될 수있다. 그리고 난 처리 어떤 종류의 예외 (오버플로 예외 포함) 시간이 걸리는 작업입니다. 이것은 .NET 문서에서 확인할 수 있습니다.
+1

FYI, VS 2008은 위의 컴파일시 'add'IL 연산을 사용하고 [CIL spec] (http://download.microsoft.com/download/7/3/3/733AD403-90B2-4064-A81E- 01035A7FE13C/MS % 20Partition % 20III.pdf)는'add' 연산이 오버 플로우를 검사하지 않는다고 말합니다. – Dathan

관련 문제