Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/150.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ “a”是什么;“缓存友好”;密码?_C++_Performance_Caching_Memory_Cpu Cache_Fortran_Matlab_C - Fatal编程技术网

C++ “a”是什么;“缓存友好”;密码?

C++ “a”是什么;“缓存友好”;密码?,c++,performance,caching,memory,cpu-cache,fortran,matlab,c,C++,Performance,Caching,Memory,Cpu Cache,Fortran,Matlab,C,“缓存不友好代码”和“缓存友好代码”之间有什么区别 如何确保编写缓存效率高的代码 预备赛 在现代计算机上,只有最低级别的内存结构(寄存器)才能在单个时钟周期内移动数据。然而,寄存器非常昂贵,大多数计算机内核只有不到几十个寄存器。在内存频谱的另一端(DRAM),内存非常便宜(即实际上便宜数百万倍),但在请求接收数据后需要数百个周期。为了弥合超快与昂贵、超慢与廉价之间的差距,我们推出了高速缓存,它以降低速度和成本的方式命名为L1、L2、L3。其思想是,大多数执行代码将经常命中一小部分变量,而其余部分

缓存不友好代码”和“缓存友好代码”之间有什么区别

如何确保编写缓存效率高的代码

预备赛 在现代计算机上,只有最低级别的内存结构(寄存器)才能在单个时钟周期内移动数据。然而,寄存器非常昂贵,大多数计算机内核只有不到几十个寄存器。在内存频谱的另一端(DRAM),内存非常便宜(即实际上便宜数百万倍),但在请求接收数据后需要数百个周期。为了弥合超快与昂贵、超慢与廉价之间的差距,我们推出了高速缓存,它以降低速度和成本的方式命名为L1、L2、L3。其思想是,大多数执行代码将经常命中一小部分变量,而其余部分(一个更大的变量集)则很少命中。如果处理器在一级缓存中找不到数据,则在二级缓存中查找。如果不存在,则为三级缓存,如果不存在,则为主内存。每一个“失误”在时间上都是昂贵的

(类似地,缓存就是系统内存,因为系统内存太过硬盘存储。硬盘存储非常便宜,但速度非常慢)

缓存是减少延迟影响的主要方法之一。套用Herb Sutter(cfr.链接如下):增加带宽很容易,但我们无法花钱摆脱延迟

数据总是通过内存层次结构进行检索(最小==从最快到最慢)。缓存命中/未命中通常是指CPU中最高级别的缓存中的命中/未命中——我所说的最高级别是指最大的==最慢的。缓存命中率对性能至关重要,因为每次缓存未命中都会导致从RAM(或更糟的…)获取数据,这需要大量的时间(RAM需要数百个周期,HDD需要数千万个周期)。相比之下,从(最高级别)缓存读取数据通常只需要几个周期

在现代计算机体系结构中,性能瓶颈是离开CPU芯片(例如访问RAM或更高)。随着时间的推移,情况只会变得更糟。处理器频率的增加目前与性能的提高不再相关问题在于内存访问。因此,CPU中的硬件设计工作目前主要集中在优化缓存、预取、管道和并发性上。例如,现代CPU大约85%的内存用于缓存,高达99%的内存用于存储/移动数据

关于这个问题有很多话要说。以下是一些关于缓存、内存层次结构和正确编程的重要参考资料:

  • 。在他的优秀文档中,您可以找到涵盖汇编语言到C++语言的详细示例。
  • 如果你喜欢视频,我强烈建议你看一看(特别是12:00以后!)
  • (索尼科技总监)
  • LWN.net的文章
缓存友好代码的主要概念 缓存友好代码的一个非常重要的方面是关于,其目标是将相关数据放在内存中,以实现高效缓存。就CPU缓存而言,了解缓存线以了解其工作原理非常重要:

以下特定方面对于优化缓存非常重要:

  • 时间位置:当访问给定内存位置时,很可能在不久的将来再次访问同一位置。理想情况下,此信息仍将在该点缓存
  • 空间位置:这是指将相关数据彼此靠近放置。缓存发生在许多级别上,而不仅仅是在CPU中。例如,当您从RAM中读取数据时,通常会提取比特定要求更大的内存块,因为程序通常很快就会需要这些数据。硬盘缓存遵循同样的思路。特别是对于CPU缓存,缓存线的概念很重要
  • 使用合适的容器

    缓存友好与缓存不友好的一个简单示例是's
    std::vector
    std::list
    std::vector
    的元素存储在连续内存中,因此访问它们比访问
    std::list
    中的元素更容易缓存,后者将其内容存储在所有位置。这是由于空间位置

    Bjarne Stroustrup在中给出了一个很好的例子(感谢@Mohammad Ali Baydoun的链接!)

    在数据结构和算法设计中不要忽视缓存

    尽可能地调整数据结构和计算顺序,以最大限度地利用缓存。这方面的一种常见技术是,这在高性能计算(例如cfr)中非常重要

    了解并利用数据的隐含结构

    另一个简单的例子是存储二维数组的列主顺序(例如,)与行主顺序(例如,)的比较,这一点很多业内人士有时会忘记。例如,考虑下面的矩阵:

    1 2
    3 4
    
    在行主顺序中,它作为
    1234
    存储在内存中;在列主顺序中,这将存储为
    1 3 2 4
    。很容易看出,不利用这种顺序的实现将很快遇到(很容易避免!)缓存问题。不幸的是,我经常在我的领域(机器学习)看到这样的东西@MatteoItalia在他的回答中更详细地展示了这个例子

    当从内存中提取矩阵的某个元素时,它附近的元素也将被提取并存储在缓存线中。如果利用排序,这将导致更少的内存访问(因为接下来的几个值
    M[0][0] (memory) + M[0][1] (cached) + M[1][0] (memory) + M[1][1] (cached)
    = 1 + 2 + 3 + 4
    --> 2 cache hits, 2 memory accesses
    
    M[0][0] (memory) + M[1][0] (memory) + M[0][1] (memory) + M[1][1] (memory)
    = 1 + 3 + 2 + 4
    --> 0 cache hits, 4 memory accesses
    
    // Cache-friendly version - processes pixels which are adjacent in memory
    for(unsigned int y=0; y<height; ++y)
    {
        for(unsigned int x=0; x<width; ++x)
        {
            ... image[y][x] ...
        }
    }
    
    // Cache-unfriendly version - jumps around in memory for no good reason
    for(unsigned int x=0; x<width; ++x)
    {
        for(unsigned int y=0; y<height; ++y)
        {
            ... image[y][x] ...
        }
    }
    
    for(i=0;i<N;i++) {
       for(j=0;j<N;j++) {
          dest[i][j] = 0;
          for( k=0;k<N;k++) {
             dest[i][j] += src1[i][k] * src2[k][j];
          }
       }
    }
    
    int itemsPerCacheLine = CacheLineSize / sizeof(elemType);
    
    for(i=0;i<N;i++) {
       for(j=0;j<N;j += itemsPerCacheLine ) {
          for(jj=0;jj<itemsPerCacheLine; jj+) {
             dest[i][j+jj] = 0;
          }
          for( k=0;k<N;k++) {
             for(jj=0;jj<itemsPerCacheLine; jj+) {
                dest[i][j+jj] += src1[i][k] * src2[k][j+jj];
             }
          }
       }
    }
    
    struct Product
    {
       int32_t key;
       char name[56];
       int32_t price'
    }
    
    /* create an array of structs */
    Product* table = new Product[N];
    /* now load this array of structs, from a file etc. */
    
    /* create separate arrays for each attribute */
    int32_t* key = new int32_t[N];
    char* name = new char[56*N];
    int32_t* price = new int32_t[N];
    /* now load these arrays, from a file etc. */
    
    SELECT SUM(price)
    FROM PRODUCT
    
    int sum = 0;
    for (int i=0; i<N; i++)
       sum = sum + table[i].price;
    
    int sum = 0;
    for (int i=0; i<N; i++)
       sum = sum + price[i];