Aakash Sabharwal
J 部分
2013 年 10 月 7 日
欢迎来到指针的世界!

课程时间安排
Cache Lab
- 截止:周四
- 开始:现在(如果你还没开始的话)
马上就考试!
- 开始做练习题
10 月 16 日 周三 —— 10 月 19 日 周六 - 10 天
大纲
- 时间表
- 内存组织
- 缓存
- 不同类型的地区(locality,我也不知道怎么翻译合适)
- 缓存组织 - Cachelab
- (a)部分:搭建缓存模拟器
- (b)部分:高效矩阵转置
内存层次

内存层次
- 寄存器
- SRAM <=(我们将讨论这两者的交互)
- DRAM <=
- 本地副存储
- 远程副存储
SRAM 与 DRAM:权衡
➤ SRAM(缓存)
- 更快(L1缓存:1个CPU周期)
- 更小(千字节(L1)或兆字节(L2))
- 更昂贵而且“吃电”
➤DRAM(主内存)
- 相对较慢(数百CPU周期)
- 更大(吉字节)
- 更便宜
地方
➤ 时间地方
- 最近被引用过的项目更有可能在未来被引用
- 在访问内存地址 X 之后,将这些字节存储在缓存中以备后续访问
➤ 空间地方
- 临近地址的项目有时候会被连续地引用
- 在访问地址 X 之后,将 X 周围的内存块存入缓存以备后续使用
内存地址
在 shark machine 上是64位

- Block offset:b 位
- Set index:s 位
- Tag Bits:地址大小 - b - s
缓存
缓存(cache)是 2s 个缓存集的集合
缓存集(cache set)是 E 个缓存行(cache line)的集合
- E 称为 结合度(associativity)
- 如果 E=1,这称之为“直接映射的”(direct-mapped)
缓存行存储一个块
- 每个块有 B = 2b 字节
总容量 = S×B×E
虚拟缓存术语

缓存的整体概念

缓存的整体概念:落空(Miss)

缓存的整体概念:落空的类型
冷落空/强行落空(clod miss/compulsory miss)
- 第一次访问块时必定落空
译注:第一次访问时,缓存中还没有任何内容,所以必然落空;至于“冷”的意思,大概是说这时缓存就如同机器刚启动,还需要预热一下。
冲突落空(conflict miss)
- 当 k 级缓存足够大,但是多个数据对象映射到同一个 k 级块时,发生冲突落空
- 例如:引用块 0,8,0,8,0,8,...每次都会落空
译注:这是一种看起来有些奇怪的落空。造成这种落空的原因是缓存的实现形式。为了保持简洁性,k 级缓存往往会通过某种算法来决定从 k+1 级缓存取回的块存放在哪里。举一个简单的例子,假设 k级 缓存容量为 4,缓存采用取模的方法,将来自 k+1 级的 块m 放在 k 级的 块 m%4 中,那么 k+1级的块 0,4,8,12……都将映射到 k级的块 0 中。从而连续访问 0,8,0,8……时,后面的数据都会覆盖前面的数据,从而每次都落空。
容量落空
- 当活动状态的内存块(正在工作的集 / working set)比缓存容量还大时,发生容量落空
译注:working set 指的是当前需要反复访问,从而应当存储在缓存中,以加快访问速度的那些块的集合;比如,我当前这段程序需要频繁访问 8 个块的数据,那么我自然想要把这 8 个块都放在缓存中,然而如果我的缓存容量只有 4 个块的大小,那么十分不幸,只能保证 4 个块的连击,另外 4 个块则会落空。
Cache Lab
- Part (a) 建造一个缓存模拟器
- Part (b) 优化矩阵转置
Part (a)
-
缓存模拟器 并不是 缓存!
—— 内容并不会被储存在内存中
—— block offsets 并没用被使用 - 你地址中的 b 位并不重要
—— 仅仅记录连击(hits),落空(misses)与驱逐(evictions)的数量 -
你的缓存模拟器需要能够对运行时给出的不同的 s,b,E,正确运行
-
使用 LRU - 最不近使用替换策略(Least Recently Used replacement policy)
—— 从内存中驱逐最不近使用的块来为后续的块腾出空间
—— 队列?时间戳?
译注:least recently used,这个想不出好的翻译。意思就是“累积未使用时间最长的”。打个比方,手机上会有清理不常用 APP 的功能,QQ 三个月内都没有使用过(最后一次使用是三个月前),QQ音乐 两个月之内没有使用过,QQ浏览器 一个月。那么,QQ 就是“最不近使用的”,推荐你卸载掉。
缓存模拟器:连击数
-
缓存只是一个缓存行的 2D 数组:
—— 结构 cache_line cache[S][E];
—— S = 2s,代表缓存集的数量
—— E 是结合度(associativity) -
每个 cache_line 包含:
—— 有效位
—— Tag
—— LRU 计数器(如果你不使用队列的话)
Cache Lab 实现:
getopt
-
如果函数声明丢失,getopt()在 unix 命令行上进行自动化元素解析:
—— 通常在循环内进行调用以获取参数
—— 返回值保存在本地变量中
—— 当 getopt()返回 -1 时,表示没有更多选项了 -
为了使用 getopt,你的程序需要包含头文件 unistd.h
-
如果你不在 shark machine 上运行,那么你需要 #include<getopt.h>
—— 更好的建议:在 Shark Machine 上运行!
getopt
-
在保存返回值的本地变量上使用 switch 语句
—— 命令行的每个输入可以被分别处理
—— “optarg” 是一个很重要的变量 - 它将指向选项参数的值 -
思考如何处理非法输入
getopt 示例
int main(int argc, char** argv){
int opt, x,y;
/* looping over arguments */
while(-1 != (opt = getopt(argc, argv, "x:y:"))){
/* determine which argument it’s processing */
switch(opt) {
case 'x':
x = atoi(optarg);
break;
case 'y':
y = atoi(optarg);
break;
default:
printf("wrong argument\n");
break;
}
}
}
假设可执行程序叫做“foo”,我们通过
./foo -x 1 -y 3
来将值 1 传给变量 x,以及 3 传给 y
fscanf
-
fscanf()函数与 scanf()函数十分相似,只不过 fscanf()可以指定读取的流(scanf()总是从标准输入 stdin 读入)
—— 参数:
- 文件指针
- 带有读取文件方式信息的格式化字符串
- 其余为指向用于储存文件中数据的变量的指针
—— 通常会在循环中使用这个函数,直至遇到文件尾 -
从追迹文件中读取行时,fscanf 将很有用
—— L 10,1
—— M 20,1
示例
FILE * pFile; //pointer to FILE object
pFile = fopen ("tracefile.txt","r"); //open file for reading
char identifier;
unsigned address;
int size;
// Reading lines like " M 20,1" or "L 19,3"
while(fscanf(pFile,“ %c %x,%d”, &identifier, &address, &size)>0){
// Do stuff
}
fclose(pFile); //remember to close file when done
malloc / free
- 使用 malloc 来在堆上分配内存
-
一定要 free 掉你 malloc 的东西,不然可能导致内存泄漏
—— some_pointer_you_malloced = malloc(sizeof(int));
—— free(some_pointer_you_malloced); - 不要去 free 你没分配过的内存
Part(b)
高效矩阵转置
-
矩阵转置(A => B)
转置
- 如何利用缓存来优化这个操作?
网友评论