
해당 글은 말록랩 실습과 리스트 종류, 배치 정책, 삽입 정책 등에 관한 내용을 다룹니다. 시간이 된다면 추후에 말록랩에 대한 개념을 정리해보겠습니다.
C 프로그램에서…
malloc은 메모리를 효과적으로 할당해주는 동적 할당을 해줍니다.
free는 그렇게 할당된 메모리를 해제해줍니다.
→ 앞으로 이러한 malloc을 직접 C언어로 구현해보면 됩니다.
직접 구현하기 전에 기본적인 리스트 종류와 배치 정책, 삽입 정책을 알아보겠습니다.
먼저, 정글에서 말록랩을 과제로 다운 받아실행을 해봅시다.
본인의 깃으로 클론한 다음에 깃 서버에 있는 리포지트리를 다운 받아 오면 로컬로 가지고오게 되고 그 파일을 열어보면 됩니다.
보통 도커를 통해 사용할 것입니다. 정글에서 설명한 기본적인 환경 세팅이 완료됐다는 가정하에 설명하겠습니다.
실행하기 전에 해당 패키지들이 우분투에 설치되어 있어야합니다.
sudo apt update # package list update
sudo apt upgrade # upgrade packages
sudo apt install gcc make valgrind gdb # gcc, make 등 개발 환경 설치
sudo apt install gcc-multilib # 32-bit lib (WEEK06만 필요)
cd 명령어를 통해 말록랩 폴더로 이동한다.make로 말록랩 파일을 만든다../mdriver 를 통해 make로 만든 말록랩을 실행시킨다.(./mdriver -V 사용시 테스트 케이스별 결과를 알 수 있다.)out of memory 로 뜬다)→ 위에 계산된 이용도와 처리량의 합을 최대치로 끌어 올리는 것이 이번 주 목표이다.
초기에는 우리가 앞으로 수정할 mm.c에 malloc을 구현하지 않았기 때문에 out of memory라고 뜰 것입니다.
malloc에 성능에 큰 영향을 주는 가용 블록 관리 방식(list 종류), 블록 배치 정책을 적절히 사용하여 높은 점수의 malloc을 구현해봅시다.
특징
예시) [헤더][데이터][헤더][데이터][헤더][데이터] (전부 이어져 있다.)
특징
k는 가용 블록 수)예시) 가용 블록 A → 가용 블록 B → 가용 블록 C (포인터로 이어진다.)
특징
O(1)에 가깝다)예시) [16B 이하 리스트][32B 이하 리스트] [64B 이하 리스트] … (각자 따로 포인터로 연결된 구조)
| 리스트 종류 | 특징 | 장단점 |
|---|---|---|
| Implicit List | 전체 블록 순차 스캔 | 단순하지만 느림 |
| Explicit List | 가용 블록만 포인터로 연결 | 빠르지만 포인터 오버헤드 있음 |
| Segregated List | 크기별로 블록 분리 관리 | 매우 빠르지만 복잡하고 관리비용 큼 |
malloc/free에서 자주 쓰이는 3가지 주요 배치 정책에 대해서 알아보겠습니다.
가용 리스트를 앞에서부터 순차적으로 탐색해서 요청한 크기보다 크거나 같은 첫 번째 가용 블록에 할당하는 방법.
특징
예시) 블록 [10][30] [50] 있는데 25바이트 요청하면 30짜리 블록에 할당.
가용 리스트 전체를 스캔한 다음에 요청한 크기보다 크거나 같은 블록 중에서 "가장 작은 것"을 선택하는 방법.
특징
O(n))예시) 블록 [10][30] [50] 있을 때, 25바이트 요청하면 30짜리 선택. (30이 50보다 작으니까)
직전에 할당했던 가용 블록 이후부터 검색을 시작하는 방법. 리스트의 끝에 도달하면 처음부터 다시 순환해서 찾는다.
특징
예시) 만약 직전에 블록 [10]을 썼다면, 다음에는 [30], [50] 순서로 탐색 시작.
| 배치 정책 | 탐색 방식 | 장점 | 단점 |
|---|---|---|---|
| First Fit | 처음부터 순차적으로 검색 | 빠를 수 있음 | 단편화 발생 가능 |
| Best Fit | 전체 검색 후 가장 작은 블록 선택 | 메모리 낭비 적음 | 탐색 오래 걸림 |
| Next Fit | 이전 탐색 위치 이후부터 검색 | 탐색 시간 줄어듦 | 단편화 문제 여전 |
특징
장점
단점
coalesce() 때 블록 병합을 하려면 인접한 블록을 별도로 찾아야 함 → 성능 저하 위험코드 예시
bp->succ = free_list_root;
if (free_list_root != NULL)
free_list_root->pred = bp;
bp->pred = NULL;
free_list_root = bp;
특징
장점
단점
코드 예시(간단화)
void add_free_block(void *bp) {
void *p = free_list_root;
void *prev = NULL;
while (p != NULL && p < bp) {
prev = p;
p = GET_SUCC(p);
}
// bp를 prev와 p 사이에 삽입
if (prev == NULL) {
free_list_root = bp;
} else {
GET_SUCC(prev) = bp;
}
GET_PRED(bp) = prev;
GET_SUCC(bp) = p;
if (p != NULL)
GET_PRED(p) = bp;
}
| 구분 | LIFO 삽입 (Last-In-First-Out) | 주소순 삽입 (Address-Ordered) |
|---|---|---|
| 동작 | 새로 생긴 free 블록을 항상 리스트 맨 앞에 넣음 | free 블록을 메모리 주소 순서에 맞게 삽입 |
| 삽입 속도 | 아주 빠름 (O(1)) | 느림 (O(n), 주소 비교 필요) |
| 병합(Coalescing) | 복잡 (인접 블록 찾기 어려움) | 아주 쉽다 (리스트상에서 바로 앞/뒤 확인 가능) |
| 단편화(Fragmentation) | 증가할 가능성 있음 | 감소할 가능성 높음 |
| 구현 난이도 | 쉬움 | 약간 복잡 |
위의 내용을 생각해보면 최적의 조합을 생각할 수 있다.
Malloc 구현 시 위에 서술된 가용 리스트 종류와 배치정책을 잘 조합하면, 메모리 성능과 단편화 문제를 해결한 조합을 만들 수 있다.