找回密码
 欢迎注册
查看: 9020|回复: 2

[讨论] 通过一个程序说明cache命中率对内存访问速度的影响

[复制链接]
发表于 2011-11-8 19:47:09 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?欢迎注册

×
我们知道,现代的CPU包含cache部件,cache亦称高速缓冲,其中存储了CPU最近访问过的或者将要访问的数据。和通常的RAM相比,cache也是一种存储器,其特点存储速度非常快,但同样容量的cache比同样的RAM成本高了很多,因此不能做的太大。
  
  cache亦采用多级存储结构,通常的CPU有8-64K的一级cache,256K-12M的2级cache,有的CPU还有3级cache.许多现代的CPU的2级cache与CPU同频,因此速度非常快。
  
  按照intel的说法,在通常情况下,对RAM的访问实际上近似等同于对cache的访问,因为在90%以上的情况下,RAM中的数据已经被缓冲到cache. 因为程序对RAM的访问具有局部性,也就是说,下次访问的数据的地址和这次访问的数据的地址相邻,所有当访问d[ i ]时,CPU的后台"程序"自动把和d[ i ]相邻的数据载入cache. 下次访问d[ i+1 ]时,可直接从cache中读写数据. 只用当访问的数据不在cache中,CPU才直接从RAM读取数据,由于RAM的很高的延时,故直接从RAM中读写数据比从cache中读写数据要慢得多。


  显然,对数据的顺序访问具有极高的cache命中率,因此对数据的顺序访问的性能几乎可达到内存带宽这个上限。内存带宽指内存和cache间批量数据传递的速率,注意内存带宽和内存延时无必然的联系,因为通常情况下,CPU在后台做内存和cache间数据交换,所有内存的高延时被隐藏了。
  
  反之,对数据的随机访问则使得cache命中率大大降低,一旦cache未命中,CPU不得不与内存之间交换数据,此时,内存的高延时就成为瓶颈。

  楼下的代码分别测试 对1M*4,4M*4,16M*4,64M*4的内存块进行顺序读取和随机读取所耗费的时间。其速度比如下(测试机的L2 cache is 6M)

块大小  速度比
4M      3.0
16M     7.1
64M     11.2
256M    12.5

cache_test.zip

3.38 KB, 下载次数: 7, 下载积分: 金币 -1 枚, 经验 1 点, 下载 1 次

毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2011-11-8 19:50:55 | 显示全部楼层

  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <memory.h>

  4. #include "currTime.h"

  5. typedef unsigned long DWORD;


  6. void initAccessTable(int accessTab[], int len, int isRand )
  7. {
  8.         int i,idx;

  9.         if (isRand==0)  //sequential access
  10.         {
  11.                 for (i=0;i<len;i++)
  12.                 {
  13.                         accessTab[i]=i;
  14.                 }
  15.         }
  16.         else
  17.         {
  18.                 char *tmpBuff=(char *)malloc(len);
  19.                 memset(tmpBuff,0,len);

  20.                 for (i=0;i<len;)
  21.                 {
  22.                         idx= ((rand() << 15)+ rand()) % len;
  23.                         if ( tmpBuff[idx]==0)
  24.                         {
  25.                                 accessTab[i++]=idx;
  26.                                 tmpBuff[idx]=1;
  27.                         }
  28.                 }
  29.                 free(tmpBuff);
  30.         }
  31. }

  32. int sum(DWORD pBuff[], int accessTab[], int len)
  33. {
  34.         DWORD s=0;
  35.         int i,idx;
  36.         for (i=0;i<len;i++)
  37.         {
  38.                 idx=accessTab[i];
  39.                 s += pBuff[idx];
  40.         }
  41.         return s;
  42. }

  43. //mode=0, sequential access
  44. //mode=1, random access
  45. void test_access_RAM(int blk_len, int mode)
  46. {
  47.         DWORD *pBuff=malloc(sizeof(DWORD)*blk_len);
  48.         int *accessTab=malloc(sizeof(int)*blk_len);
  49.         DWORD i,s;
  50.         double t;
  51.        
  52.         for (i=0;i<blk_len;i++)
  53.         {
  54.                 pBuff[i]=rand() & 15;
  55.         }
  56.        
  57.         initAccessTable(accessTab, blk_len, mode);
  58.        
  59.         t=currTime();
  60.         s=sum(pBuff,accessTab,blk_len);
  61.         t=currTime()-t;

  62.         printf("s=%d\n",s);
  63.         if ( mode ==0)
  64.                 printf("sequential access %d number, it take %.6f ms\n",blk_len,t*1000 );
  65.         else
  66.                 printf("random access %d number, it take %.6f ms\n",blk_len,t*1000 );

  67.         free(pBuff);
  68.         free(accessTab);

  69. }

  70. void test()
  71. {
  72.         int blk_len;
  73.         int i,loop;
  74.        
  75.         loop=3;
  76.         blk_len=1024*1024;
  77.        
  78.         for (i=0;i<loop;i++)
  79.         {        test_access_RAM(blk_len,0); }
  80.        
  81.         for (i=0;i<loop;i++)
  82.         {        test_access_RAM(blk_len,1); }
  83.        
  84.         //----------------------------
  85.         blk_len=4*1024*1024;
  86.         for (i=0;i<loop;i++)
  87.         {        test_access_RAM(blk_len,0); }
  88.        
  89.         for (i=0;i<loop;i++)
  90.         {        test_access_RAM(blk_len,1); }
  91.        
  92.         //----------------------------
  93.         blk_len=16*1024*1024;
  94.         for (i=0;i<loop;i++)
  95.         {        test_access_RAM(blk_len,0); }
  96.        
  97.         for (i=0;i<loop;i++)
  98.         {        test_access_RAM(blk_len,1); }
  99.        
  100.         //----------------------------
  101.         blk_len=64*1024*1024;
  102.         for (i=0;i<loop;i++)
  103.         {        test_access_RAM(blk_len,0); }
  104.        
  105.         for (i=0;i<loop;i++)
  106.         {        test_access_RAM(blk_len,1); }
  107. }

  108. int main(int argc, char* argv[])
  109. {
  110.         test();
  111.         return 0;
  112. }
复制代码
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2011-11-9 01:53:05 | 显示全部楼层
的确,random access 现在已经不能简单认为是O(1)了,但是细化的研究还是比较欠缺,做cache的还是做编译器的比较多,系统里面大家对cache的认识都比较简化。

最近在思索如果提供给programmer在cache-aware 的programming model,但又不至于增加复杂度
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
您需要登录后才可以回帖 登录 | 欢迎注册

本版积分规则

小黑屋|手机版|数学研发网 ( 苏ICP备07505100号 )

GMT+8, 2024-4-28 05:45 , Processed in 0.048541 second(s), 19 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表