2018q3 Malloc Lab
===
contributed by < `Jyun-Neng`, `LiuJuiHung` >
## Writing a Dynamic Storage Allocator for C Programs
分配器(Allocator)有兩種:
* 顯式分配器(Explicit allocator): 需要自行分配及釋放空間,例如:C語言中的 `malloc` 及 `free`。
* 隱式分配器(Implicit allocator): 僅需自行分配,當被分配的空間被檢測到不再使用時會自動釋放,例如:Java的垃圾收集(garbage collection)。
[Malloc Lab](http://csapp.cs.cmu.edu/3e/malloclab.pdf) 目的為實作出正確、有效率、快速的 `malloc free realloc`。
在 `mm.c mm.h` 中提供四種函式:
* `int mm_init(void)`
* `void *mm_malloc(size_t size)`
* `void mm_free(void *ptr)`
* `void *mm_realloc(void *ptr, size_t size)`
## 實現一個簡單的分配器(Allocator)
我們參照 CS:APP 的範例來實作,假設記憶體為 double-word(8-byte) 對齊。
### Heap
在實作中我們採用的 boundary tag 的 Heap 格式如下圖所示:
![](https://i.imgur.com/kBXQUVQ.png)
* Boundary tag: Heap 除了有 header 記錄此 Heap 中 block 的大小及是否被分配外,另外於 heap 尾端複製了 header 的資料 -- footer。==此方法允許常數時間前後 block 合併。==
### 定義巨集
```clike
#define WSIZE 4
#define DSIZE 8
#define CHUNKSIZE (1 << 12)
#define MAX(x, y) ((x) > (y) ? (x) : (y))
// Pack a size and allocated bit into a word
#define PACK(size, alloc) ((size) | (alloc))
// Read and write a word at address p
#define GET(p) (*(unsigned int *)(p))
#define PUT(p, val) (*(unsigned int *)(p) = (val))
// Read the size and allocation bit from address p
#define GET_SIZE(p) (GET(p) & ~0x7)
#define GET_ALLOC(p) (GET(p) & 0x1)
// Address of block's header and footer
#define HDRP(ptr) ((char *)(ptr) - WSIZE)
#define FTRP(ptr) ((char *)(ptr) + GET_SIZE(HDRP(ptr)) - DSIZE)
// Address of (physically) next and previous blocks
#define NEXT_BLKP(ptr) ((char *)(ptr) + GET_SIZE(((char *)(ptr) - WSIZE)))
#define PREV_BLKP(ptr) ((char *)(ptr) - GET_SIZE(((char *)(ptr) - DSIZE)))
```
### `mm_init` 函式實作
```clike=
int mm_init(void) {
/* Create the initial empty heap */
if ((heap_listp = mem_sbrk(4 * WSIZE)) == (void *)-1)
return -1;
PUT(heap_listp, 0); /* Alignment padding */
PUT(heap_listp + (1 * WSIZE), PACK(DSIZE, 1)); /* Prologue header */
PUT(heap_listp + (2 * WSIZE), PACK(DSIZE, 1)); /* Prologue footer */
PUT(heap_listp + (3 * WSIZE), PACK(0, 1)); /* Epilogue header */
heap_listp += (2 * WSIZE);
/* Extend the empty heap with a free block of CHUNKSIZE bytes */
if (extend_heap(CHUNKSIZE / WSIZE) == NULL)
return -1;
return 0;
}
```
```clike=
static void *extend_heap(size_t words) {
char *bp;
size_t size;
// Allocate an even number rds to maintain alignment
size = (words % 2) ? (words + 1) * WSIZE : words * WSIZE;
if ((long)(bp = mem_sbrk(size)) == -1)
return NULL;
PUT(HDRP(bp), PACK(size, 0));
PUT(FTRP(bp), PACK(size, 0));
PUT(HDRP(NEXT_BLKP(bp)), PACK(0, 1));
return coalesce(bp);
}
```
產生新的 heap 後,我們需要判斷其前後 heap 是否被分配,進而進行合併。
```clike=
static void *coalesce(void *bp) {
size_t prev_alloc = GET_ALLOC(FTRP(PREV_BLKP(bp)));
size_t next_alloc = GET_ALLOC(HDRP(NEXT_BLKP(bp)));
size_t size = GET_SIZE(HDRP(bp));
if (prev_alloc && next_alloc)
return bp;
else if (prev_alloc && !next_alloc) {
size += GET_SIZE(HDRP(NEXT_BLKP(bp)));
PUT(HDRP(bp), PACK(size, 0));
PUT(FTRP(bp), PACK(size, 0));
} else if (!prev_alloc && next_alloc) {
size += GET_SIZE(HDRP(PREV_BLKP(bp)));
PUT(FTRP(bp), PACK(size, 0));
PUT(HDRP(PREV_BLKP(bp)), PACK(size, 0));
bp = PREV_BLKP(bp);
} else {
size += GET_SIZE(HDRP(PREV_BLKP(bp))) + GET_SIZE(FTRP(NEXT_BLKP(bp)));
PUT(HDRP(PREV_BLKP(bp)), PACK(size, 0));
PUT(FTRP(NEXT_BLKP(bp)), PACK(size, 0));
bp = PREV_BLKP(bp);
}
return bp;
}
```
### `mm_malloc` 函式實作
```clike=
void *mm_malloc(size_t size) {
size_t asize;
size_t extendsize;
char *bp;
if (heap_listp == 0)
mm_init();
if (size == 0)
return NULL;
if (size <= DSIZE)
asize = 2 * DSIZE;
else
asize = DSIZE * ((size + (DSIZE) + (DSIZE - 1)) / DSIZE);
if ((bp = find_fit(asize)) != NULL) {
place(bp, asize);
return bp;
}
extendsize = MAX(asize, CHUNKSIZE);
if ((bp = extend_heap(extendsize / WSIZE)) == NULL)
return NULL;
place(bp, asize);
return bp;
}
```
```clike=
static void *find_fit(size_t asize) {
void *bp;
for (bp = heap_listp; GET_SIZE(HDRP(bp)) > 0; bp = NEXT_BLKP(bp)) {
if (!GET_ALLOC(HDRP(bp)) && (asize <= GET_SIZE(HDRP(bp))))
return bp;
}
return NULL;
}
```
```clike=
static void place(void *bp, size_t asize) {
size_t csize = GET_SIZE(HDRP(bp));
if ((csize - asize) >= (2 * DSIZE)) {
PUT(HDRP(bp), PACK(asize, 1));
PUT(FTRP(bp), PACK(asize, 1));
bp = NEXT_BLKP(bp);
PUT(HDRP(bp), PACK(csize - asize, 0));
PUT(FTRP(bp), PACK(csize - asize, 0));
} else {
PUT(HDRP(bp), PACK(csize, 1));
PUT(FTRP(bp), PACK(csize, 1));
}
}
```
### `mm_free` 函式實作
```clike=
void mm_free(void *ptr) {
if (ptr <= 0)
return;
size_t size = GET_SIZE(HDRP(ptr));
if (heap_listp == 0)
mm_init();
PUT(HDRP(ptr), PACK(size, 0));
PUT(FTRP(ptr), PACK(size, 0));
coalesce(ptr);
}
```
### `mm_realloc` 函式實作
```clike=
void *mm_realloc(void *ptr, size_t size) {
void *newptr;
size_t copySize;
if (size == 0) {
mm_free(ptr);
return 0;
}
if (ptr == NULL)
return mm_malloc(size);
newptr = mm_malloc(size);
if (!newptr)
return NULL;
copySize = GET_SIZE(HDRP(ptr));
if (size < copySize)
copySize = size;
memcpy(newptr, ptr, copySize);
mm_free(ptr);
return newptr;
}
```
### 實驗結果
```shell
$./mdriver -V
...
Testing mm malloc
Reading tracefile: short1-bal.rep
Checking mm_malloc for correctness, efficiency, and performance.
Reading tracefile: short2-bal.rep
Checking mm_malloc for correctness, efficiency, and performance.
Results for mm malloc:
trace valid util ops secs Kops
0 yes 66% 12 0.000001 17143
1 yes 89% 12 0.000001 20000
Total 78% 24 0.000001 18462
Perf index = 47 (util) + 40 (thru) = 87/100
```