我试图了解.NET世界中的CPU缓存性能.具体来说,我正在研究Igor Ostovsky 关于处理器缓存效果的文章.
我在他的文章中经历了前三个例子,并记录了与他的文章大不相同的结果.我想我一定做错了,因为我机器上的表现几乎与他在文章中所表现出来的结果完全相反.我没有看到我期望的缓存未命中的巨大影响.
我究竟做错了什么?(错误代码,编译器设置等)
以下是我机器上的性能结果:



如果有帮助,我机器上的处理器是Intel Core i7-2630QM.这是关于我的处理器缓存的信息:

我已经在x64 Release模式下编译了.
以下是我的源代码:
class Program
{
static Stopwatch watch = new Stopwatch();
static int[] arr = new int[64 * 1024 * 1024];
static void Main(string[] args)
{
Example1();
Example2();
Example3();
Console.ReadLine();
}
static void Example1()
{
Console.WriteLine("Example 1:");
// Loop 1
watch.Restart();
for (int i = 0; i < arr.Length; i++) arr[i] *= 3;
watch.Stop();
Console.WriteLine(" Loop 1: " + watch.ElapsedMilliseconds.ToString() + " ms");
// Loop 2
watch.Restart(); …Run Code Online (Sandbox Code Playgroud) 在CUDA 2.0设备上有没有办法只为一个特定变量禁用L1缓存?我知道,一个可以在编译时禁用L1缓存添加标志-Xptxas -dlcm=cg,以nvcc对所有内存操作.但是,我想仅对特定全局变量的内存读取禁用缓存,以便所有其余内存读取都通过L1缓存.
基于我在网络上进行的搜索,可能的解决方案是通过PTX汇编代码.
以下示例代码生成一个size的矩阵N,并将其转换SAMPLES次数.当N = 512转置操作的平均执行时间是2144 ?s(coliru链接)时.乍一看,没有什么特别的,对吗?...
好吧,这是结果
N = 513 → 1451 ?s N = 519 → 600 ?sN = 530 → 486 ?sN = 540→ 492 ?s(终于!理论开始工作:).那么为什么在实践中这些简单的计算与理论如此不同呢?此行为是否与CPU缓存一致性或缓存未命中有关?如果是这样请解释.
#include <algorithm>
#include <iostream>
#include <chrono>
constexpr int N = 512; // Why is 512 specifically slower (as of 2016)
constexpr int SAMPLES = 1000;
using us = std::chrono::microseconds;
int A[N][N];
void transpose()
{
for ( int i = …Run Code Online (Sandbox Code Playgroud) 有没有办法读取CPU缓存内容?架构适用于ARM.
我使一系列地址无效,然后想确定它是否无效.虽然我可以读取和写入地址范围,无论是否有无效和检查失效,我想知道是否可以读取缓存内容
谢谢!!
我目前正试图在我的一个C程序中理解一些非常奇怪的行为.显然,在它结尾处添加或删除看似无关紧要的行会大大影响程序其余部分的性能.
我的程序看起来有点像这样:
int large_buffer[10000];
void compute(FILE * input) {
for(int i=0; i<100; i++) {
do_lots_of_stuff();
printf(".");
fflush(stdout);
}
}
int main() {
FILE *input = fopen("input.txt", "r");
compute(input);
fclose(input); // <--- everything gets 2x slower if I comment this out (!)
return 0;
}
Run Code Online (Sandbox Code Playgroud)
理论上,fclose(input)主函数末尾的那一行应该无关紧要,因为OS无论如何都应该在程序结束时自动关闭文件.但是我观察到当我将fclose语句包含在内时,我的程序需要2.5秒才能运行.差异2倍!这不是由于程序开始或结束时的延迟:在.使用fclose语句的版本中打印输出的速度明显更快.
我怀疑这可能与某些内存对齐或缓存未命中问题有关.如果我用另一个函数(如ftell)替换fclose,它也需要5s来运行,如果我减小large_bufferto <= 8000元素的大小,那么它总是在2.5秒内运行,无论fclose语句是否存在.
但我真的希望能够100%确定这种奇怪行为背后的罪魁祸首.是否有可能在某种分析器或其他工具下运行我的程序,这些工具会给我这些信息?到目前为止,我尝试运行两个版本,valgrind --tool=cachegrind但它报告了我的程序的两个版本的相同数量的缓存未命中(0%).
编辑1:运行我的程序的两个版本后,我perf stat -d -d -d得到以下结果:
Performance counter stats for './no-fclose examples/bench.o':
5625.535086 task-clock (msec) # 1.000 CPUs utilized
38 context-switches …Run Code Online (Sandbox Code Playgroud) 我想学习如何编写更好的代码,利用CPU的缓存.使用连续记忆似乎是理想的情况.话虽如此,我很好奇是否可以使用非连续内存进行类似的改进,但是需要遵循一系列指针,例如:
struct Position {
int32_t x,y,z;
}
...
std::vector<Position*> posPointers;
...
updatePosition () {
for (uint32_t i = 0; i < posPointers.size(); i++) {
Position& nextPos = *posPointers[i];
nextPos.x++;
nextPos.y++;
nextPos.z++;
}
}
Run Code Online (Sandbox Code Playgroud)
这只是一些粗略的模拟代码,为了正确地学习这个,我们只是说所有的位置结构都是在整个堆中随机创建的.
英特尔的i7等现代智能处理器能不能直接看到它很快就会需要X_ptr数据?以下代码行会有帮助吗?
... // for loop
Position& nextPos1 = *posPointers[i];
Position& nextPos2 = *posPointers[i+1];
Position& nextPos3 = *posPointers[i+2];
Position& nextPos4 = *posPointers[i+3];
... // Work on data here
Run Code Online (Sandbox Code Playgroud)
我读过一些演示幻灯片,似乎表明这样的代码会导致处理器预取一些数据.真的吗?我知道有非标准的,特定于平台的方式来调用预取__builtin_prefetch,但是把它扔到这个地方看起来就像是一个丑陋的过早优化.我正在寻找一种方法,我可以下意识地编写缓存效率的代码.
我从这个链接(https://gist.github.com/jiewmeng/3787223)获得了这个程序.我一直在网上搜索,以便更好地理解处理器缓存(L1和L2).我想成为能够编写一个程序,让我能够猜测我的新笔记本电脑上L1和L2缓存的大小.(仅用于学习目的.我知道我可以检查规格.)
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define KB 1024
#define MB 1024 * 1024
int main() {
unsigned int steps = 256 * 1024 * 1024;
static int arr[4 * 1024 * 1024];
int lengthMod;
unsigned int i;
double timeTaken;
clock_t start;
int sizes[] = {
1 * KB, 4 * KB, 8 * KB, 16 * KB, 32 * KB, 64 * KB, 128 * KB, 256 * KB,
512 * KB, 1 * MB, 1.5 …Run Code Online (Sandbox Code Playgroud) 当处理某些数据流(例如,来自网络的请求)时,使用一些临时存储器是很常见的.例如,URL可以分成多个字符串,每个字符串可能从堆中分配内存.这些实体的使用通常是短暂的,并且内存总量通常相对较小,应该适合CPU缓存.
在用于临时字符串的内存被释放时,字符串内容很可能只存在于缓存中.但是,CPU并不知道要释放的内存:释放只是内存管理系统中的更新.结果,当CPU高速缓存用于其他存储器时,CPU可能最终不必要地将未使用的内容写入实际存储器 - 除非存储器释放以某种方式向CPU指示存储器不再被使用.因此,问题变为:
释放内存的内存管理功能是否表示可以丢弃相应内存的内容?有没有办法向CPU指示不再使用内存?(至少,对于某些CPU:显然,架构之间可能存在差异)由于不同的实现可能会在质量上有所不同,可能会或可能不会做任何花哨的事情,问题实际上是否有任何内存管理实现将内存指示为未使用?
我确实意识到始终使用相同的内存领域可能是一种缓解策略,以避免对实际内存的不必要写入.在这种情况下,将使用相同的缓存内存.类似地,内存分配可能总是产生相同的内存,也避免了不必要的内存传输.但是,我可能不需要依赖任何适用的技术.
memory performance memory-management dynamic-memory-allocation cpu-cache
Apple提供了一些有关同步变量甚至执行顺序的文档.我没看到的是有关CPU缓存行为的任何文档.Objective-C开发人员有什么保证和控制来确保线程之间的缓存一致性?
考虑以下情况,其中变量在后台线程上设置但在主线程上读取:
self.count = 0;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
self.count = 5;
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"%i", self.count);
});
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,是否应该计数?
更新1
线程间通信中的文档保证共享变量可用于线程间通信.
在两个线程之间传递信息的另一种简单方法是使用全局变量,共享对象或共享内存块.
在这种情况下,这不是必需的吗?这与内存屏障和易失性变量中的文档相矛盾:
但是,如果该变量在另一个线程中可见,则此类优化可能会阻止另一个线程注意到对它的任何更改.将volatile关键字应用于变量会强制编译器在每次使用时从内存加载该变量.
因此,我仍然不知道是否需要volatile,因为编译器可以使用寄存器缓存优化,或者如果不需要,因为编译器以某种方式知道它是"共享"的东西.
关于共享变量是什么或者编译器如何知道它的文档不是很清楚.在上面的例子中,是否计算共享对象?假设count是一个int,那么它不是一个对象.它是共享的内存块还是只适用于__block声明的变量?对于非块,非对象,非全局共享变量,可能需要volatile.
更新2
对于每个人来说,这是一个关于同步的问题,但事实并非如此.这是关于iOS平台上的CPU缓存行为.
我的程序添加了浮点数组,并且在通过 MSVC 和 G++ 进行最大优化编译时展开了 4 倍。我不明白为什么两个编译器都选择展开 4x,所以我做了一些测试,发现只是偶尔在运行时手动展开 1-vs-2 或 1-vs-4 迭代的 t 测试给出 p 值 ~0.03, 2-vs-4 很少 < 0.05,2-vs-8+ 总是 > 0.05。
如果我将编译器设置为使用 128 位向量或 256 位向量,它总是展开 4x,这是 64 字节缓存行的倍数(有意义还是巧合?)。
我考虑缓存行的原因是因为我没想到展开会对顺序读取和写入千兆字节浮点数的内存绑定程序产生任何影响。在这种情况下展开是否有好处?也有可能没有显着差异,而且我的样本量不够大。
我发现这个博客说,对于中等大小的数组,手动展开数组副本速度更快,而对于较长的数组,流式传输速度最快。他们的 AvxAsyncPFCopier 和 AvxAsyncPFUnrollCopier 函数似乎受益于使用整个缓存行以及手动展开。博客中的基准测试及其来源在这里。
#include <iostream>
#include <immintrin.h>
int main() {
// example of manually unrolling float arrays
size_t bytes = sizeof(__m256) * 10;
size_t alignment = sizeof(__m256);
// 10 x 32-byte vectors
__m256* a = (__m256*) _mm_malloc(bytes, alignment);
__m256* b = …Run Code Online (Sandbox Code Playgroud) cpu-cache ×10
performance ×6
caching ×5
c++ ×3
c ×2
.net ×1
arrays ×1
assembly ×1
c# ×1
cachegrind ×1
cpu ×1
cuda ×1
hardware ×1
ios ×1
low-latency ×1
matrix ×1
memory ×1
objective-c ×1
pointers ×1
profiling ×1
ptx ×1
simd ×1
visual-c++ ×1