2013-10-02 3 views
9

캐시 크기 (L1, L2, L3)를 얻기위한 프로그램을 작성하고 싶습니다. 나는 그것의 일반적인 생각을 안다. CPU 캐시 크기 및 레벨을 얻기위한 프로그램 작성

  1. 큰 배열을 다른 크기의 그것의
  2. 액세스 부분마다 할당합니다.

그래서 나는 작은 프로그램을 작성했습니다.

#include <cstdio> 
#include <time.h> 
#include <sys/mman.h> 

const int KB = 1024; 
const int MB = 1024 * KB; 
const int data_size = 32 * MB; 
const int repeats = 64 * MB; 
const int steps = 8 * MB; 
const int times = 8; 

long long clock_time() { 
    struct timespec tp; 
    clock_gettime(CLOCK_REALTIME, &tp); 
    return (long long)(tp.tv_nsec + (long long)tp.tv_sec * 1000000000ll); 
} 

int main() { 
    // allocate memory and lock 
    void* map = mmap(NULL, (size_t)data_size, PROT_READ | PROT_WRITE, 
        MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); 
    if (map == MAP_FAILED) { 
     return 0; 
    } 
    int* data = (int*)map; 

    // write all to avoid paging on demand 
    for (int i = 0;i< data_size/sizeof(int);i++) { 
     data[i]++; 
    } 

    int steps[] = { 1*KB, 4*KB, 8*KB, 16*KB, 24 * KB, 32*KB, 64*KB, 128*KB, 
        128*KB*2, 128*KB*3, 512*KB, 1 * MB, 2 * MB, 3 * MB, 4 * MB, 
        5 * MB, 6 * MB, 7 * MB, 8 * MB, 9 * MB}; 
    for (int i = 0; i <= sizeof(steps)/sizeof(int) - 1; i++) { 
     double totalTime = 0;  
     for (int k = 0; k < times; k++) { 
      int size_mask = steps[i]/sizeof(int) - 1; 
      long long start = clock_time(); 
      for (int j = 0; j < repeats; j++) { 
       ++data[ (j * 16) & size_mask ]; 
      } 
      long long end = clock_time(); 
      totalTime += (end - start)/1000000000.0; 
     } 
     printf("%d time: %lf\n", steps[i]/KB, totalTime); 
    } 
    munmap(map, (size_t)data_size); 
    return 0; 
} 

그러나, 결과가 너무 이상해 : 여기 내 코드의

1 time: 1.989998 
4 time: 1.992945 
8 time: 1.997071 
16 time: 1.993442 
24 time: 1.994212 
32 time: 2.002103 
64 time: 1.959601 
128 time: 1.957994 
256 time: 1.975517 
384 time: 1.975143 
512 time: 2.209696 
1024 time: 2.437783 
2048 time: 7.006168 
3072 time: 5.306975 
4096 time: 5.943510 
5120 time: 2.396078 
6144 time: 4.404022 
7168 time: 4.900366 
8192 time: 8.998624 
9216 time: 6.574195 

내 CPU는 인텔 (R) 코어 (TM) i3-2350M입니다. L1 캐시 : 32K (데이터 용), L2 캐시 256K, L3 캐시 3072K. 규칙을 따르지 않는 것 같습니다. 캐시 크기 나 캐시 수준에 대한 정보를 얻을 수 없습니다. 아무도 도움을 줄 수 있습니까? 미리 감사드립니다.

업데이트 : 따르 @Leeor 조언, 나는 j*64 대신 j*16 사용합니다. 새로운 결과 :

1 time: 1.996282 
4 time: 2.002579 
8 time: 2.002240 
16 time: 1.993198 
24 time: 1.995733 
32 time: 2.000463 
64 time: 1.968637 
128 time: 1.956138 
256 time: 1.978266 
384 time: 1.991912 
512 time: 2.192371 
1024 time: 2.262387 
2048 time: 3.019435 
3072 time: 2.359423 
4096 time: 5.874426 
5120 time: 2.324901 
6144 time: 4.135550 
7168 time: 3.851972 
8192 time: 7.417762 
9216 time: 2.272929 
10240 time: 3.441985 
11264 time: 3.094753 

두 개의 피크, 4096K 및 8192K. 아직도 이상해.

답변

5

이것이 유일한 문제인지는 확실치 않지만, 가장 큰 문제입니다. 코드가 HW 스트림 프리 페처를 빠르게 트리거하여 거의 항상 L1 또는 L2 대기 시간에 도달합니다.

자세한 내용은 여기에서 찾을 수 있습니다 - 벤치 마크 당신은 (BIOS 또는 기타 방법을 통해)을 비활성화하거나 적어도 j*16을 (교체하여 단계를 더해야 하나 들어 http://software.intel.com/en-us/articles/optimizing-application-performance-on-intel-coret-microarchitecture-using-hardware-implemented-prefetchers

* 4 바이트 INT = 당 64B, 하나의 캐시 라인 - 스트림 검출기의 전형적인 유닛 스트라이드), j*64 (4 캐시 라인). 그 이유는 프리 페처가 스트림 요청 당 2 개의 프리 페치를 발행 할 수 있기 때문에 단위 진행을 할 때 코드보다 먼저 실행되기 때문에 코드가 2 행 이상으로 점프 할 때 조금 앞서 갈 수 있지만 길어질수록 쓸모가 없어집니다 점프 (귀하의 모듈러 때문에 3이 좋지 않아서 step_size의 제수가 필요합니다)

새로운 결과로 질문을 업데이트하면 여기에 다른 것이 있는지 알아낼 수 있습니다.

EDIT1


: 좋아, 난 고정 된 코드를 실행하고있어 - 당신은 몇 열을 무시하면

1 time: 1.321001 
4 time: 1.321998 
8 time: 1.336288 
16 time: 1.324994 
24 time: 1.319742 
32 time: 1.330685 
64 time: 1.536644 
128 time: 1.536933 
256 time: 1.669329 
384 time: 1.592145 
512 time: 2.036315 
1024 time: 2.214269 
2048 time: 2.407584 
3072 time: 2.259108 
4096 time: 2.584872 
5120 time: 2.203696 
6144 time: 2.335194 
7168 time: 2.322517 
8192 time: 5.554941 
9216 time: 2.230817 

그것은 훨씬 더 의미가 있습니다 - 당신은 32K (L1 크기) 후 점프 그러나 256k (L2 크기) 이후에 점프하는 대신 384에 대해서는 결과가 너무 좋고 512k에서만 점프합니다.마지막 점프가 8M (내 LLC 크기)이지만 9k가 다시 부러졌습니다.

이렇게하면 다음 오류를 발견 할 수 있습니다. 크기 마스크로 ANDing은 2의 거듭 제곱일 때만 의미가 있습니다. 그렇지 않으면 랩하지 않고 마지막 주소 중 일부를 다시 반복합니다 (낙관적으로 끝남). 결과는 캐시에서 새 것임). ... & size_mask% steps[i]/sizeof(int)로 교체

시도는 modulu 더 비싸지 만 당신이이 크기를 갖고 싶어 당신은 (대안은 현재 크기를 초과 할 때마다 제로 도착 실행중인 인덱스 또는)

+0

고맙습니다!. 대신 j * 64를 사용하지만 결과는 혼란 스럽습니다. 내 업데이트를 참조하십시오. –

+0

@ KanLiu - 내 대답 – Leeor

+1

업데이트, 잘 작동합니다! 고마워. –

4

CPUID 지침을 살펴 보는 것이 더 나을 것이라고 생각합니다. 사소하지는 않지만 웹에 대한 정보가 있어야합니다.

또한 Windows를 사용하는 경우 GetLogicalProcessorInformation 기능을 사용할 수 있습니다. Windows XP SP3 이상에서만 제공됩니다. 나는 리눅스/유닉스에 대해 아무것도 모른다.

+0

좋은 방법입니다. 하지만 캐시가 실제로 어떻게 작동하는지 이해하고 왜 코드가 이와 같이 작동하는지 이해하고 싶습니다. 어쨌든 고마워. –

+1

@ KanLiu - [이 기사] (http://lwn.net/Articles/250967/)를 보았습니까? 그것은 캐시의 내용을 포함하여 모든 세부 사항을 가지고 있습니다. –

+0

감사합니다. 정말 좋은 물건. –

2

당신이 만약 그것을 필요 GNU/Linux를 사용하고 있다면 파일 /proc/cpuinfo의 내용을 읽고 자세한 내용은 /sys/devices/system/cpu/*입니다. UNIX에서는 일반 파일이 어쨌든 그 작업을 수행 할 수있는 API를 정의하지 않는 것이 일반적입니다. util을-리눅스

나는 또한 의 소스를 살펴 것, 그것은 lscpu라는 프로그램이 포함되어 있습니다. 이것은 필요한 정보를 검색하는 방법에 대한 예제를 제공해야합니다.

// 그들의 소스를 살펴 촬영 한 경우
http://git.kernel.org/cgit/utils/util-linux/util-linux.git/tree/sys-utils/lscpu.c

를 업데이트합니다. 기본적으로 위에서 언급 한 파일에서 읽습니다. 그러므로 그 파일들로부터 읽는 것이 절대적으로 유효합니다. 그것들은 커널에 의해 제공됩니다.

+0

감사합니다. 캐시의 크기와 레벨이 내 코드의 동작과 일치하지 않는다고 말했고 그 이유를 알고 싶습니다. –