Tag: cpu cache

数组结构,结构数组和内存使用模式

我一直在阅读有关SOA的内容,我想在我正在构建的系统中尝试实现它。 我正在编写一些简单的C结构来做一些测试,但我有点困惑,现在我有一个vec3 3个不同的结构。 我将在下面显示它们,然后进一步详细说明这个问题。 struct vec3 { size_t x, y, z; }; struct vec3_a { size_t pos[3]; }; struct vec3_b { size_t* x; size_t* y; size_t* z; }; struct vec3 vec3(size_t x, size_t y, size_t z) { struct vec3 v; vx = x; vy = y; vz = z; return v; } struct vec3_a vec3_a(size_t […]

cpu cacheline和prefetch策略

我读了这篇文章http://igoro.com/archive/gallery-of-processor-cache-effects/ 。 文章说因为缓存行延迟,代码: int[] arr = new int[64 * 1024 * 1024]; // Loop 1 for (int i = 0; i < arr.Length; i++) arr[i] *= 3; // Loop 2 for (int i = 0; i < arr.Length; i += 16) arr[i] *= 3; 将几乎有相同的执行时间,我写了一些示例c代码来测试它。 我使用Ubuntu 64位运行Xeon(R)E3-1230 V2上的代码,使用Debian运行ARMv6兼容处理器rev 7,并在Core 2 T6600上运行它。 所有结果都不是文章所说的。 我的代码如下: long int […]

您如何从用户模式代码中一般性地检测缓存行关联性?

我正在为valgrind中的cachegrind / callgrind工具编写一个小补丁,它将使用完全通用的代码,CPU指令和缓存配置自动检测(现在只有x86 / x64自动配置,而其他架构不提供CPUID类型配置为非特权代码)。 此代码需要完全在非特权上下文中执行,即纯用户模式代码。 它还需要可以在非常不同的POSIX实现中移植,因此grokking / proc / cpuinfo不会这样做,因为我们的目标系统之一没有这样的东西。 检测CPU的频率,高速缓存的数量,它们的大小,甚至高速缓存行大小都可以使用100%通用POSIX代码完成,该代码没有任何特定于CPU的操作码(只是很多合理的假设,例如添加两个数字在一起,如果没有内存或寄存器依赖性停顿,可能会在一个周期内执行)。 这部分相当简单。 什么不是那么简单,为什么我问StackOverflow,是如何检测给定缓存的缓存行关联性? 关联性是缓存中可以包含来自主内存的给定缓存行的位数。 我可以看到可以检测到L1缓存关联,但L2缓存? 当然L1关联性会受到影响吗? 我很欣赏这可能是一个无法解决的问题。 但我把它扔到StackOverflow上,希望有人知道我不知道的事情。 请注意,如果我们在这里失败,我将简单地在四方的关联性默认值中进行硬编码,假设它不会对结果产生巨大影响。 谢谢, 尼尔

CLFLUSH如何处理尚未缓存的地址?

我们正在尝试使用Intel CLFLUSH指令在用户空间中刷新Linux中进程的缓存内容。 我们创建了一个非常简单的C程序,它首先访问一个大型数组,然后调用CLFLUSH来刷新整个数组的虚拟地址空间。 我们测量CLFLUSH刷新整个arrays所需的延迟。 程序中arrays的大小是一个输入,我们将输入从1MB变为40MB,步长为2MB。 根据我们的理解,CLFLUSH应该刷新缓存中的内容。 所以我们期望看到整个arrays的刷新延迟首先在arrays大小方面线性增加,然后在arrays大小大于20MB(这是我们程序的LLC的大小)之后延迟应该停止增加。 然而,实验结果非常令人惊讶,如图所示。 数组大小超过20MB后,延迟不会停止增加。 我们想知道如果地址不在缓存中,CLFLUSH是否可能在CLFLUSH将地址刷出缓存之前引入地址? 我们还尝试在英特尔软件开发人员手册中进行搜索,但没有找到任何解释,如果地址不在缓存中,CLFLUSH会做什么。 以下是我们用于绘制图形的数据。 第一列是以KB为单位的数组大小,第二列是以秒为单位刷新整个数组的延迟。 任何建议/建议都不仅仅是值得赞赏的。 [改性] 以前的代码是不必要的。 尽管CLFLUSH具有相似的性能,但它可以更容易地在用户空间中完成。 所以我删除了凌乱的代码以避免混淆。 SCENARIO=Read Only 1024,.00158601000000000000 3072,.00299244000000000000 5120,.00464945000000000000 7168,.00630479000000000000 9216,.00796194000000000000 11264,.00961576000000000000 13312,.01126760000000000000 15360,.01300500000000000000 17408,.01480760000000000000 19456,.01696180000000000000 21504,.01968410000000000000 23552,.02300760000000000000 25600,.02634970000000000000 27648,.02990350000000000000 29696,.03403090000000000000 31744,.03749210000000000000 33792,.04092470000000000000 35840,.04438390000000000000 37888,.04780050000000000000 39936,.05163220000000000000 SCENARIO=Read and Write 1024,.00200558000000000000 3072,.00488687000000000000 5120,.00775943000000000000 7168,.01064760000000000000 9216,.01352920000000000000 11264,.01641430000000000000 13312,.01929260000000000000 15360,.02217750000000000000 17408,.02516330000000000000 19456,.02837180000000000000 21504,.03183180000000000000 23552,.03509240000000000000 […]

如何在动态图中避免“堆指针意大利面”?

一般问题 假设您正在编写一个由图形组成的系统,以及可以根据相邻节点的配置激活的图形重写规则。 也就是说,您有一个在运行时期间无法预测地增长/缩小的动态图形。 如果你天真地使用malloc ,新的节点将被分配在内存中的随机位置; 经过足够的时间,你的堆将成为指针意大利面,给你可怕的缓存效率。 是否有任何轻量级的增量技术可以使连接在一起的节点在内存中保持紧密联系 ? 我尝试了什么 我唯一能想到的是将节点嵌入笛卡尔空间,并使用一些物理弹性模拟来排斥/吸引节点。 那将有线节点保持在一起,但看起来很傻,我想模拟的开销会比缓存效率加速更大。 坚实的例子 这是我正在尝试实施的系统。 这是我试图在C中优化的代码的简短片段。 这个 repo是JS中的原型,工作实现,具有可怕的缓存效率(以及语言本身)。 该video以图形方式显示系统的运行情况。

通过已知索引重新调整,聚集,分散对数组进行缓存友好复制

假设我们有一个数据数组和另一个带索引的数组。 data = [1, 2, 3, 4, 5, 7] index = [5, 1, 4, 0, 2, 3] 我们想要从index位置的data元素创建一个新数组。 结果应该是 [4, 2, 5, 7, 3, 1] 朴素算法适用于O(N),但它执行随机存储器访问。 你能建议具有相同复杂性的CPU缓存友好算法吗? PS在我的特定情况下,数据数组中的所有元素都是整数。 PPSarrays可能包含数百万个元素。 PPPS我可以使用SSE / AVX或任何其他x64特定的优化

为什么我的8M L3缓存不能为大于1M的arrays带来任何好处?

我受这个问题的启发,编写了一个简单的程序来测试我的机器在每个缓存级别的内存带宽: 为什么矢量化循环没有性能改进 我的代码使用memset反复写入缓冲区(或缓冲区)并测量速度。 它还保存了最后打印的每个缓冲区的地址。 这是列表: #include #include #include #include #define SIZE_KB {8, 16, 24, 28, 32, 36, 40, 48, 64, 128, 256, 384, 512, 768, 1024, 1025, 2048, 4096, 8192, 16384, 200000} #define TESTMEM 10000000000 // Approximate, in bytes #define BUFFERS 1 double timer(void) { struct timeval ts; double ans; gettimeofday(&ts, NULL); ans = ts.tv_sec […]

使用while()和sleep()将程序置于睡眠模式之间的区别

我创建了一个共享对象,并从两个不同的程序访问它并测量时间。 DATA数组是两个进程之间的共享对象。 案例1:在program1中使用while program1: access shared DATA array ;// to load into memory and avoid page fault during access time calculation start=timer; access shared DATA array end=timer; Time_needed= end-start printf(“Inside Program1, Time1=%d\n”,Time_needed); start=timer; access shared DATA array end=timer; Time_needed= end-start printf(“Inside Program1, Time2=%d\n”,Time_needed); while(1){}; // I replace this by sleep(1000) in CASE-2 计划2: access shared […]

包容性还是排他性? 英特尔酷睿IvyBridge处理器中的L1,L2缓存

我使用的是Intel Core IvyBridge处理器,Intel(R)Core(TM)i7-3770 CPU @ 3.40GHz(L1-32KB,L2-256KB,L3-8MB)。 我知道L3是包容性的,并且在多个核心之间共享。 我想知道关于我的系统的以下内容 第1部分 : L1是包容性还是排他性? L2是包容性还是排他性? 第2部分 : 如果L1和L2都包含在内,那么为了找到L2的访问时间,我们首先声明一个大小超过L2缓存(256KB)的数组(1MB),然后开始访问整个数组以加载到L2缓存中。 之后,我们从开始索引到结束索引访问数组元素,步长为64B,因为缓存行大小为64B。 为了获得更准确的结果,我们重复这个过程(在索引,开始端访问数组元素)多次,比如100万次并取平均值。 我理解为什么这种方法给出了如下正确的结果 – 当我们访问大小超过L2高速缓存大小的数组时,整个数组从主存储器加载到L3,然后从L3加载到L2,然后L2加载到L1。 最近访问的是整个arrays的最后32KB在L1中。 由于包含属性和高速缓存一致性,整个arrays也存在于L2和L3高速缓存中。 现在,当我开始从启动索引再次访问数组时,该索引不在 L1缓存中,而是在L2缓存中,因此将存在缓存未命中,并且它将从L2缓存加载。 这样,整个arrays的所有元素都需要更长的访问时间,总的来说,我将获得整个arrays的总访问时间。 为了获得单一访问权限,我将取总访问量的平均值。 我的问题是 – 我是否正确? 提前致谢 。

循环平铺。 如何选择块大小?

我正在尝试学习循环优化。 我发现循环平铺有助于使数组循环更快。 我尝试使用下面给出的两个代码块,有或没有循环阻塞,并测量两者的时间。 我大部分时间都没有发现明显的差异。 我测试了不同的块大小,但我不知道如何选择块大小。 如果我的方向错了,请帮助我。 实际上我发现没有块的循环工作速度快了很多倍。 一个。 随着阻止 int max = 1000000; int B = 100; for (i = 0; i < max; i += B) { for (j = i; j < (i + B); j++) { array[j] = 0; } } 湾 没有阻止 for (i = 0; i < max; i++) { […]