cpu缓存访问时间分析

cpu缓存访问时间分析,c,performance,optimization,cpu,cpu-architecture,C,Performance,Optimization,Cpu,Cpu Architecture,我在stackoverflow上的其他人的帮助下编写了以下程序,以了解缓存线和CPU缓存 1 450.0 440.0 2 420.0 230.0 4 400.0 110.0 8 390.0 60.0 16 380.0 30.0 32 320.0 10.0 64 180.0 10.0 128 60.0 0.0 256 40.0 10.

我在stackoverflow上的其他人的帮助下编写了以下程序,以了解缓存线和CPU缓存

    1     450.0  440.0
    2     420.0  230.0
    4     400.0  110.0
    8     390.0   60.0
   16     380.0   30.0
   32     320.0   10.0
   64     180.0   10.0
  128      60.0    0.0
  256      40.0   10.0
  512      10.0    0.0
 1024      10.0    0.0
我已经用下面发布的gnuplot绘制了一个图

    1     450.0  440.0
    2     420.0  230.0
    4     400.0  110.0
    8     390.0   60.0
   16     380.0   30.0
   32     320.0   10.0
   64     180.0   10.0
  128      60.0    0.0
  256      40.0   10.0
  512      10.0    0.0
 1024      10.0    0.0

我有以下问题

  • 我的计时计算(毫秒)正确吗?440ms似乎 有很多时间吗

  • 从图缓存访问1(红线)可以得出以下结论: 缓存线的大小是32位(而不是64位?)

  • 在代码中的for循环之间,清除 隐藏物如果是,我如何通过编程实现

  • 如您所见,我在上面的结果中有一些
    0.0
    值。? 这意味着什么?测量的粒度是否也一样 粗糙

  • 请回复

    #include <stdio.h>
    #include <sys/time.h>
    #include <time.h>
    #include <unistd.h>
    #include <stdlib.h>
    
    #define MAX_SIZE (512*1024*1024)
    
    int main()
    {
        clock_t start, end;
        double cpu_time;
        int i = 0;
        int k = 0;
        int count = 0;
    
        /*
         * MAX_SIZE array is too big for stack.This is an unfortunate rough edge of the way the stack works. 
         * It lives in a fixed-size buffer, set by the program executable's configuration according to the 
         * operating system, but its actual size is seldom checked against the available space.
         */
    
        /*int arr[MAX_SIZE];*/
    
        int *arr = (int*)malloc(MAX_SIZE * sizeof(int));
    
        /*cpu clock ticks count start*/
    
        for(k = 0; k < 3; k++)
        {
            start = clock();
            count = 0;
    
            for (i = 0; i < MAX_SIZE; i++)
            { 
                arr[i] += 3;
                /*count++;*/
            }
    
            /*cpu clock ticks count stop*/
            end = clock();
    
            cpu_time = ((double) (end - start)) / CLOCKS_PER_SEC;
    
            printf("cpu time for loop 1 (k : %4d) %.1f ms.\n",k,(cpu_time*1000));
        }
    
        printf("\n");
        for (k = 1 ; k <= 1024 ; k <<= 1)
        {
            /*cpu clock ticks count start*/
            start = clock();
            count = 0;
    
            for (i = 0; i < MAX_SIZE; i += k) 
            {
                /*count++;*/
                arr[i] += 3;
            }
    
            /*cpu clock ticks count stop*/
            end = clock();
    
            cpu_time = ((double) (end - start)) / CLOCKS_PER_SEC;
    
            printf("cpu time for loop 2 (k : %4d) %.1f ms.\n",k,(cpu_time*1000));
        }
        printf("\n");
    
        /* Third loop, performing the same operations as loop 2, 
           but only touching 16KB of memory
         */
        for (k = 1 ; k <= 1024 ; k <<= 1)
        {
            /*cpu clock ticks count start*/
            start = clock();
            count = 0;
    
            for (i = 0; i < MAX_SIZE; i += k) 
            {
                count++;
                arr[i & 0xfff] += 3;
            }
    
            /*cpu clock ticks count stop*/
            end = clock();
    
            cpu_time = ((double) (end - start)) / CLOCKS_PER_SEC;
    
            printf("cpu time for loop 3 (k : %4d) %.1f ms.\n",k,(cpu_time*1000));
        }
    
        return 0;
    }
    
    #包括
    #包括
    #包括
    #包括
    #包括
    #定义最大大小(512*1024*1024)
    int main()
    {
    时钟开始、结束;
    双cpu时间;
    int i=0;
    int k=0;
    整数计数=0;
    /*
    *MAX_SIZE数组对于堆栈来说太大。这是堆栈工作方式的一个不幸的粗糙边缘。
    *它位于一个固定大小的缓冲区中,由程序可执行文件的配置根据
    *操作系统,但很少对照可用空间检查其实际大小。
    */
    /*int arr[最大尺寸]*/
    int*arr=(int*)malloc(MAX_SIZE*sizeof(int));
    /*cpu时钟滴答开始计数*/
    对于(k=0;k<3;k++)
    {
    开始=时钟();
    计数=0;
    对于(i=0;i对于(k=1;k,由于您使用的是Linux,我将从这个角度进行回答。我还将在编写时考虑Intel(即x86-64)体系结构

  • 440毫秒可能是准确的。查看结果的更好方法是每个元素或访问的时间。请注意,增加k会减少访问的元素数。现在,缓存访问2显示了相当稳定的结果,即0.9ns/访问。此时间大致相当于每个访问的1-3个周期(取决于CPU的时钟频率).所以1-16号(可能是32号)是准确的
  • 不(虽然我首先假定您的意思是32字节对64字节)。您应该问问自己,“缓存线大小”是多少看起来像?如果您访问的缓存线小于缓存线,则您将错过并随后命中一次或多次。如果您大于或等于缓存线大小,则每次访问都将错过。在k=32及以上时,访问1的访问时间相对恒定,每次访问为20ns。在k=1-16时,总体访问时间恒定,这表明缓存未命中的数量大致相同,因此我认为缓存线的大小是64字节
  • 是的,至少对于仅存储~16KB的最后一个循环来说是这样。如何操作?要么触摸大量其他数据,如另一个GB数组。要么调用一条指令,如x86的WBINVD,该指令写入内存,然后使所有缓存内容无效;然而,它要求您处于内核模式
  • 正如您所指出的,超过大小32,时间徘徊在10ms左右,这显示了您的计时粒度。您需要增加所需的时间(以便10ms的粒度足够),或者切换到不同的计时机制,这正是评论所争论的。我非常喜欢使用指令rdtsc(读取时间戳计数器)(即循环计数),但这可能比上面的建议问题更大。将代码切换到rdtsc基本上需要每秒切换时钟、时钟和时钟。但是,如果线程迁移,您仍然可能面临时钟漂移,但这是一个有趣的测试,所以我不关心这个问题
  • 更多注意事项:一致的步幅(如2的幂)的问题在于处理器喜欢通过预取来隐藏缓存未命中惩罚。您可以在BIOS中的许多机器上禁用预取器(请参阅)

    页面错误也可能会影响您的结果。您正在分配5亿整数或大约2GB的存储。循环1尝试触摸内存,以便操作系统分配页面,但如果您没有这么多可用内存(不仅仅是总内存,因为操作系统等会占用一些空间)此外,操作系统可能会开始回收一些空间,因此在某些访问中,您总是会出现页面错误

    与前一个相关,TLB也会对结果产生一些影响。硬件在转换查找缓冲区(TLB)中保留从虚拟地址到物理地址的映射的小缓存。每一页内存(Intel上为4KB)需要一个TLB条目。因此,您的实验将需要2GB/4KB=>~500000个条目。大多数TLB包含的条目不到1000个,因此测量值也会因未命中而发生偏差。幸运的是,每4KB或1024 int只会发生一次。malloc可能会为您分配“大”或“大”页面,详细信息-


    另一个实验是重复第三个循环,但更改正在使用的掩码,以便可以观察每个缓存级别的大小(L1、L2,可能是L3,很少是L4)。您还可能会发现不同的缓存级别使用不同的缓存线大小。

    既然您在Linux上,我将从这个角度回答。我还将考虑Intel(即x86-64)体系结构

  • 440毫秒可能是准确的。查看结果的更好方法是每个元素或访问的时间。请注意,增加k会减少访问的元素数。现在,缓存访问2显示了相当稳定的结果,即0.9ns/访问。此时间大致相当于每个访问的1-3个周期(取决于CPU的时钟频率).So尺寸1-16(5月)