Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/149.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++ 如何计算内存访问时间?_C++ - Fatal编程技术网

C++ 如何计算内存访问时间?

C++ 如何计算内存访问时间?,c++,C++,我创建了一个大型的布尔2d数组(5000X5000,23MB时总共有250亿个元素)。然后我循环并用随机的true或false实例化每个元素。然后我循环阅读每一个元素。所有2500万个元素的读取时间约为100ms 23MB太大,无法放入CPU的缓存中,我认为我的程序太简单,无法从任何类型的编译器优化中获益,那么我得出的结论是,该程序在~100ms的时间内从RAM中读取2500万个元素,对吗 #include "stdafx.h" #include <iostream>

我创建了一个大型的布尔2d数组(5000X5000,23MB时总共有250亿个元素)。然后我循环并用随机的true或false实例化每个元素。然后我循环阅读每一个元素。所有2500万个元素的读取时间约为100ms

23MB太大,无法放入CPU的缓存中,我认为我的程序太简单,无法从任何类型的编译器优化中获益,那么我得出的结论是,该程序在~100ms的时间内从RAM中读取2500万个元素,对吗

    #include "stdafx.h"
    #include <iostream>
    #include <chrono>
    using namespace std;

    int _tmain(int argc, _TCHAR* argv[])
    {
        bool **locs;
        locs = new bool*[5000];
        for(int i = 0; i < 5000; i++)
            locs[i] = new bool[5000];
        for(int i = 0; i < 5000; i++)
            for(int i2 = 0; i2 < 5000; i2++)
                locs[i][i2] = rand() % 2 == 0 ? true : false;
        int *idx = new int [5000*5000];
        for(int i = 0; i < 5000*5000; i++)
            *(idx + i) = rand() % 4999;

        bool val;
        int memAccesses = 0;
        auto start = std::chrono::high_resolution_clock::now();
        for(int i = 0; i < 5000*5000; i++) {
            val = locs[*(idx + i)][*(idx + ++i)];
            memAccesses += 2;
        }
        auto finish = std::chrono::high_resolution_clock::now();

        std::cout << std::chrono::duration_cast<std::chrono::nanoseconds>(finish-start).count() << " ns\n";
        std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(finish-start).count() << " ms\n";
        cout << "TOTAL MEMORY ACCESSES: " << memAccesses << endl;
        cout << "The size of the array in memory is " << ((sizeof(bool)*5000*5000)/1048576) << "MB";

        int exit; cin >> exit;
        return 0;
    }

    /*
    OUTPUT IS:

        137013700 ns
        137 ms
        TOTAL MEMORY ACCESSES: 25000000
        The size of the array in memory is 23MB
    */
#包括“stdafx.h”
#包括
#包括
使用名称空间std;
int _tmain(int argc,_TCHAR*argv[]
{
bool**locs;
locs=新bool*[5000];
对于(int i=0;i<5000;i++)
locs[i]=新bool[5000];
对于(int i=0;i<5000;i++)
对于(int i2=0;i2<5000;i2++)
locs[i][i2]=rand()%2==0?真:假;
int*idx=新int[5000*5000];
对于(int i=0;i<5000*5000;i++)
*(idx+i)=兰德()%4999;
布尔瓦尔;
int=0;
自动启动=标准::时钟::高分辨率时钟::现在();
对于(int i=0;i<5000*5000;i++){
val=locs[*(idx+i)][*(idx+++i)];
内存访问数+=2;
}
自动完成=标准::时钟::高分辨率时钟::现在();

std::cout否。读取不会一直向下进入RAM。当读取(或写入)时,内存块会被拉入缓存执行。只要从中读取的块已经在缓存中,就会使用缓存。如果您从不在缓存中的块请求数据,则访问RAM以获取内存块并将其放入缓存。从缓存中读取要比从RAM中读取便宜得多

编辑

同样,写操作会导致内存中的块被拉入缓存。因为在读取值之前,您正在将这些值存储在程序中,所以您正在读取的数据很可能从存储时就已经在缓存中。因此,读取这些值的循环很可能永远不需要访问RAM。

缓存的使用率是有限的独立于程序的复杂性。每当从RAM读取数据时,数据都会进入缓存。因为缓存有一定的大小,所以总是有那么多的数据可用。如果您访问前一个内存位置旁边的内存位置,很有可能它已经被缓存了。在这种情况下,不会访问RAM

我建议通过阅读来扩大你的知识面

顺便说一句:
val=locs[*(idx+i)][*(idx+++i)];
您确定这是从左到右计算的吗?我不确定。这是一种未定义的行为。我建议将
++i
放在访问器行下方

//编辑:


对从内存读取的值没有任何处理。这些指令很可能根本没有执行!检查字节码或添加一条
(void)val;
指令,强制生成该指令。

部分(块)每次处理器缓存中都会存储大量的内存,这使得处理器能够快速访问这些项目。但是,对于现代内存来说,这种速度是完全合理的。

正如其他答案所提到的,您看到的“速度”(即使CPU正在执行您的代码,并且没有被编译器剥离)大约是250 MBps,这对于现代系统来说是非常非常低的数字

然而,在我看来,你的方法论似乎有缺陷(不可否认,我不是基准测试方面的专家),以下是我看到的问题:

  • 对于任何这样的基准测试,即使是最简单的形式,您也需要区分随机访问和顺序访问。内存不是一个随机访问设备(尽管它的名称),并且在这里的性能非常差。您的代码似乎是随机访问内存,因此您可以将其作为一个限定符添加到结论中:您是“在约100ms的时间内从RAM的随机位置读取2500万个元素。”
  • 这种基准测试的另一个方面是延迟与吞吐量的概念。同样,如果你想从数字和时间中得出任何结论,你需要知道你到底在测量什么
  • 内存访问计数不正确。根据编译器生成的确切代码,此行:

    val = locs[*(idx + i)][*(idx + ++i)];
    
    可以实际访问内存系统4到9次

    • 充其量,如果
      i
      idx
      loc
      val
      都在寄存器中,或者对它们的访问被取消,那么您需要读取
      *(idx+i)
      ,读取
      locs[*(idx+i)]
      (请记住
      locs
      是指向数组的指针数组,而不是二维数组,)读取
      *(idx+++i)
      ,最后读取
      locs[*(idx+i)][*(idx+++i)]
      。其中一些可能会被缓存,但不太可能,因为缓存正在发生抖动
    • 在最坏的情况下,除了上述内容外,您还需要两次访问
      ++i
      (读取,然后写回),一次访问
      idx
      ,一次访问
      loc
      ,一次访问
      val
      。我不知道,您甚至可能需要对单个
      i
      进行另一次读取,和/或对两次
      idx
      进行两次读取(由于指针别名等原因。)
  • 您需要注意的是,永远不会以单个字节甚至字访问内存。内存总是以缓存线为单位进行读取和写入。系统之间的缓存线大小可能会有所不同,尽管目前最常见的大小是64字节。因此,每次读取不在缓存中的内存位置时,您都会加载64字节(或更多)从RAM。如果您正在读取的内存位置位于缓存线边界(一条缓存线中的一些字节和下一条缓存线中的一些字节),则您正在从RAM加载两条缓存线。给定一个健全的编译器并正确对齐