实验答案托管在我的GitHub

由于Cache Lab的Part B的64 × 64的矩阵一直拿不到满分,索性放在了一边,先从老的Performance Lab开始做起。

实验简介

Performance Lab - Code Optimization是有关性能优化的实验,对应于书本的第5章:优化程序性能和第6章:存储器层次结构。主要提供了利用书本所学的知识优化代码的机会。

本实验主要分为两个部分,每个部分分别对于一个简单的图像处理函数进行优化。第一个部分所优化的函数旋转(rotate)对于缓存更加敏感,而第二个部分所优化的函数平滑(smooth)则属于计算密集型函数。对它们优化的侧重是有所不同的。

本实验的具体介绍见实验讲义

实验介绍

在本实验中,我们将实现两个图像处理函数——旋转和平滑。

我们将一张图片抽象地表示为一个二维矩阵M,M(i, j)代表了M中第i行第j列的像素的值,该像素值被定义为红色、绿色、蓝色(RGB)的值的三元组。在本实验中,我们只考虑正方形的图片。下标遵循C语言风格,从0到N-1。

旋转操作被定义为将图片逆时针旋转90°,旋转可以分为两步实现——

  1. 将矩阵转置,即将M(i, j)变为M(j, i)
  2. 将矩阵的第i行和第N - i - 1交换

平滑操作被定义为将每一个像素点的值换位周围像素点(以该像素点为中心的最多3*3的正方形)的平均值。

实验要求

在本实验中像素(pixel)是一个定义如下的结构体:

1
2
3
4
5
6
The core data structure deals with image representation. A pixel is a struct as shown below:
typedef struct {
unsigned short red; /* R value */
unsigned short green; /* G value */
unsigned short blue; /* B value */
} pixel;

此外,为了简化起见,还定义了如下的宏:

1
#define RIDX(i,j,n) ((i)*(n)+(j))

旋转操作的朴素版本如下:

1
2
3
4
5
6
7
void naive_rotate(int dim, pixel *src, pixel *dst) {
int i, j;
for(i=0; i < dim; i++)
for(j=0; j < dim; j++)
dst[RIDX(dim-1-j,i,dim)] = src[RIDX(i,j,dim)];
return;
}

平滑操作的朴素版本如下(辅助函数未列出):

1
2
3
4
5
6
7
void naive_smooth(int dim, pixel *src, pixel *dst) {
int i, j;
for(i=0; i < dim; i++)
for(j=0; j < dim; j++)
dst[RIDX(i,j,dim)] = avg(dim, i, j, src); /* Smooth the (i,j)th pixel */
return;
}

为了简化起见,你可以假设所有的矩阵的边长都是32的倍数,并且在进行正确性测试的时候,只会测试5个值。

具体的注册函数和自动跑分机制请参阅实验讲义。

实验思路

旋转操作:旋转操作不涉及复杂的计算,经过测试将代码移动所带来的性能提升微乎其微。主要应当采用分块的手段提高代码的空间局部性的利用率。

平滑操作(平滑操作参考了别人的思路):平滑操作是计算密集型函数,经过测试,通过减少过程调用以及代码移动带来的有限的性能提升。
观察原始代码可以发现分支预测的惩罚是巨大的(因为循环只会执行3次,会带来大量的预测不命中)。我们可以考虑将红、绿、蓝三色的值的计算分开地并行处理,并且依次处理四个角、四条边、和剩余的像素点。以提升局部性的利用率并且降低分支预测不命中所带来的惩罚。

实验答案

旋转操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
void block_rotate(int dim, pixel * src, pixel * dst) {
int i, j, k, l;
int cst0 = dim - 1;
for (i = 0 ; i < dim ; i += 8) {
for (j = 0 ; j < dim ; j += 8) {
for (k = i ; k < i + 8 ; k++) {
for (l = j ; l < j + 8 ; l++) {
dst[RIDX(cst0 - l, k, dim)] = src[RIDX(k, l, dim)];
}
}
}
}
}

平滑操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
void optimized_smooth(int dim, pixel * src, pixel * dst) {
int i, j;
int tmp;

dst[0].red = (src[0].red + src[1].red + src[dim].red + src[dim + 1].red) >> 2;
dst[0].green = (src[0].green + src[1].green + src[dim].green + src[dim + 1].green) >> 2;
dst[0].blue = (src[0].blue + src[1].blue + src[dim].blue + src[dim + 1].blue) >> 2;
i = dim - 1;
dst[i].red = (src[i].red + src[i - 1].red + src[i + dim].red + src[i + dim -1].red) >> 2;
dst[i].green = (src[i].green + src[i - 1].green + src[i + dim].green + src[i + dim -1].green) >> 2;
dst[i].blue = (src[i].blue + src[i - 1].blue + src[i + dim].blue + src[i + dim -1].blue) >> 2;
i = i * dim;
dst[i].red = (src[i].red + src[i + 1].red + src[i - dim].red + src[i - dim + 1].red) >> 2;
dst[i].green = (src[i].green + src[i + 1].green + src[i - dim].green + src[i - dim + 1].green) >> 2;
dst[i].blue = (src[i].blue + src[i + 1].blue + src[i - dim].blue + src[i - dim + 1].blue) >> 2;
i = i + dim - 1;
dst[i].red = (src[i].red + src[i - 1].red + src[i - dim].red + src[i - dim - 1].red) >> 2;
dst[i].green = (src[i].green + src[i - 1].green + src[i - dim].green + src[i - dim - 1].green) >> 2;
dst[i].blue = (src[i].blue + src[i - 1].blue + src[i - dim].blue + src[i - dim - 1].blue) >> 2;

for (i = 1 ; i < dim - 1 ; i++) {
dst[i].red = (src[i].red + src[i - 1].red + src[i + 1].red + src[i + dim].red + src[i + dim - 1].red + src[i + dim + 1].red) / 6;
dst[i].green = (src[i].green + src[i - 1].green + src[i + 1].green + src[i + dim].green + src[i + dim - 1].green + src[i + dim + 1].green) / 6;
dst[i].blue = (src[i].blue + src[i - 1].blue + src[i + 1].blue + src[i + dim].blue + src[i + dim - 1].blue + src[i + dim + 1].blue) / 6;
}
for (i = dim * (dim - 1) + 1 ; i < dim * dim - 1 ; i++) {
dst[i].red = (src[i].red + src[i - 1].red + src[i + 1].red + src[i - dim].red + src[i - dim - 1].red + src[i - dim + 1].red) / 6;
dst[i].green = (src[i].green + src[i - 1].green + src[i + 1].green + src[i - dim].green + src[i - dim - 1].green + src[i - dim + 1].green) / 6;
dst[i].blue = (src[i].blue + src[i - 1].blue + src[i + 1].blue+ src[i - dim].blue + src[i - dim - 1].blue+ src[i - dim + 1].blue) / 6;
}
for (j = dim ; j < dim * (dim - 1) ; j+= dim) {
dst[j].red = (src[j].red + src[j + 1].red + src[j - dim].red + src[j - dim + 1].red + src[j + dim].red + src[j + dim + 1].red) / 6;
dst[j].green = (src[j].green + src[j + 1].green + src[j - dim].green+ src[j - dim + 1].green + src[j + dim].green + src[j + dim + 1].green) / 6;
dst[j].blue = (src[j].blue + src[j + 1].blue + src[j - dim].blue + src[j - dim + 1].blue + src[j + dim].blue + src[j + dim + 1].blue) / 6;
}
for (j = 2 * dim - 1 ; j < dim * dim - 1 ; j += dim) {
dst[j].red = (src[j].red + src[j - 1].red + src[j - dim].red + src[j - dim - 1].red + src[j + dim].red + src[j + dim - 1].red) / 6;
dst[j].green = (src[j].green + src[j - 1].green + src[j - dim].green + src[j - dim - 1].green + src[j + dim].green + src[j + dim - 1].green) / 6;
dst[j].blue = (src[j].blue + src[j - 1].blue + src[j - dim].blue + src[j - dim - 1].blue + src[j + dim].blue + src[j + dim - 1].blue) / 6;
}

for (i = 1 ; i < dim - 1 ; i++) {
for (j = 1 ; j < dim - 1 ; j++) {
tmp = i * dim + j;
dst[tmp].red = (src[tmp].red + src[tmp - 1].red + src[tmp + 1].red + src[tmp - dim].red + src[tmp - dim - 1].red + src[tmp - dim + 1].red + src[tmp + dim].red + src[tmp + dim - 1].red + src[tmp + dim + 1].red) / 9;
dst[tmp].green = (src[tmp].green + src[tmp - 1].green + src[tmp + 1].green + src[tmp - dim].green + src[tmp - dim - 1].green + src[tmp - dim + 1].green + src[tmp + dim].green + src[tmp + dim - 1].green + src[tmp + dim + 1].green) / 9;
dst[tmp].blue = (src[tmp].blue + src[tmp - 1].blue + src[tmp + 1].blue + src[tmp - dim].blue + src[tmp - dim - 1].blue + src[tmp - dim + 1].blue + src[tmp + dim].blue + src[tmp + dim - 1].blue + src[tmp + dim + 1].blue) / 9;
}
}
}

实验总结

实验的第一部分对于缓存的要求不是很高,简单的分块可以带来很大的性能提升。

实验的第二部分实际上我是卡了比较久的,在发现减少过程调用和代码移动性能提升有限后我就卡住了,因为我发现似乎无法进行循环展开。实际上还是太生疏了,没有想到要分析性能瓶颈,最后参考了别人的答案,实际上看到了别人答案的一瞬间就豁然开朗了,但是在这之前完全没有想到会是分支预测的问题。还需要多加练习。

评论和共享

实验答案托管在我的GitHub

考完试之后一直比较颓废,本来想看完《深入理解计算机系统》的第5章——优化程序性能之后就赶快来做实验的,后来发现无论是Cache Lab还是Performance Lab都需要第6章——存储器层次结构的知识。看了几天的书,又磨蹭了几天,终于把Cache Lab的Part A写完了,总结如下。

实验简介

Cache Lab - Understanding Cache Memories主要是有关缓存的实验,对应于书本的第6章:存储器层次结构。主要阐明了缓存对于C语言程序的性能影响。

本实验主要分为两个部分。第一个部分要求完成一个C语言程序用来模拟缓存的行为;而第二个部分要求优化一个小的矩阵变换函数,使其具有尽可能小的缓存不命中率。

关于本实验的具体介绍详见实验讲义

实验介绍

实验讲义中的traces子文件夹下包含了一组reference trace files,用来作为评估Part A中缓存模拟器正确性的样例数据。这些文件是由valgrind生成的。

Valgrind是一个用于内存调试,内存泄露以及性能分析的软件。例如,我们输入如下的命令:

1
linux> valgrind --log-fd=1 --tool=lackey -v --trace-mem=yes ls -l

valgrind会在命令行中执行ls -l命令并且按照顺序记录该命令的每一次内存访问,并且将其输出到标准输出流中。

下面是一个valgrind的输出样例:

1
2
3
4
I 0400d7d4,8
M 0421c7f0,4
L 04f6b868,8
S 7ff0005c8,8

每一行的基本格式如下:

1
[空格]操作 地址,大小

操作代表了内存访问的种类,'I'代表的是指令加载,'L'代表的是数据加载,'S'代表的是数据存储,'M'代表的是数据修改(如一条数据加载之后紧跟着数据存储)。在'I'之前永远没有空格,而其他的操作前必定有前置空格。
地址代表的是一个64位的16进制内存地址。
大小指明了该次内存访问涉及的字节数。

实验要求 - Part A

在Part A中你需要在csim.c中实现一个LRU驱逐机制的缓存模拟器,该模拟器接收valgrind的trace作为输入,模拟一个缓存在该情况下的命中/不命中情况,并且输出所有的命中,不命中以及驱逐的次数。

本实验已经提供了一个用来参考的缓存模拟器csim-ref,其使用方式以及输出如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Usage: ./csim-ref [-hv] -s <s> -E <E> -b <b> -t <tracefile>
• -h: Optional help flag that prints usage info
• -v: Optional verbose flag that displays trace info
• -s <s>: Number of set index bits (S = 2 s is the number of sets)
• -E <E>: Associativity (number of lines per set)
• -b <b>: Number of block bits (B = 2 b is the block size)
• -t <tracefile>: Name of the valgrind trace to replay

linux> ./csim-ref -s 4 -E 1 -b 4 -t traces/yi.trace
hits:4 misses:5 evictions:3

linux> ./csim-ref -v -s 4 -E 1 -b 4 -t traces/yi.trace
L 10,1 miss
M 20,1 miss hit
L 22,1 hit
S 18,1 hit
L 110,1 miss eviction
L 210,1 miss eviction
M 12,1 miss eviction hit
hits:4 misses:5 evictions:3

对于Part A有以下的要求和限制:

  1. 所写的程序编译时不能有任何的警告
  2. 能应对不同s,E和b的缓存生成对应的结果,这要求使用malloc来为你的数据结构分配空间
  3. 不需要考虑指令加载的缓存情况
  4. 调用printSummary打印最终的结果
  5. 假定数据已经适当对齐,不会出现一次数据加载跨区块的情况。这样,你可以忽略trace中的大小

除此以外,实验的自学者讲义中还包含了一些提示,这里就不再赘述。

完成了Part A之后,可以用make clean && make进行编译,并且用./test-csim进行自动评分,总共有8组样例,共27分满分。

实验过程 - Part A

实验思路

整个模拟器在思路上可以进行如下的拆分:

1.命令行参数解析

由于该模拟器是命令行程序并且接受命令行参数,因此需要能对命令行参数进行解析和处理,这里推荐使用getopt进行参数的解析。并且根据不同的参数执行不同的控制流,并且处理一些基本的错误如参数缺失以及参数类型错误。

2.数据结构

这是整个模拟器最基本的部分。我们需要创建合适的数据结构来模拟缓存,该数据结构不仅要能模拟缓存的数据的实际组织方式(有效位,标志位以及行和块等等),还需要考虑到LRU的驱逐机制。

3.文件中内存访问记录的处理和解析

这个是模拟器中核心的部分。模拟器的功能就是从文件中接收内存访问记录,并且根据这些记录来模拟缓存的行为,操作我们所设计的缓存的数据结构。

实验代码

该代码仅供参考,很可能并不是一个非常好的解法。如果你也在面对同样的实验,请不要抄袭,毕竟抄袭是不会使你变得更强的:)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
/* include necessary headers */
#include "cachelab.h"

#include "getopt.h"
#include "stdlib.h"
#include "unistd.h"

#include "string.h"

#include "stdio.h"

#include "stdint.h"

/* cache struct definition */
typedef struct CSim_Cache_Entry {
char valid_bit;
uint64_t tag_bit;
}CSim_Cache_Entry;

typedef struct CSim_Cache_Set{
CSim_Cache_Entry * entries;
}CSim_Cache_Set;

typedef struct CSim_Cache {
/* basic arguments */
int block_offset;
int set_number;
int line_number;
/* masks and offsets */
int tag_offset;
int set_offset;
uint64_t tag_mask;
uint64_t set_mask;
/* cache */
CSim_Cache_Set * sets;
}CSim_Cache;

/* simulation result definition */
typedef struct CSim_Cache_Result {
int hit;
int miss;
int evict;
}CSim_Cache_Result;

/* operation type definition */
typedef enum CSIM_OPERATION_TYPE {
CSIM_OPERATION_TYPE_NONE,
CSIM_OPERATION_TYPE_MODIFY,
CSIM_OPERATION_TYPE_LOAD,
CSIM_OPERATION_TYPE_STORE,
}CSIM_OPERATION_TYPE;

/* operation result definition */
typedef enum CSIM_OPERATION_RESULT {
CSIM_OPERATION_RESULT_MISS,
CSIM_OPERATION_RESULT_HIT,
CSIM_OPERATION_RESULT_MISS_EVICTION,
}CSIM_OPERATION_RESULT;

/* error definition */
typedef enum CSIM_ERROR {
CSIM_OK = 0,
CSIM_ERROR_INVALID_OPTION,
CSIM_ERROR_MISSING_ARGUMENT,
CSIM_ERROR_FILE_CANNOT_OPEN,
CSIM_ERROR_OUT_OF_MEMORY,
}CSIM_ERROR;

/* macro definition for get a value given mask and offset */
#define csim_get_value(number, mask, offset) (((number) & (mask)) >> (offset))

/* function list */
/* message print functions */
void csim_print_help_info();
void csim_error_missing_argument();
void csim_error_file_cannot_open();
void csim_error_out_of_memory();
/* cache structure related functions */
CSim_Cache * csim_construct_cache(int set_number, int line_number, int block_offset);
void csim_deconstruct_cache(CSim_Cache ** pcache);
/* cache simulation function */
CSim_Cache_Result csim_parse_trace_file(CSim_Cache * cache, FILE * file_pointer, char verbose_flag);

/* global variable */
char * program_name = NULL;

void csim_print_help_info() {
printf("Usage: ./csim [-hv] -s <num> -E <num> -b <num> -t <file>\n");
printf("Options:\n");
printf(" -h Print this help message.\n");
printf(" -v Optional verbose flag.\n");
printf(" -s <num> Number of set index bits.\n");
printf(" -E <num> Number of lines per set.\n");
printf(" -b <num> Number of block offset bits.\n");
printf(" -t <file> Trace file.\n\n");
printf("Examples:\n");
printf(" linux> ./csim -s 4 -E 1 -b 4 -t traces/yi.trace\n");
printf(" linux> ./csim -v -s 8 -E 2 -b 4 -t traces/yi.trace\n");
}

void csim_error_missing_argument() {
printf("%s: Missing required command line argument\n", program_name);
csim_print_help_info();
}

void csim_error_file_cannot_open() {
printf("%s: No such file or directory\n", program_name);
}

void csim_error_out_of_memory() {
printf("%s: Out of memory\n", program_name);
}

CSim_Cache * csim_construct_cache(int set_number, int line_number, int block_offset) {
/* memory allocation */
CSim_Cache * cache = malloc(sizeof(CSim_Cache));
if (cache == NULL) {
csim_error_out_of_memory();
exit(CSIM_ERROR_OUT_OF_MEMORY);
}
/* configure basic arguments */
cache->set_number = set_number;
cache->line_number = line_number;
cache->block_offset = block_offset;
/* caculate masks and offsets */
cache->tag_mask = ((uint64_t)0xFFFFFFFFFFFFFFFF) << (block_offset + set_number);
cache->tag_offset = block_offset + set_number;
cache->set_mask = ((((((uint64_t)0xFFFFFFFFFFFFFFFF) << (64 - cache->tag_offset)) >> (64 - cache->tag_offset)) >> block_offset) << block_offset);
cache->set_offset = block_offset;
/* memory allocation for cache */
cache->sets = calloc((1 << set_number), sizeof(CSim_Cache_Set));
if (cache->sets == NULL) {
csim_error_out_of_memory();
exit(CSIM_ERROR_OUT_OF_MEMORY);
}
for (int index = 0 ; index < (1 << set_number) ; ++index) {
(cache->sets)[index].entries = calloc(line_number, sizeof(CSim_Cache_Entry));
if ((cache->sets)[index].entries == NULL) {
csim_error_out_of_memory();
exit(CSIM_ERROR_OUT_OF_MEMORY);
}
}
return cache;
}

void csim_deconstruct_cache(CSim_Cache ** pcache) {
/* release cache according to construct function */
CSim_Cache * temp = *pcache;
int set_number = temp->set_number;
for (int index = 0 ; index < set_number ; ++index) {
free((temp->sets)[index].entries);
}
free(temp->sets);
free(temp);
*pcache = NULL;
}

CSim_Cache_Result csim_parse_trace_file(CSim_Cache * cache, FILE * file_pointer, char verbose_flag) {
/* file I/O related */
char line[80];
char * line_pointer = NULL;
/* cache type and cache result */
CSIM_OPERATION_TYPE type;
CSIM_OPERATION_RESULT result;
/* address and set index & tag bis from it */
int address = 0;
int set = 0, tag = 0;
/* line number */
int line_number = cache->line_number;
/* simulation result */
CSim_Cache_Result summary = {0, 0, 0};
/* parsing process */
while (fgets(line, 80, file_pointer), feof(file_pointer) == 0) {
line_pointer = line;
type = CSIM_OPERATION_TYPE_NONE;
/* exclude instrction load */
if (*line_pointer++ == ' ') {
switch(*line_pointer++) {
case 'L':
type = CSIM_OPERATION_TYPE_LOAD;
if (verbose_flag) {
putchar('L');
}
break;
case 'S':
type = CSIM_OPERATION_TYPE_STORE;
if (verbose_flag) {
putchar('S');
}
break;
case 'M':
type = CSIM_OPERATION_TYPE_MODIFY;
if (verbose_flag) {
putchar('M');
}
break;
default:
type = CSIM_OPERATION_TYPE_NONE;
break;
}
result = CSIM_OPERATION_RESULT_MISS_EVICTION;
sscanf(line_pointer, "%x", &address);
if (verbose_flag) {
while ((*line_pointer) != '\n') {
putchar(*line_pointer++);
}
}
/* separate set and tag according to mask and offset */
set = csim_get_value(address, cache->set_mask, cache->set_offset);
tag = csim_get_value(address, cache->tag_mask, cache->tag_offset);
/* determine the cache result */
CSim_Cache_Entry * pentry = (cache->sets)[set].entries;
int index;
CSim_Cache_Entry temp;
for (index = 0; index < line_number ; ++index) {
if (!pentry[index].valid_bit) {
result = CSIM_OPERATION_RESULT_MISS;
break;
}
if (pentry[index].tag_bit == tag) {
result = CSIM_OPERATION_RESULT_HIT;
break;
}
}
/* simulate according to the result of cache behavior */
switch(result) {
case CSIM_OPERATION_RESULT_MISS:
if (verbose_flag) {
printf(" miss");
}
pentry[index].valid_bit = 1;
pentry[index].tag_bit = tag;
summary.miss++;
break;
case CSIM_OPERATION_RESULT_MISS_EVICTION:
if (verbose_flag) {
printf(" miss eviction");
}
pentry[0].tag_bit = tag;
temp = pentry[0];
for (index = 0 ; index < line_number - 1 ; ++index) {
pentry[index] = pentry[index + 1];
}
pentry[index] = temp;
summary.miss++;
summary.evict++;
break;
case CSIM_OPERATION_RESULT_HIT:
if (verbose_flag) {
printf(" hit");
}
temp = pentry[index];
for ( ; (index < line_number - 1) && (pentry[index + 1].valid_bit) ; ++index) {
pentry[index] = pentry[index + 1];
}
pentry[index] = temp;
summary.hit++;
break;
}
if (type == CSIM_OPERATION_TYPE_MODIFY) {
if (verbose_flag) {
printf(" hit");
}
summary.hit++;
}
if (verbose_flag) {
putchar('\n');
}
}
}
return summary;
}

int main(int argc, char *argv[]) {
/* variables for argument parsing */
char h = 0, v = 0, s = 0, E = 0, b = 0, t = 0;
int opt;
/* cache arguments */
int set_number = 0, line_number = 0, block_offset = 0;
/* cache */
CSim_Cache * cache = NULL;
/* file path */
char file_path[80];
/* verbose flag */
char verbose_flag = 0;
/* summary */
CSim_Cache_Result summary = {0, 0, 0};
/* pre-operation */
program_name = argv[0];
/* argument parsing */
while ((opt = getopt(argc, argv, "hvs:E:b:t:")) != -1) {
switch(opt) {
case 'h':
h = 1;
break;
case 'v':
v = 1;
verbose_flag = 1;
break;
case 's':
set_number = atoi(optarg);
if (set_number == 0) {
csim_error_missing_argument();
return CSIM_ERROR_MISSING_ARGUMENT;
}
s = 1;
break;
case 'E':
line_number = atoi(optarg);
if (line_number == 0) {
csim_error_missing_argument();
return CSIM_ERROR_MISSING_ARGUMENT;
}
E = 1;
break;
case 'b':
block_offset = atoi(optarg);
if (block_offset == 0) {
csim_error_missing_argument();
return CSIM_ERROR_MISSING_ARGUMENT;
}
b = 1;
break;
case 't':
t = 1;
strcpy(file_path, optarg);
break;
default:
csim_print_help_info();
return CSIM_ERROR_INVALID_OPTION;
break;
}
}
if (h == 1) {
csim_print_help_info();
return CSIM_OK;
}
if (v == 1) {
verbose_flag = 1;
}
if (!(s == 1 && E == 1 && b == 1 && t == 1)) {
csim_error_missing_argument();
return CSIM_ERROR_MISSING_ARGUMENT;
}
/* file processing */
FILE * file_pointer = fopen(file_path, "r");
if (file_pointer == NULL) {
csim_error_file_cannot_open(file_path);
return CSIM_ERROR_FILE_CANNOT_OPEN;
}
/* cache construction */
cache = csim_construct_cache(set_number, line_number, block_offset);
/* trace file parsing */
summary = csim_parse_trace_file(cache, file_pointer, verbose_flag);
/* summary */
printSummary(summary.hit, summary.miss, summary.evict);
/* post operations */
csim_deconstruct_cache(&cache);
fclose(file_pointer);
return CSIM_OK;
}

实验结果

自动评分脚本给出的输出如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
user@blackdragon ~/C/C/cachelab-handout> ./test-csim
Your simulator Reference simulator
Points (s,E,b) Hits Misses Evicts Hits Misses Evicts
3 (1,1,1) 9 8 6 9 8 6 traces/yi2.trace
3 (4,2,4) 4 5 2 4 5 2 traces/yi.trace
3 (2,1,4) 2 3 1 2 3 1 traces/dave.trace
3 (2,1,3) 167 71 67 167 71 67 traces/trans.trace
3 (2,2,3) 201 37 29 201 37 29 traces/trans.trace
3 (2,4,3) 212 26 10 212 26 10 traces/trans.trace
3 (5,1,5) 231 7 0 231 7 0 traces/trans.trace
6 (5,1,5) 265189 21775 21743 265189 21775 21743 traces/long.trace
27

TEST_CSIM_RESULTS=27

实验总结

由于在寒假的课程设计中已经使用过了命令行参数解析以及复杂的数据结构设计与实验,Part A总体上来说不是很难,我主要的精力花费在代码风格上,希望代码能尽量具有良好的架构以及可读性。

本部分中犯的错误有,直接根据s设置缓存的组数,而实际上的组数是2^s个。循环中由于手误导致出现死循环。没有搞清楚inline static关键字而导致的编译错误等等。存在知识的盲区也存在着粗心导致的错误,以后还需要更加注意。

评论和共享

近期将主力环境从macOS切换至Arch Linux,然后将博客从原环境迁移至现环境,本来以为过程会比较简单,结果却还是踩了几个坑,现在将配置过程中遇到的问题记录并且总结如下。

在Arch Linux中安装Git和Node.js

根据Hexo官方文档的说明,安装Hexo需要的依赖为Git和Node.js。
Git的安装比较简单,使用Arch Linux的默认包管理器pacman即可安装:

1
$ pacman -S git

Node.js官方文档推荐使用nvm安装,并且官方文档给出了安装脚本。
但是我的电脑使用的是伪fish——参考Arch Wiki,我通过的是通过给terminal emulator添加参数的方式进入fish的。
因此,我的默认shell仍然是bash。而该安装脚本在检测到我的默认shell之后直接将相关的导出环境变量的命令添加至了.bashrc,导致了进入fish之后由于相关的环境变量丢失无法找到nvm命令的问题。

  1. 第一种解决方法是采用Node.js的fish版安装脚本,该脚本托管在Github上,但是该脚本目前已不再被MAINTAINED,需要自行承担风险。
  2. 第二种解决方法是采用bass包装原始的nvm。

对于第二种解决方法,我们首先使用官方文档的安装脚本安装nvm:

1
$ wget -qO- https://raw.github.com/creationix/nvm/master/install.sh | sh

然后我们按照Github上的教程手动安装bass:
1
2
3
$ git clone https://github.com/edc/bass.git
$ cd bass
$ make install

最后我们建立如下的文件并重启终端,即可正常地在fish中使用nvm:
1
2
3
4
$ cat ~/.config/fish/functions/nvm.fish
function nvm
bass source ~/.nvm/nvm.sh --no-use ';' nvm $argv
end

使用nvm安装Node.js最新版本的命令如下:
1
$ nvm install v8.1.3

最后我们安装hexo:
1
$ npm install -g hexo-cli

hexo server报EMFILE错误

报错内容如下:

1
Error: EMFILE, too many open files

Hexo官网的问题解答中给出了如下描述:
虽然 Node.js 有非阻塞 I/O,同步 I/O 的数量仍被系统所限制,在生成大量静态文件的时候,您可能会碰到 EMFILE 错误,您可以尝试提高同步 I/O 的限制数量来解决此问题。
1
$ ulimit -n 10000

但是当我尝试使用ulimit的时候,却报了如下的错误:
1
Permission denied when changing resource of type 'Maximum number of open file descriptors'

在这之后进行了如下的尝试:

  1. 使用sudo无法运行ulimit,后su至root运行,虽然没有报错,但是使用ulimit -n查看发现修改失败。
  2. 尝试添加参数-S和-H,没有效果。
  3. 最后根据Arch Forum上的回答,修改了/etc/security/limits.conf文件并重启电脑,问题解决。

hexo server报ENOSPC错误

Hexo官网的问题解答中给出了如下描述:
它可以用过运行 $ npm dedupe 来解决,如果不起作用的话,可以尝试在 Linux 终端中运行下列命令:

1
$ echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p

这将会提高你能监视的文件数量。
$ npm dedupe没有解决问题,最后使用上述的命令解决了问题。(注意在fish中,'&&'要用'COMMAND; and COMMAND'来替代)

评论和共享

  • 第 1 页 共 1 页
作者的图片

码龙黑曜

iOS开发者/计算机科学/兽人控


华中科技大学 本科在读


Wuhan, China