2016-12-05 20 views
0

我的问题是有关了解Linux perf工具度量标准。我做了一个优化与我的代码中的预取/缓存未命中相关,现在速度更快。但是,perf并没有告诉我(或者更确定地说,我不明白perf显示了我)无法理解由“perf”返回的有关缓存未命中的度量标准

回到它开始的地方。我做了一个调查,以便speed up random memory access using prefetch

这里是我的程序做:

  1. 它同样采用了尺寸
  2. 的两个int缓冲区它读取一个接一个的第一缓冲器的所有值
    • 每个值都是第二个缓冲区中的随机索引
  3. 它读取第二个缓冲区中索引处的值
  4. 它总结从第二缓冲器
  5. 它全部为越来越大
  6. 前面的步骤最后,我打印随意和不随意CPU上下文的数量切换
采取的所有值

我最后的调音后,我的代码是以下之一:

#define _GNU_SOURCE 

#include <stdio.h> 
#include <stdlib.h> 
#include <limits.h> 
#include <sys/time.h> 
#include <math.h> 
#include <sched.h> 

#define BUFFER_SIZE ((unsigned long) 4096 * 50000) 
#define PADDING  256 

unsigned int randomUint() 
{ 
    int value = rand() % UINT_MAX; 
    return value; 
} 


unsigned int * createValueBuffer() 
{ 
    unsigned int * valueBuffer = (unsigned int *) malloc(BUFFER_SIZE * sizeof(unsigned int)); 
    for (unsigned long i = 0 ; i < BUFFER_SIZE ; i++) 
    { 
    valueBuffer[i] = randomUint(); 
    } 

    return (valueBuffer); 
} 


unsigned int * createIndexBuffer() 
{ 
    unsigned int * indexBuffer = (unsigned int *) malloc((BUFFER_SIZE + PADDING) * sizeof(unsigned int)); 
    for (unsigned long i = 0 ; i < BUFFER_SIZE ; i++) 
    { 
    indexBuffer[i] = rand() % BUFFER_SIZE; 
    } 

    return (indexBuffer); 
} 


double computeSum(unsigned int * indexBuffer, unsigned int * valueBuffer, unsigned short prefetchStep) 
{ 
    double sum = 0; 

    for (unsigned int i = 0 ; i < BUFFER_SIZE ; i++) 
    { 
    __builtin_prefetch((char *) &valueBuffer[indexBuffer[i + prefetchStep]], 0, 0); 
    unsigned int index = indexBuffer[i]; 
    unsigned int value = valueBuffer[index]; 
    double s = sin(value); 
    sum += s; 
    } 

    return (sum); 
} 


unsigned int computeTimeInMicroSeconds(unsigned short prefetchStep) 
{ 
    unsigned int * valueBuffer = createValueBuffer(); 
    unsigned int * indexBuffer = createIndexBuffer(); 

    struct timeval startTime, endTime; 
    gettimeofday(&startTime, NULL); 

    double sum = computeSum(indexBuffer, valueBuffer, prefetchStep); 

    gettimeofday(&endTime, NULL); 

    printf("prefetchStep = %d, Sum = %f - ", prefetchStep, sum); 
    free(indexBuffer); 
    free(valueBuffer); 

    return ((endTime.tv_sec - startTime.tv_sec) * 1000 * 1000) + (endTime.tv_usec - startTime.tv_usec); 

} 


void testWithPrefetchStep(unsigned short prefetchStep) 
{ 
    unsigned int timeInMicroSeconds = computeTimeInMicroSeconds(prefetchStep); 
    printf("Time: %u micro-seconds = %.3f seconds\n", timeInMicroSeconds, (double) timeInMicroSeconds/(1000 * 1000)); 

} 


int iterateOnPrefetchSteps() 
{ 
    printf("sizeof buffers = %ldMb\n", BUFFER_SIZE * sizeof(unsigned int)/(1024 * 1024)); 
    for (unsigned short prefetchStep = 0 ; prefetchStep < 250 ; prefetchStep++) 
    { 
    testWithPrefetchStep(prefetchStep); 
    } 
} 

void setCpuAffinity(int cpuId) 
{ 
    int pid=0; 
    cpu_set_t mask; 
    unsigned int len = sizeof(mask); 
    CPU_ZERO(&mask); 
    CPU_SET(cpuId,&mask); 
    sched_setaffinity(pid, len, &mask); 
} 

int main(int argc, char ** argv) 
{ 
    setCpuAffinity(7); 

    if (argc == 2) 
    { 
    testWithPrefetchStep(atoi(argv[1])); 
    } 
    else 
    { 
    iterateOnPrefetchSteps(); 
    } 

} 

在我以前的计算器的问题,我求你结束ght我有所有的元素:为了避免缓存未命中我做了我的代码预取数据(使用__builtin_prefetch),我的程序更快。一切看起来尽可能正常

但是,我想使用Linux perf工具研究它。于是,我开始了我的程序的两个执行之间的比较:

  • ./TestPrefetch 0:这样做,因为它是这只是后读取的数据进行预取是无效的(当数据被访问,它不能是加载到CPU缓存中)。运行持续时间:21.346秒
  • ./TestPrefetch 1:这里的预取效率要高得多,因为数据在读取之前被读取一个循环迭代。运行时间:12.624秒

perf输出以下的:

$ gcc -O3 TestPrefetch.c -o TestPrefetch -lm && for cpt in 0 1; do echo ; echo "### Step=$cpt" ; sudo perf stat -e task-clock,cycles,instructions,cache-references,cache-misses ./TestPrefetch $cpt; done 

### Step=0 
prefetchStep = 0, Sum = -1107.523504 - Time: 21346278 micro-seconds = 21.346 seconds 

Performance counter stats for './TestPrefetch 0': 

     24387,010283  task-clock (msec)   # 1,000 CPUs utilized   
    97 274 163 155  cycles     # 3,989 GHz      
    59 183 107 508  instructions    # 0,61 insn per cycle   
     425 300 823  cache-references   # 17,440 M/sec     
     249 261 530  cache-misses    # 58,608 % of all cache refs  

     24,387790203 seconds time elapsed 


### Step=1 
prefetchStep = 1, Sum = -1107.523504 - Time: 12623665 micro-seconds = 12.624 seconds 

Performance counter stats for './TestPrefetch 1': 

     15662,864719  task-clock (msec)   # 1,000 CPUs utilized   
    62 159 134 934  cycles     # 3,969 GHz      
    59 167 595 107  instructions    # 0,95 insn per cycle   
     484 882 084  cache-references   # 30,957 M/sec     
     321 873 952  cache-misses    # 66,382 % of all cache refs  

     15,663437848 seconds time elapsed 

在这里,我有困难明白为什么我更好:

  • cache-misses数几乎是一样的(我甚至还有一点点):我不明白为什么和(总体)如果是这样,我为什么更快?
  • 什么是cache-references
  • 什么是任务时钟和周期?它们是否包含在缓存未命中时等待数据访问的时间?

回答

0

不明白为什么和(总体)如果是这样,为什么我更快?

因为您每次运行更多指令。老一:

0,61 insn per cycle 

和新

0,95 insn per cycle 

哪些缓存引用?

计数缓存被询问的次数,如果它包含您正在加载/存储的数据。

什么是任务时钟和周期?它们是否包含在缓存未命中时等待数据访问的时间?

是的。但请注意,在今天的处理器中,没有任何等待。这些指令是乱序执行的,通常是预取的,如果下一条指令需要某些未准备好的数据,则其他指令将被执行。

+0

'insn per cycle'是所有执行的平均值,不是吗?我的程序现在更快(12.624秒,相对于21.346秒),因此每个循环的逻辑“insn”更高。但我仍然不明白为什么'cache-missses'更高 –

+0

事实上,我仍然不明白为什么'cache-missses'更高 –

+0

_“这些指令是乱序执行的”_你是什么确切的意思_“如果下一条指令需要某些未准备好的数据,则会执行其他指令”_我不明白你的意思?你能给我提供例子还是很好的链接?谢谢 –

1

我不会相信perf的总结,因为它不是很清楚每个名字代表什么,以及他们编程遵循哪个perf计数器。默认设置也被称为计数错误的东西(参见 - https://software.intel.com/en-us/forums/software-tuning-performance-optimization-platform-monitoring/topic/557604

这里可能发生的情况是,您的缓存未命中计数器也可能会计入预取指令(这可能看起来像是对机器的负载,尤其是对于您下降到缓存层次结构中)。在这种情况下,拥有更多的缓存引用(查找)是有意义的,并且您会希望这些请求被错过(预取的全部点将错过...)。

而不是依靠一些模棱两可的计数器,找到我们的计数器ID和掩码为您的特定机器需求读取查找和未命中,看看他们是否改善。

编辑:再看看你的号码,我看到增加了5000万的访问量,但是~70M的错失。可能由于预取缓存抖动造成更多的丢失

0

我最近在我的perf问题上取得了进展。我发现了很多新事件,其中一些真的很有趣。

关于当前的问题,以下事件都被concidered:L1-icache-load-misses

当监控比以前相同的条件下我的测试应用程序与PERF,我得到以下值此事件:

 1 202 210  L1-icache-load-misses 

 530 127  L1-icache-load-misses 

目前,我还不明白为什么cache-misses事件不是IMPAC由prefetches提供,而L1-icache-load-misses是...