最近两天有幸接受Intel公司的培训。然后培训的时候讲到cache命中的问题。了解到cpu在从memory中load数据的时候其实是将memory中的地址连续的一段数据都load到cache中的,而这时基于这样一个设想:即当要用到的数据附近的数据就是下一次或者下面几次将要进行的操作的数据。。所以将地址连续的一段数据load到cache中,这样将来操作的时候就可以直接从cache而不是内存中取数据,增加cache命中率,减少数据传输的时间。进而提高程序性能。
当时还讲到一个具体的例子,,最典型的就是矩阵加法。。因为矩阵加法必须有二重循环,形式如下:
for(size i = 0; i < HEIGHT; ++i){
for(size j = 0; j < WIDTH; ++j){
C[i][j] = A[i][j] + B[i][j];
}
}
这是一个不好的实现,因为内层循环变量时j递增时,我们可以具体看看循环里面的操作:
C[j][i]的访问是跳跃式的前进的(stride),比如j = 1时,我们将会访问C[1][i],这样,C[1][i]附近的数据将会被load到cache,但是j递增之后。我们下一个操作访问的数据时C[2][i],它和C[1][i]地址差距为i,这样C[2][i]不一定在缓存中。还得重新到memory中load。减缓了速度。。
相反,我们再看看另外一种实现:
for(size i = 0; i < HEIGHT; ++i){
for(size j = 0; j < WIDTH; ++j){
C[i][j] = A[i][j] + B[i][j];
}
}
针对此种实现,对于每一次递增的j,内存地址的增加都是挨着的,所以内存访问效率更高,当然程序速度更快。。
以下是本人基于上述两种方法对矩阵加法的实现,并对比给出执行时间的差距:
1.跳跃式访问:
#include <stdio.h>
#include<time.h>
#include<stdlib.h>
#define WIDTH 20000
#define HEIGHT 20000
#define MAX 100
template<typename size>
size** matAdd(size** A, size** B){
/*
size height = sizeof(A)/sizeof(A[0]);
printf("sizeof(A) is :%d\n",sizeof(A));
printf("sizeof(A[0]) is:%d\n",sizeof(A[0]));
printf("height is: %d\n", height);
size width = sizeof(A[0])/sizeof(A[0][0]);
printf("width is :%d\n", width);
*/
size** C = new size*[HEIGHT];
for(size i = 0; i < HEIGHT; ++i){
C[i] = new size[WIDTH];
}
for(size i = 0; i < HEIGHT; ++i){
for(size j = 0; j < WIDTH; ++j){
C[i][j] = A[i][j] + B[i][j];
}
}
return C;
}
int main(){
typedef int size;
size** A = new size*[HEIGHT];
for(size i = 0; i < HEIGHT; ++i){
A[i] = new size[WIDTH];
}
srand((int)time(0));
size** B = new size*[HEIGHT];
for(size i = 0; i < HEIGHT; ++i){
B[i] = new size[WIDTH];
}
for(size i = 0; i < HEIGHT; ++i){
for(size j = 0; j < WIDTH; ++j){
A[i][j] = rand() % MAX;
B[i][j] = rand() % MAX;
}
}
size** C = matAdd(A, B);
}
执行时间如下所示:
time ./matAdd
real 0m57.507s
user 0m54.718s
sys 0m2.781s
相对比下:连续访问内存实现方式如下:
#include <stdio.h>
#include<time.h>
#include<stdlib.h>
#define WIDTH 20000
#define HEIGHT 20000
#define MAX 100
template<typename size>
size** matAdd(size** A, size** B){
/*
size height = sizeof(A)/sizeof(A[0]);
printf("sizeof(A) is :%d\n",sizeof(A));
printf("sizeof(A[0]) is:%d\n",sizeof(A[0]));
printf("height is: %d\n", height);
size width = sizeof(A[0])/sizeof(A[0][0]);
printf("width is :%d\n", width);
*/
size** C = new size*[HEIGHT];
for(size i = 0; i < HEIGHT; ++i){
C[i] = new size[WIDTH];
}
for(size i = 0; i < HEIGHT; ++i){
for(size j = 0; j < WIDTH; ++j){
C[i][j] = A[i][j] + B[i][j];
}
}
return C;
}
int main(){
typedef int size;
size** A = new size*[HEIGHT];
for(size i = 0; i < HEIGHT; ++i){
A[i] = new size[WIDTH];
}
srand((int)time(0));
size** B = new size*[HEIGHT];
for(size i = 0; i < HEIGHT; ++i){
B[i] = new size[WIDTH];
}
for(size i = 0; i < HEIGHT; ++i){
for(size j = 0; j < WIDTH; ++j){
A[i][j] = rand() % MAX;
B[i][j] = rand() % MAX;
}
}
size** C = matAdd(A, B);
}
执行时间如下所示:
time ./matAdd
real 0m22.881s
user 0m20.197s
sys 0m2.680s
由此可见,性能相差一倍有余。这也更加体现出连续访问内存的重要性。。
分享到:
相关推荐
论文中提到的应用程序签名问题是指多MAPS(Multi-MAP Signature)无法全面反映HPC应用的内存访问模式。作者分析了这个问题,并提出了相应的解决建议,以更好地捕捉和预测不同应用的内存行为。实验结果证实了所提出的...
RDMA允许节点直接访问远程机器的内存,而NUMA则是多处理器架构中用来扩展内存访问性能的技术。当这两种技术结合时,会在某些配置下出现性能惩罚。本文是第一篇研究这类配置的论文,提供了NUMA对RDMA基于系统的定量...
本文档是一篇研究论文,对数据库访问性能优化进行了深入探讨,尤其着重于物理设计层面、JDBC驱动程序的选择,以及代码编写等关键方面。 首先,物理设计层面的优化包括但不限于数据库表的规范化设计。规范化可以减少...
### 性能测试方法论详解 #### 一、性能测试基本概念及流程 **性能测试**是一种评估系统或组件在特定工作负荷下表现的过程,旨在验证系统是否能够满足预定的性能要求。它不仅关注系统的响应时间和吞吐量,还考虑到...
10. 数据量对性能的影响:在进行微信小程序API性能测试时,可以通过逐渐增加数据量来观察API性能的变化。例如,当数据量达到一定数值后,可能会发现系统性能开始下降,或者无法支持更深层次的查询。 11. 系统瘫痪和...
频繁的动态内存分配和释放可能导致内存碎片,即内存被分割成很多小块,难以找到连续的大块内存,影响程序性能。 8. **C语言内存模型**: C语言的内存模型允许直接访问硬件,程序员可以直接操控内存地址,但这也...
AdaptMemBench的出现解决了这个问题,它是一个专门设计用于模拟特定应用程序内存访问模式的基准工具。该框架采用了一组模板来管理标准的计时和测量任务,允许研究人员构建适应多面体模型,以测试潜在的代码优化效果...
由于多处理器的性能取决于许多以不同方式影响性能的参数,因此在指令执行级使用定时Petri网对基于共享内存总线的多处理器进行建模,并使用已开发的模型来研究处理器随系统中处理器数量的变化而变化。 结果很好地...
然而,多核异构架构也给共享内存的设计带来了新的挑战,特别是在不同核心间的协调、内存访问一致性、资源管理和性能优化等方面。 在多核异构模式下,设计一个有效的共享内存系统需要考虑以下几个关键点: 首先,...
此外,可能还涉及了处理器的并行处理能力、指令集优化、内存访问模式等对数据库性能的优化策略。 论文可能通过实验方法,比较了不同的内存数据库管理系统在TPC-H查询集上的执行时间和资源利用率,以此来评估处理器...
文章强调了CPU缓存对性能影响的重要性,指出了CPU缓存、内存控制器设计和DMA技术(直接内存访问)的重要性。作者表明,这些硬件设计的变更几乎都对内存性能有决定性的影响。尽管文章主要讨论的是CPU缓存和内存控制器...
这有助于减少写操作对主存的影响,并提高整体性能。另一方面,失效队列用于跟踪需要被标记为无效的缓存行,以保持缓存间的数据一致性。 #### 内存屏障的作用 内存屏障作为一种必要的手段,其目的是强制执行有序的...
因此,有必要在内存中建立文件系统,利用内存文件系统(Memory File System,简称MFS)来提高数据的存取速度,并节约外部存储空间,同时也能减少对磁盘的操作,保护磁盘不受频繁读写的影响。 内存文件系统是指在...
3. **提高了并发性能**:内存池通过使用互斥锁来同步访问,确保多线程环境下的一致性和安全性。 在实际应用中,内存池广泛应用于各种场景,如网络协议栈、文件系统缓存管理等领域,以提高整体系统的性能和响应速度...
研究人员发现,DyCache能够在对于具有非规则内存访问模式的应用程序上实现相比128字节基线缓存(baseline cache)40%的指令每周期(IPC)的几何平均提升,而对具有规则内存访问模式的应用程序性能没有影响。...
内存访问效率直接影响到GPU程序的性能,尤其是在处理大规模并行计算任务时,优化访存行为至关重要。 【性能分析】对GPU程序的访存行为进行深入分析,有助于找出性能瓶颈,理解不同存储层次对程序执行的影响。例如,...