2016-09-12 1 views
0

clflush 후 테이블 항목에 액세스하고 다른 항목에 액세스하는 시간차를 측정하고 싶습니다. 아래에서 두 가지 작업에 대한 벌칙은 거의 없습니다. 테이블의 길이는 256이고 각 항목에는 8 비트가 있습니다. 내 clflush가 제대로 작동하지 않는 것 같습니다. gcc에서 -O3 플래그로 컴파일 중입니다.clflush를 사용하는 방법?

  #include <stdio.h> 
      #include <stdlib.h> 
      #include <stdint.h> 
      #define ARRAYSIZE(arr) (sizeof(arr)/sizeof(arr[0])) 

      #define REPEAT 10000 

      unsigned char table[256]={103,198,105,115,81,255,74,236,41,205,186,171,242,251,227,70,124,194,84,248,27,232,231,141,118,90,46,99,51,159,201,154,102,50,13,183,49,88,163,90,37,93,5,23,88,233,94,212,171,178,205,198,155,180,84,17,14,130,116,65,33,61,220,135,112,233,62,161,65,225,252,103,62,1,126,151,234,220,107,150,143,56,92,42,236,176,59,251,50,175,60,84,236,24,219,92,2,26,254,67,251,250,170,58,251,41,209,230,5,60,124,148,117,216,190,97,137,249,92,187,168,153,15,149,177,235,241,179,5,239,247,0,233,161,58,229,202,11,203,208,72,71,100,189,31,35,30,168,28,123,100,197,20,115,90,197,94,75,121,99,59,112,100,36,17,158,9,220,170,212,172,242,27,16,175,59,51,205,227,80,72,71,21,92,187,111,34,25,186,155,125,245,11,225,26,28,127,35,248,41,248,164,27,19,181,202,78,232,152,50,56,224,121,77,61,52,188,95,78,119,250,203,108,5,172,134,33,43,170,26,85,162,190,112,181,115,59,4,92,211,54,148,179,175,226,240,228,158,79,50,21,73,253,130,78,169}; 



      inline void clflush(volatile void *p) 
      { 
       asm volatile ("clflush (%0)" :: "r"(p)); 
      } 

      inline uint64_t rdtsc() 
      { 
       unsigned long a, d; 
       asm volatile ("cpuid; rdtsc" : "=a" (a), "=d" (d) : : "ebx", "ecx"); 
       return a | ((uint64_t)d << 32); 
      } 

      inline int func(int *a) { 
       int i; 
       for(i=0;i<REPEAT;i++){ 
        a[i]=(int)table[rand()%256]; 
       } 

      } 
      void flushCache(unsigned char *start) 
      { 
       // flush table 
       unsigned char* fPtr = (unsigned char*)start; 
       clflush(fPtr); 
       clflush(fPtr+64); 
       clflush(fPtr+128); 
       clflush(fPtr+192); 
       clflush(fPtr+256); 
      } 


      inline void test() 
      { 
       int i=0; 
       uint64_t start, end; 
       char c; 
       int temp[REPEAT]; 

       start = rdtsc(); 

       func(temp); 

       end = rdtsc(); 

       //following line of code to prevent compiler from optimizing. do something with the return value 
       for(i-0;i<REPEAT;i++){ 
       temp[i]=temp[i]+temp[i/2]; 
       } 

       printf("%ld ticks\n", end - start); 
      } 

      inline void testflush() 
      { 
       int i=0; 
       uint64_t start, end; 
       char c; 
       int temp[REPEAT]; 

       start = rdtsc(); 

       func(temp); 
       flushCache(table); //flush afer every read 

       end = rdtsc(); 

       //following line of code to prevent compiler from optimizing. do something with the return value 
       for(i-0;i<REPEAT;i++){ 
       temp[i]=temp[i]+temp[i/2]; 
       } 

       printf("%ld ticks\n", end - start); 
      } 



      int main(int ac, char **av) 
      { 
       test(); 
       printf("Tables in cache!\n"); 
       testflush(); 
       printf("Tables evicted from cache.\n"); 
       test(); 

       return 0; 
      } 

업데이트 : 테이블 액세스로 인해 문제가있을 수 있습니다. 다음은 전체 테이블 대신 단일 변수를 제거하는 또 다른 코드입니다. 이것은 clflush()를 사용할 때 클럭 사이클에 중요한 포함을 보여줍니다. clflush()가 제대로 작동하고 있으며 메모리에서 변수에 액세스 할 예정인 시간을 의미합니까?

  #include <stdint.h> 
      #include <stdio.h> 
      #define REPEAT 100000 
      inline void clflush(volatile void *p) 
      { 
       asm volatile ("clflush (%0)" :: "r"(p)); 
      } 

      inline uint64_t rdtsc() 
      { 
       unsigned long a, d; 
       asm volatile ("rdtsc" : "=a" (a), "=d" (d)); 
       return a | ((uint64_t)d << 32); 
      } 

      volatile int i; 

      inline void test() 
      { 
       uint64_t start, end,clock; 
       volatile int j; 
       long int rep; 
       int k; 

       clock=0; 
       for(rep=0;rep<REPEAT;rep++){ 
        start = rdtsc(); 
        j = i+1; 
        end = rdtsc(); 
        clock=clock+(end-start); 
        k=j; 
       } 
       printf("took %lu ticks\n", clock); 
      } 

      inline void testflush() 
      { 
       uint64_t start, end,clock; 
       volatile int j; 
       int k; 
       long int rep; 

       clock=0; 
       for(rep=0;rep<REPEAT;rep++){ 
        start = rdtsc(); 
        j = i+1; 
        end = rdtsc(); 
        clflush(&i); 
        clock=clock+(end-start); 
        k=j; 
       } 
       printf("took %lu ticks\n", clock); 
      } 


      int main(int ac, char **av) 
      { 
       i=5; 
       printf("------------------------------------------\n"); 
       test(); 
       printf("------------------------------------------\n"); 
       testflush(); 
       printf("------------------------------------------\n"); 
       test(); 
       return 0; 
      } 
+0

clflush (fPtr + 256); 주 :'fPtr'의 이유는 없습니다. – Olaf

+0

확인. 그럼 어떻게해야합니까? – Rick

+0

@ user2764478 : 그렇습니다. (두 번째 버전의 경우)주기가 상당히 길어지면 clflush가 작동 함을 의미합니다. – Brendan

답변

0

코드와 관련된 몇 가지 문제점이 있습니다.

clflush을 호출 한 후 testflush의 타이머를 종료합니다. 따라서 이러한 지침을 처리하는 데 필요한주기를 타이밍해야합니다. 나는 그것이 의도 된 것이라고 생각하지 않는다.

테스트 함수에는 반복 횟수가 10000 인 루프가 있습니다. 각 반복마다 하나의 새로운 캐시 라인에 대한 참조를 호출 할 수 있지만 table에는 4 개의 캐시 라인 만 있습니다. 따라서 적어도 9996 번 반복으로 어쨌든 캐시 누락이 발생하지 않습니다.

따라서에 4 회의 캐시로드를 더한 시간이 10000 회 있습니다. 캐시로드가 수백 사이클 걸리더라도 rand()%256의 10000 반복은 여전히 ​​그 그림자를가립니다.

생성 된 10000 개의 정수도 다시 써야합니다. L1-> L2 캐시 대역폭이 제한 요소가 될지는 확실치 않지만 그렇다고 할 수 있습니다.

또한 수천 번 정도의 평균 테스트를 실행해야하며 그렇지 않은 경우 샘플 분산이 너무 높습니다.

그런 다음 요청하기 전에 CPU가 추측을 통해 캐시 라인을 다시 프리 페치 할 수도 있습니다. 그렇게 할 수는 있지만, 현재의 CPU가 얼마나 영리한 지 모르겠습니다.

+0

도움 주셔서 감사합니다. clflush()에 대한 시간을 측정하지 않는 또 다른 코드를 추가했습니다. 이 코드는 clflush()를 사용할 때 효율성이 크게 감소 함을 보여줍니다 (거의 4 배). 플러싱 (flushing)으로 인한 효율 저하가 있는지 확인할 수 있습니까? – Rick

+0

@ user2764478 업데이트 된 코드는 내 시스템에서 clflush로 약 50 % 더 오래 걸립니다. rdtsc 명령어는 캐시 미스만큼 많은 시간이 걸리는 것으로 보입니다. 그러나 cpuid 명령을 제거 했으므로 틱 수가 덜 안정적이라고 생각합니다. 캐시 미스를 추가하면 완전히 다시 그림자가 생깁니다. – user4407569

+0

인라인 uint64_t의 RDTSC() \t \t \t \t \t { \t \t \t 긴 부호 A, D; \t \t \t \t asm volatile ("cpuid; rdtsc": "a", "= d"(d) : "ebx", "ecx"); \t \t \t \t a | ((uint64_t) d << 32); \t \t \t \t – Rick

관련 문제