我进行了一项小测试,以确定访问指针向量与值向量的行为。事实证明,对于小内存块,两者都表现得同样好,但是,对于大内存块,存在显着差异。
这种行为的解释是什么?
对于下面的代码,在我的电脑上执行,D = 0的差异约为35%,而D = 10的差异则不明显。
int D = 0;
int K = 1 << (22 - D);
int J = 100 * (1 << D);
int sum = 0;
std::vector<int> a(K);
std::iota(a.begin(), a.end(), 0);
long start = clock();
for (int j = 0; j < J; ++j)
for (int i = 0; i < a.size(); ++i)
sum += a[i];
std::cout << double(clock() - start) / CLOCKS_PER_SEC << " " << sum << std::endl;
sum = 0;
std::vector<int*> b(a.size());
for (int i = 0; i < a.size(); ++i) b[i] = &a[i];
start = clock();
for (int j = 0; j < J; ++j)
for (int i = 0; i < b.size(); ++i)
sum += *b[i];
std::cout << double(clock() - start) / CLOCKS_PER_SEC << " " << sum << std::endl;
答案 0 :(得分:1)
从全局内存中获取数据很慢,因此CPU有一点点非常快的内存来帮助内存访问跟上处理器。处理内存请求时,您的计算机将尝试通过在您请求的位置周围请求一大堆整数或指针并将它们存储在缓存中来加速将来对内存中的单个整数或指针的请求。一旦快速内存已满,就必须在每次请求新内容时摆脱最不喜欢的位。
您的小问题可能完全或大部分适合缓存,因此内存访问速度非常快。大问题无法适应这种快速记忆,因此您遇到了问题。矢量存储为K个连续存储位置。当您访问int
的向量时,它会加载int和一些附近的值,这些值可以立即使用。但是,当您加载int*
时,它会加载指向实际值的指针以及其他几个指针。这占用了一些记忆。然后,当您使用*
取消引用时,它会加载实际值,并可能加载附近的某些实际值。这会占用更多内存。您不仅需要执行更多工作,还要更快地填充内存。实际的时间增加会有所不同,因为它高度依赖于体系结构,操作(在这种情况下为+
)和内存速度。此外,您的编译器将非常努力地将延迟最小化。