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