Malloc Lab
2024. 4. 28. 17:09ㆍ크래프톤 정글 5기/공부
묵시적 가용 리스트 + First_fit
묵시적 가용 리스트 + Next_fit
묵시적 가용 리스트 + Best_fit
전체 코드 (Github)
1. Macro
#define WSIZE 4 // word & header/footer size 4 bytes
#define DSIZE 8 // double word size 8 bytes
#define CHUNKSIZE (1<<12) // extend heap by this amout
#define MIN(x,y) ((x) < (y) ? (x) : (y))
#define MAX(x, y) ((x) > (y) ? (x) : (y))
#define PACK(size, alloc) ((size) | (alloc)) // pack a size and allocated bit into a word
#define GET(p) (*(unsigned int *)(p)) // read a word at address p
#define PUT(p, val) (*(unsigned int *)(p) = (val)) // write a word at address p
#define GET_SIZE(p) (GET(p) & ~0x7) // read the size from address p
#define GET_ALLOC(p) (GET(p) & 0x1) // allocated fields from address p
#define HDRP(bp) ((char *)(bp) - WSIZE) // compute address of its header
#define FTRP(bp) ((char *)(bp) + GET_SIZE(HDRP(bp)) - DSIZE) // compute address of its footer
#define NEXT_BLKP(bp) ((char *)(bp) + GET_SIZE(((char *)(bp) - WSIZE))) // compute address of next block
#define PREV_BLKP(bp) ((char *)(bp) - GET_SIZE(((char *)(bp) - DSIZE))) // compute address of previous block
2. mm_init
int mm_init(void)
{
if ((heap_listp = mem_sbrk(4*WSIZE)) == (void *) -1)
{
return -1;
}
PUT(heap_listp, 0); // 힙 구분용 첫 블록
PUT(heap_listp + (1*WSIZE), PACK(DSIZE, 1)); // 프롤로그 블록 2개중 Header block
PUT(heap_listp + (2*WSIZE), PACK(DSIZE, 1)); // 프롤로그 블록 2개중 Footer block
PUT(heap_listp + (3*WSIZE), PACK(0, 1)); // 힙 구분용 마지막 블록
heap_listp += (2*WSIZE); // 포인터를 2칸 움직이게 해서 Header block 다음에 위치시킴
lastp = heap_listp; // next_fit 방식 사용 시 포인터를 항상 동기화 시켜야 함
if (extend_heap(CHUNKSIZE/WSIZE) == NULL) // 더 이상 영역 확장이 불가능하면
{
return -1; // 종료
}
return 0; // 리턴 값 없는 초기 함수
}
3. mm_malloc
void *mm_malloc(size_t size)
{
size_t asize; // 할당을 위해 조정할 사이즈
size_t extendsize;
char *bp;
if (size == 0) // 할당하려는 크기가 0이면
{
return NULL;
}
if (size <= DSIZE) // 할당하려는 크기가 8 bytes보다 적다면 (Header, Footer block)
{
asize = 2 * DSIZE; // 조정할 사이즈를 4 word (최소 블록 사이즈, 1 + 2 + 1)로 설정
}
else // 할당하려는 크기가 8 bytes보다 크다면
{
asize = DSIZE * ((size + (DSIZE) + (DSIZE-1)) / DSIZE); // Padding 고려하여 사이즈 설정
}
if ((bp = find_fit(asize)) != NULL) // 할당하려는 사이즈를 할당할 위치를 찾았다면
{
place(bp, asize); // 할당
lastp = bp; // next_fit 사용을 위해 포인터 동기화
return bp;
}
// 할당하려는 사이즈를 할당할 위치를 찾지 못했다면
extendsize = MAX(asize, CHUNKSIZE); // CHUNKSIZE는 최대 힙 크기
if ((bp = extend_heap(extendsize/WSIZE)) == NULL) // 더 이상 확장이 불가능하면
{
return NULL;
}
// 힙 확장 이후
place(bp, asize); // 할당
lastp = bp; // 포인터 동기화
return bp;
}
4. mm_free
void mm_free(void *ptr) // 할당 해제
{
size_t size = GET_SIZE(HDRP(ptr)); // 현재 블록 사이즈
PUT(HDRP(ptr), PACK(size, 0)); // 현재 블록의 Header block에, Free block의 상태로 기입
PUT(FTRP(ptr), PACK(size, 0)); // 현재 블록의 Footer block에, Free block의 상태로 기입
coalesce(ptr); // 현재 block은 Free block이 되었으니, 병합함수 호출
}
5. mm_realloc
void *mm_realloc(void *ptr, size_t size) // 재할당
{
if (ptr == NULL) // 입력 포인터가 NULL이면, 입력 사이즈만큼 새롭게 할당 (예외처리)
{
return mm_malloc(size);
}
if (size == 0) // 입력 사이즈가 0이면, 입력 포인터의 블록을 해제 (예외처리)
{
mm_free(ptr);
return NULL;
}
void *oldptr = ptr;
void *newptr;
size_t copySize = GET_SIZE(HDRP(oldptr)); // 재할당하려는 블록의 사이즈
if (size + DSIZE <= copySize) // (재할당 하려는 블록 사이즈 + 8 bytes(Header + Footer)) <= 현재 블록 사이즈
{
return oldptr; // 현재 블록에 재할당해도 문제 없기 때문에, 포인터만 반환
}
else // (재할당 하려는 블록 사이즈 + 8 bytes) > 현재 블록 사이즈
// 경우에 따라서 인접 Free block을 활용하는 방안과, 새롭게 할당하는 방안을 이용해야 함
{
size_t next_size = copySize + GET_SIZE(HDRP(NEXT_BLKP(oldptr))); // 현재 블록 사이즈 + 다음 블록 사이즈 = next_size
if (!GET_ALLOC(HDRP(NEXT_BLKP(oldptr))) && (size + DSIZE <= next_size))
// 다음 블록이 Free block이고, (재할당 하려는 블록의 사이즈 + 8 bytes) <= (현재 블록 사이즈 + 다음 블록 사이즈)
// 현재 블록과 다음 블록을 하나의 블록으로 취급해도 크기의 문제가 발생하지 않음
// malloc을 하지 않아도 됨 -> 메모리 공간 및 시간적 이득을 얻을 수 있음
{
PUT(HDRP(oldptr), PACK(next_size, 1)); // 현재 블록의 Header Block에, (현재 블록 사이즈 + 다음 블록 사이즈) 크기와 Allocated 상태 기입
PUT(FTRP(oldptr), PACK(next_size, 1)); // 현재 블록의 Footer Block에, (현재 블록 사이즈 + 다음 블록 사이즈) 크기와 Allocated 상태 기입
lastp = oldptr; // next_fit 사용을 위한 포인터 동기화
return oldptr;
}
// else if (!GET_ALLOC(HDRP(PREV_BLKP(oldptr))) && ())
else // 위 케이스에 모두 해당되지 않아, 결국 malloc을 해야 하는 경우
{
newptr = mm_malloc(size + DSIZE); // (할당하려는 크기 + 8 bytes)만큼 새롭게 할당
if (newptr == NULL) // 새로 할당한 주소가 NULL일 경우 (예외처리)
{
return NULL;
}
memmove(newptr, oldptr, size + DSIZE); // payload 복사
lastp = newptr; // next_fit 사용을 위한 포인터 동기화
mm_free(oldptr); // 기존의 블록은 Free block으로 바꾼다
return newptr; // 새롭게 할당된 주소의 포인터를 반환
}
}
}
6. place
static void place(void *bp, size_t asize) // 배치 및 분할
{
size_t csize = GET_SIZE(HDRP(bp)); // 입력 포인터가 위치한 블록 사이즈 설정
if ((csize - asize) >= (2 * DSIZE)) // (블록 사이즈 - 조정할 사이즈)가 16 bytes보다 같거나 클 경우
// 할당 하고도 공간이 남으니, 분할 작업이 이루어진다
{
PUT(HDRP(bp), PACK(asize, 1)); // 입력 포인터가 위치한 블록의 Header block에 [조정할 사이즈, Allocated] 상태 기입
PUT(FTRP(bp), PACK(asize, 1)); // 입력 포인터가 위치한 블록의 Footer block에 [조정할 사이즈, Allocated] 상태 기입
bp = NEXT_BLKP(bp); // 입력 포인터가 위치한 블록의 다음 블록으로 포인터 변경
PUT(HDRP(bp), PACK(csize - asize, 0)); // 입력 포인터가 위치한 블록의 Header block에 [블록 사이즈 - 조정할 사이즈, Free] 상태 기입
PUT(FTRP(bp), PACK(csize - asize, 0)); // 입력 포인터가 위치한 블록의 Footer block에 [블록 사이즈 - 조정할 사이즈, Free] 상태 기입
}
else // (블록 사이즈 - 조정할 사이즈)가 16 bytes보다 작을 경우
// 분할작업 X
{
PUT(HDRP(bp), PACK(csize, 1)); // 입력 포인터가 위치한 블록의 Header block에 [블록 사이즈 - 조정할 사이즈, Allocated] 상태 기입
PUT(FTRP(bp), PACK(csize, 1)); // 입력 포인터가 위치한 블록의 Footer block에 [블록 사이즈 - 조정할 사이즈, Allocated] 상태 기입
}
}
7. extend_heap
static void *extend_heap(size_t words) // 최대 힙 영역을 넘어가지 않는 안에서, 영역 확장
{
char *bp;
size_t size;
// 블록 사이즈가 짝수면 그대로, 홀수면 패딩을 위해 1 워드 추가
size = (words % 2) ? (words + 1) * WSIZE : words * WSIZE;
if ((long)(bp = mem_sbrk(size)) == -1)
{
return NULL;
}
PUT(HDRP(bp), PACK(size, 0)); // 현재 블록의 Header block 위치에 [사이즈, 0(FREE)] 입력
PUT(FTRP(bp), PACK(size, 0)); // 현재 블록의 Footer block 위치에 [사이즈, 0(FREE)] 입력
// 에필로그 블록 헤더 설정
PUT(HDRP(NEXT_BLKP(bp)), PACK(0, 1)); // 다음 블록의 Header block 위치에 [0, 1(allocated)] 입력
return coalesce(bp); // Free block 병합하는 함수 호출
// Free block이 새로 생성되었으니까
}
8. coalesce
static void *coalesce (void *bp) // 분할되어있는 Free block을 병합하여 1개의 block으로 취급하기 위해
{
// 코드 작성 편리를 위해 변수 설정
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) // case1 : 이전, 다음 블록 allocated
{
return bp; // 병합이 불가능하니, 현재 포인터만 반환
}
else if (prev_alloc && !next_alloc) // case2 : 이전 블록 allocated, 다음 블록 free
{
size += GET_SIZE(HDRP(NEXT_BLKP(bp))); // 다음 블록의 사이즈만큼 더해줌
PUT(HDRP(bp), PACK(size, 0)); // 현재 블록의 Header block에, Free block의 상태로 기입
PUT(FTRP(bp), PACK(size, 0)); // 현재 블록의 Footer block에, Free block의 상태로 기입
}
else if (!prev_alloc && next_alloc) // case3 : 이전 블록 free, 다음 블록 allocated
{
size += GET_SIZE(HDRP(PREV_BLKP(bp))); // 이전 블록 사이즈만큼 더해줌
PUT(FTRP(bp), PACK(size, 0)); // 현재 블록의 Footer block에, Free block의 상태로 기입
PUT(HDRP(PREV_BLKP(bp)), PACK(size, 0)); // 이전 블록의 Header block에, Free block의 상태로 기입
bp = PREV_BLKP(bp); // 현재 블록을 가리키는 포인터를, 이전 블록을 가리키는 포인터로 업데이트
}
else // case4 : 이전 블록 free, 다음 블록 free
{
size += GET_SIZE(HDRP(PREV_BLKP(bp))) + GET_SIZE(FTRP(NEXT_BLKP(bp))); // 이전 + 다음 블록 사이즈만큼 더해줌
PUT(HDRP(PREV_BLKP(bp)), PACK(size, 0)); // 현재 블록의 Header block에, Free block의 상태로 기입
PUT(FTRP(NEXT_BLKP(bp)), PACK(size, 0)); // 다음 블록의 Footer block에, Free block의 상태로 기입
bp = PREV_BLKP(bp); // 현재 블록을 가리키는 포인터를, 이전 블록을 가리키는 포인터로 업데이트
}
lastp = bp; // next_fit 사용을 위한 포인터 동기화
return bp;
}
9. Find_fit (first, next, best)
static void *find_fit(size_t asize)
{
void *bp;
// 새롭게 정의한 포인터 bp를 힙 초기 영역을 가리키는 포인터로 설정
// bp를 블록 단위로 이동시킴
for (bp = heap_listp; GET_SIZE(HDRP(bp)) > 0; bp = NEXT_BLKP(bp))
{
// 블록이 Free block이고, 해당 블록의 사이즈가 입력 사이즈보다 크거나 같을 경우
if (!GET_ALLOC(HDRP(bp)) && (asize <= GET_SIZE(HDRP(bp))))
{
// 해당 블록은 할당이 가능하므로, 해당 블록에 위치한 bp를 그대로 반환한다
return bp;
}
}
return NULL;
}
static void *find_fit(size_t asize)
{
void *bp;
// 새롭게 정의한 포인터 bp를 최근 할당이 이루어진 위치의 포인터로 설정
// bp를 블록 단위로 이동시킴
for (bp = lastp; GET_SIZE(HDRP(bp)) > 0; bp = NEXT_BLKP(bp))
{
// 블록이 Free block이고, 해당 블록 사이즈가 입력 사이즈보다 크거나 같을 경우
if (!GET_ALLOC(HDRP(bp)) && (asize <= GET_SIZE(HDRP(bp))))
{
// 해당 블록은 할당이 가능하므로, 해당 블록에 위치한 bp를 반환하며 lastp 포인터를 동기화한다
lastp = bp;
return bp;
}
}
return NULL;
}
static void *find_fit(size_t asize)
{
void *bp;
void *bestp = NULL;
for (bp = heap_listp; GET_SIZE(HDRP(bp)) > 0; bp = NEXT_BLKP(bp))
{
if (!GET_ALLOC(HDRP(bp)) && (asize <= GET_SIZE(HDRP(bp))))
{
if (bestp == NULL || GET_SIZE(HDRP(bp)) < GET_SIZE(HDRP(bestp)))
{
bestp = bp;
}
}
}
return bestp;
}
Todo :
1. 명시적 가용 리스트 사용
2. realloc 더 최적화
3. find_fit 최적화
'크래프톤 정글 5기 > 공부' 카테고리의 다른 글
Malloc-lab (realloc 개선) (0) | 2024.04.30 |
---|---|
Heap Sort (0) | 2024.04.29 |
분할정복, Divde / Conquer / Combine (0) | 2024.04.06 |
B-Tree, Balanced Tree (1) | 2024.04.06 |
MST, Minimum Spanning Tree (0) | 2024.03.29 |