`
xpp02
  • 浏览: 1048860 次
社区版块
存档分类
最新评论

关于cache 说明

 
阅读更多
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define DUMPCOPY for(i = 0; i < 65536; i++){\
destination = source;\
}

#define SMARTCOPY memcpy(destination, source ,65536);

int main(void)
{
char source, destination;
int i, j;

for(j = 0; j < 100; j++){
SMARTCOPY
// DUMPCOPY
}

return 0;
}

在DUMPCOPY方式下运行time ./a.out
real 0m0.333s
user 0m0.047s
sys 0m0.001s

在SMARTCOPY方式下运信time ./a.out
real 0m0.018s
user 0m0.010s
sys 0m0.001s
C专家编程中的解释:
之所以出现性能的下降,因为destination和source的大小正好是cache容量的整数倍,且cache行的填充使用的是一种优化
的算法:填充于同一cache行的主存地址恰好是该cache行大小的整数倍,所以只有地址的高位才会放入cache行的标签中,
这样destiantion和source便遍不可能同时出现在cache中,于是导致了性能的下降。
在DUMPCOPY方式下:destination 和source在使用同一cache行的情况下,会导致每次对内存的引用都没发命中cache,
使cpu的利用率大大下降,因为它不 得不等待常规的内存操作完成。
而在SMARTCOPY方式下:memcpy()函数经过特别优化来提高性能,它把先读取一个cache行再对它进行写入这个循环分分解
开来,避免了上述问题。

上面的解释我看的不太明白,请大家帮我 分析一下性能下降的原因。谢谢!建议了解一下 cache 的结构,记忆中 cache 是n-set/n-way 结构吧,1个set 好像是 64 Byte吧
cache fill 行为至少需加载 1set 吧,memcpy的实现大概是这样的(用的当然是汇编代码):

for(i=0;i<65536;i+=8){
int temp; //temp其实是寄存器。
temp=source;
temp=source;
temp=source;
temp=source;
temp=source;
temp=source;
temp=source;
temp=source;
destination[--i]=temp;
destination[--i]=temp;
destination[--i]=temp;
destination[--i]=temp;
destination[--i]=temp;
destination[--i]=temp;
destination[--i]=temp;
destination[--i]=temp;
}
原帖由 xiaozhu2007 于 2007-10-20 22:11 发表http://bbs.chinaunix.net/images/common/back.gif
#include
#include
#include

#define DUMPCOPY for(i = 0; i < 65536; i++){\
destination = source;\
}

#define SMARTCOPY memcpy(destination, sourc ...
这个问题很难两三句话就讲清楚,但可以肯定的告诉lz,你看到的性能降低不是因为书上说的原因,除非你使用了书上提到的sparcCPU。这里简单介绍一下http://www.xmenwolverine.com:

首先抛开复杂的cache结构,lz可以把cache想象成一个大数组数组的每个元素为一个cache line,我们假定一个cacheline为64 bytes。要把内存中的内容缓存到cache这个数组中,就需要一个索引,确定它存到哪一个cacheline。CPU是用地址作为索引的,当然不是整个地址,而是地址的一部分。下图中,书上提到的SPARC使用了地址的高几位作为索引,而大部分架构使用地址的中间几位作为索引。现在就可以看出为什么sparc上这种方式性能低下了。

当程序中使用到数组时,由于数组地址是连续的,那么数组每个元素地址的高几位都是相同的,它们通通对应cache中同一个cacheline。我们假设地址的高4位作为索引(再假设对于source这个数组,它地址的高4位是0011)。那么当程序顺序扫描数组时,cpu先读入64bytes,放到第3个cache line中,go on。当读到第65个字节时,CPU再次读入64 bytes,然后把第3个cacheline清空,把新的数据放进去……反复如此。这样,整个source数组在同一时刻就只有64bytes数据在cache中,每读入新的64bytes数据,就要把旧的清空,新的放入。这样反复的访存效率是很低下的,cache也没充分利用。并且这里dest数组是和source数组紧临的,也就是说source和dest数组地址的高几位都相同,它们都对应同一个cacheline!

如果用地址的中间几位作为索引,由于是低位地址,数组的不同部分它们通常是不同的。这样同一个数组的的不同部分就可以对应到cache中不同的cacheline去,那么source数组就可以被整个放到cache中去,而不是读64 byte新数据就把64bytes老数据冲掉了。减少了cache的“颠簸”。

(当然,这里举的例子可能不恰当,如果仅仅用4 bit做索引,而cache line又只有64bytes的话,cache显然太小了。而且4位做索引,中间几位通常也都是相同的。但如果用中间10bit做索引,数组不同部分显然索引就不一样了。这里为了方便讲解,用了4bit。具体的规范,应该查所用cpu的cache规范。)

lz这里看到的性能不同,是因为memcpy用了movsb这条指令做拷贝,性能当然比笨循环的方式高。当然还有库函数针对cacheline对齐做的一些优化等等

备注一下:实际上index索引的是组,组里有很多cache line,index确定内存对应哪个组后,由偏移寻址对应的cacheline。

[ 本帖最后由 zx_wing 于 2007-10-21 22:26 编辑]此外上面举例中关于cache的用语都是不正规的,只是为了说明问题简化而已。为了避免一些专业朋友给我纠正用语,还是先说了好。免得后面又来讨论cache结构,麻烦又跑题:mrgreen:不错,赞一个,我认为4楼已经把cache说得很明白了解释的不错,补充一个n-way的理解

http://blog.chinaunix.net/u1/43233/showart_339984.html回复 #4 zx_wing的帖子谢谢,讲的很好,明白 了,继续学下下呵呵回复 #4 zx_wing 的帖子还有点问题,
1。就是memcpy()函数你说的由于使用了movsb指令,所以效率比较高,根据3楼兄弟的理解,是不是因为memcpy()函数在每次循环中拷贝了8个字符,而且是通过寄存器来缓存而不是cache,所以它的效率更高。
2。我不太熟悉cache缓存的过程,就按上面的DUMPCOPY方式举个例子,在从source数组拷贝一个字符给destination的过程中,是不是首先存内存中读一个字符,然后放入cache缓存的一个cache行中,然后当下一个从source过来的数据再次要把数据写

求教-谁能提供ntohl htonl 的函数源码

入到该cache行中时,此时cache把这一行原先缓存的数据发送到destination数组中,再在该cache行中放入新的数据,过程是不是这样的?还有cache是怎么知道要把缓存http://www.51mingyu.com/的cache行发送到destionation的某个位置上呢(cache知道destination的地址吗)?

C专家编程上写的,cache主要有两种类型,一种是全写法:就是发往目的地址的同时保存到cache中;令一种是写回法,就是发往目的地址数据的时候,先不发往目的地址对应的内存,而是先写入cache中对应的一个cache行,当下一次内存再次往这个cache行写数据的时候,就把原先在该行缓存的数据写入内存,然后缓存新的数据。
我想根据书上理解的,我的cache应该是采取写回法的方式吧?!

3。呵呵,我基础不好,能给我说说,在数据复制的过程中,cpu,寄存器,内存,和cache它们之间是怎么相互合作完成这个过程的?以及它们各自的作用是什么?

谢谢!!!!!!!!!!:)回复 #9 xiaozhu2007的帖子呵呵,lz这些问题,说实话,问的太宽了。不容易说清楚,也不容易说严谨。我只能简单说一下。

1.在x86中,memcpy确实是使用movs指令簇(这里b是个后缀,表示一次拷贝的字节数,也可能是w、d、q)。它之所以快,是因为你只需要指定source地址和dest地址,拷贝工作就由硬件为你完成了。在现代cpu中,只要涉及到了访问内存,就离不开cache,没有寄存器直接和内存打交道的说法,都要通过cache(只有一种例外,就是写操作的not-write-allocate)。一句话,CPU只和寄存器和cache打交到,cache和物理内存的交互、同步都是北桥中的内存控制器做的(限于intel的CPU,amd的也有类似的机制)。在实际硬件中,cpu都以cacheline为单位读入数据,cache line长度一般都大于8个字节,所以即使你只操作了一个字节的数据,它还是会读入一个cacheline长度的数据,只是多于的没用到而已。所以你这里的8个字节说法不成立。当然,3楼的兄台演示的“循环展开”确实是一种优化的方法,这里就不多讨论了。

2.上面说了cpu只和cache打交道,并不和实际的内存打交道。就用你的程序例子来解释你的第二个问题,它的流程是这样的:
从source第一字节开始,读入source(一次读一个cache line长度),放入对应的cache line中-------> 向dest数组写数据,写到dest数组对应的cacheline中(从dest第一字节开始) -------->根据cache的策略同步回物理内存---------->反复

这里cache的策略就是lz提到的两种方法。一个叫write-through(直写),一个叫write-back(写回)。对于前者,cpu直接把dest中cacheline的内容写到内存中的dest数组去。对于后者,尽量推迟将cache line同步内存中的时间,通常只在该行cacheline需要被新的内容占用时才同步回内存。现代cpu通常采用write-back,但在un-cacheable的内存上(即该内存不能被cache,通常是MMIO空间)使用write-through。地址到cacheline的转换类似于hash表,文字很难说清楚,得画图,lz想了解应查阅相关资料。

(注意,上面举例画的流程图是以write-allocate举例的,也就是写前先写到cache中,然后同步到内存。对于non-write-allocate,没有中间那个步骤,数据直接从source数组对应的cacheline同步到内存中。

3.寄存器只是cpu做运算使用的工具,通常需要先把内存中的内容搬到寄存器中,在寄存器中运算完了再写回内存(当然,它们中间就是上面的cache)。

呵呵,lz问的这些不是基础不好,我觉得你问的深了。上面大概介绍了下过程,但实际上中间很多细节都没讲到,因为太多太多,打字累啊。我建议lz现在先对它有个感性认识就好了,需要的时候再阅读专门的资料深入学习。论坛只能让你知道个大概,不能让你知道细节。
分享到:
评论

相关推荐

    java连接cache数据库说明,数据库驱动,cache可视化工具

    Java连接Cache数据库主要涉及到的是如何使用Java编程...以上就是关于"java连接cache数据库说明,数据库驱动,cache可视化工具"的主要知识点。理解并掌握这些内容,将有助于你在Java项目中成功地集成和操作Cache数据库。

    MIPS cache指令说明

    ### MIPS Cache指令详解 在深入探讨MIPS处理器的Cache指令之前,我们首先简要回顾一下MIPS架构的特点以及Cache在其中扮演的角色。MIPS(Microprocessor without Interlocked Pipeline Stages)是一种精简指令集...

    Trace_Cache的说明和介绍

    文档中提到的“trace cache研究的几种变形说明”可能涵盖不同的优化策略,例如: - **动态Trace Cache**:根据程序运行情况动态调整大小和结构,以适应各种工作负载。 - **混合Trace Cache**:结合传统的指令缓存...

    ASP.NET中Cache的Insert参数说明

    ### 参数说明 #### 1. `string key` - **含义**:用于唯一标识缓存项的字符串键。 - **示例**:`"ds"`,这个字符串可以是任何有意义的名称,只要能够区分不同的缓存项即可。 #### 2. `object value` - **含义**:...

    jdbc连接cache的demo及jar包,自己备份.rar

    标题"jdbc连接cache的demo及jar包,自己备份.rar"指出这是一个关于使用JDBC(Java Database Connectivity)连接Cache数据库的示例项目,其中包含了必要的jar包,并且用户已经将其作为个人备份保存。这里的“Cache”...

    cache-api-1.1.1-API文档-中文版.zip

    赠送jar包:cache-api-1.1.1.jar; 赠送原API文档:cache-api-1.1.1-javadoc.jar; 赠送源代码:cache-api-1.1.1-sources.jar;...人性化翻译,文档中的代码和结构保持不变,注释和说明精准翻译,请放心使用。

    实验3 直接相联Cache设计1

    - Driver模块设计说明:解释了控制机构的驱动模块如何控制Cache的操作,如读写信号的生成、命中/缺失的判断等。 这个实验旨在帮助学生掌握直接相联Cache的设计与实现,通过实际操作加深对Cache工作原理的理解,...

    Cache数据库表及其Global结构的查看.pdf

    从提供的文件内容来看,我们可以提取以下关于Cache数据库的知识点: 1. Cache数据库基础概念:Cache是InterSystems公司开发的一个高性能的对象数据库,适合处理大量数据和复杂数据结构,通常用于医疗、金融等行业的...

    Cache设计说明1

    在本文中,我们将讨论Cache的设计,特别是针对一级Cache的考虑点和优化策略。一级Cache因其较高的命中率,通常对CPU性能有显著影响。在流水线CPU架构中,数据和指令的访问并行处理是关键,因为它们分别在流水线的...

    北交计算机体系结构Cache实验报告

    Cache 模拟器代码说明: 1. Cache 模拟器使用 newarray 二维数组模拟 Cache 2. 在直接映射和全相连映射中,newarray[index][0] 用来表示 Cache 中 index 对应记录的有效位,newarray[index][1] 代表 Cache 中 ...

    值得推荐的WordPress缓存插件DB Cache

    DB Cache插件只缓存数据库,节省了空间,就像插件作者在说明这款插件是所说的:“你听说过WP-Cache或者WP Super Cache吧,它们可以加快数据空间的加载速度,那么,忘掉它们吧!DB Cache将更快的、并节省空间,CPU的...

    odbc-连接cache数据库.rar

    压缩包中的“部署说明和代码.txt”可能包含了示例代码。通常,连接Cache数据库的C#代码如下: ```csharp using System.Data.Odbc; // 创建ODBC连接字符串 string connectionString = "Driver={InterSystems Cache ...

    ngx_cache_purge_2.4.2.tar.gz

    这个版本2.4.2的压缩包包含了ngx_cache_purge模块的所有源代码及相关文件,以便开发者在自己的Nginx环境中集成和使用。 Nginx是一款高性能、轻量级的Web服务器和反向代理服务器,广泛应用于互联网服务。其内置的...

    Supercache+超级缓存使用详解

    二、设置说明 在 Supercache 的设置中,有多个参数需要注意: * Cache page size:缓存页大小,默认为 32K。大值会增加碎片,小值增加开销。推荐值为 64K。 * Cache size:缓存大小,默认为 128M,指定分配给 ...

    oracle Library cache latch 竞争的解决

    此外,还可以通过查询`v$sysstat`视图中的统计数据来获取更多关于SQL解析的信息: ```sql SELECT name, value FROM v$sysstat WHERE name LIKE 'parsecount%'; ``` 如果发现`parsecount total`的值较高,那么...

    SuperCache5.0超级缓存

    《SuperCache5.0超级缓存:提升系统性能的秘密武器》 在当今信息化时代,计算机系统的性能优化成为了每个用户关注的焦点。SuperCache5.0超级缓存是一款针对这一需求而设计的专业软件,旨在通过优化硬盘读写速度,...

    PyPI 官网下载 | diskcache-1.3.5.tar.gz

    在实际使用`diskcache`时,开发者可以按照`README`中的说明安装库,然后导入并使用其提供的类和方法来创建和管理缓存。例如,可能有如`diskcache.Cache`这样的主要类,以及`get`、`set`、`delete`等操作方法。通过...

    PrimoCache3.0.2+永久60天+免PE重置(x86.x64)

    PrimoCache3.0.2+永久60天+免PE重置(x86.x64),亲测(win7 x86,x64 win10 x64,其他未测,理论可用),一键恢复60天试用状态,不用重启进PE这么麻烦。使用方法见文件中的说明文档,需要说明的是,程序运行需要...

Global site tag (gtag.js) - Google Analytics