# 2023q1 Homework6 (quiz5) contributed by < `terry23304` > ## 測驗 1 根據電腦結構設定 `word_size` ,`header_size = sizeof(block_t)` 更好理解 ```c /* Size of a memory element, 32 or 64 bits */ enum { word_size = __SIZE_WIDTH__ / 8, /**< size of memory element */ #if __SIZE_WIDTH__ == 64 log2_word_size = 3, #else log2_word_size = 2, #endif // header_size = 3 * word_size, /**< size, previous/next block addresses */ header_size = sizeof(block_t), /**< size, previous/next block addresses */ }; ``` - pool_init 檢查傳入的 address 是否合法與大小是否大於 `block_t` 的大小,若通過則用 `current->size` 紀錄可用空間, `current->size` 要減去儲存 `size` 的大小, `current->prev` 、 `current->next` 因為是指向 `NULL` 所以不占空間。 ```c pool_free_space = size - word_size; current = (block_t *) addr; current->size = pool_free_space; current->prev = NULL, current->next = NULL; ``` - round_up 確保空間為 `word_size` 的倍數,原本的寫法在 32 位元時不成立 ```c static inline int round_up(const int *x) { // return ((*x + 7) >> log2_word_size) << log2_word_size; return ((*x + (word_size - 1)) >> log2_word_size) << log2_word_size; } ``` - get_loc_to_place 找到可以配置的空間 先確認 `current` size 夠不夠大,因為是 first-fit,找到就直接 retrun ```c static inline void *get_loc_to_place(void *current, int size) { /* Search for a free space to place a new block */ block_t *parse = current; if (parse->size >= (size + header_size)) return current; ``` 往前找 ```c /* parse the prev blocks to find a place */ for (parse = ((block_t *) current)->prev; parse; parse = parse->prev) { if (parse->size >= (size + header_size)) return parse; } ``` 往後找,原本是 `parse = ((block_t *) current)->prev` , 會重複檢查 `current->prev` 跟 `current` ,應為 `parse = ((block_t *) current)->next` ```c // for (parse = ((block_t *) current)->prev; parse; parse = parse->next) { for (parse = ((block_t *) current)->next; parse; parse = parse->next) { if (parse->size >= (size + header_size)) return parse; } return NULL; } ``` - pool_malloc 先把大小補到 `word_size` 的倍數後,找到可配置的空間,但得到可配置空間後卻使用 `current` 來配置 ```c int _size = round_up(&size); /* Return NULL if failed to allocate a buffer */ if (pool_free_space <= (_size + header_size)) return NULL; void *ret = get_loc_to_place(current, _size); if (!ret) return NULL; /* payload's address the application can use */ ret = (char *) current + word_size; void *next_pt = current->next, *prev_pt = current->prev; int new_size = current->size - word_size - _size; ``` - pool_free 呼叫 `get_loc_to_free` 得到 free space,並檢查 block 能不能跟 free space 合併,再嘗試與前後的 block 合併 ```c /* Get block info */ void *block_pt = (char *) addr - word_size; block_t *block = block_pt; /* Update pool arena statistics */ pool_free_space += block->size; void *region; void *free_pt = get_loc_to_free(block_pt); block_t *free_block = (block_t *) free_pt; ``` 修改 first-fit 成 best-fit ```c static inline void *get_loc_to_place(void *current, int size) { block_t *parse = current; block_t *best_fit = NULL; if (parse->size >= (size + header_size)) best_fit = parse; for (parse = ((block_t *)current)->prev; parse; parse = parse->prev) { if (parse->size >= (size + header_size) && (best_fit == NULL || parse->size < best_fit->size)) { best_fit = parse; } } for (parse = ((block_t *)current)->next; parse; parse = parse->next) { if (parse->size >= (size + header_size) && (best_fit == NULL || parse->size < best_fit->size)) { best_fit = parse; } } /* No space found, stop the allocation */ return best_fit; } ``` --- ## 測驗 2 用 `rbnode` 儲存各個 block 的資訊 ```c typedef struct rbnode { size_t size; size_t free; metadata_t *next, *prev; t_key key; t_value **tab_values; size_t size_tab; size_t n_active; rbcolor_t color; struct rbnode *left, *right; } rbnode_t; ``` :::warning TODO: 討論使用紅黑樹實作 free list 的好處和考量因素 :notes: jserv ::: 走訪紅黑樹找到最接近且大於 size 的 node ```c static inline rbnode_t *find_best(rbnode_t *node, size_t size) { rbnode_t *tmp = NULL; while (node) { if (node->key >= size) { tmp = node; node = node->left; } else node = node->right; } return tmp; } ``` 把 `node` 從 `freed_list` 中刪除後,用 `new` 更新剩餘空間的大小,再插入 freed list ```c static void *split_block(metadata_t *node, size_t size) { g_info.root_rbtree = remove_from_freed_list(g_info.root_rbtree, node); if (node->size > size + sizeof(size_t) && node->size - size > sizeof(rbnode_t) + SIZE_DEFAULT_BLOCK) { metadata_t *new = (void *) node + size; new->size = node->size - size; new->free = YFREE; new->prev = node; new->next = node->next; node->next = new; node->size = size; if (new->next) new->next->prev = new; g_info.root_rbtree = insert_in_freed_list(g_info.root_rbtree, new); } return node; } ``` 合併 `first` 、 `second` ```c static metadata_t *fusion(metadata_t *first, metadata_t *second) { first->size += second->size; first->next = second->next; if (first->next) first->next->prev = first; else g_info.last_node = first; return first; } ``` free 時檢查前後是否為 freed 狀態,若則合併兩個 node ```c static inline metadata_t *try_fusion(metadata_t *node) { while (IS_FREE(node->prev)) { g_info.root_rbtree = remove_from_freed_list(g_info.root_rbtree, node->prev); node = fusion(node->prev, node); } while (IS_FREE(node->next)) { g_info.root_rbtree = remove_from_freed_list(g_info.root_rbtree, node->next); node = fusion(node, node->next); } return node; } ``` 避免多個 thread 同時存取 memory block ```c void *realloc(void *ptr, size_t size) { if (!ptr) return malloc(size); if (!size) return free_realloc(ptr); ptr = (void *) ptr - META_SIZE; metadata_t *tmp = (metadata_t *) ptr; metadata_t *new = ptr; if (size + META_SIZE > tmp->size) { if (!(new = malloc(size))) return NULL; size = ALIGN_BYTES(size); pthread_mutex_lock(&g_info.mutex); memcpy(new, (void *) ptr + META_SIZE, (size <= tmp->size) ? (size) : (tmp->size)); pthread_mutex_unlock(&g_info.mutex); free((void *) ptr + META_SIZE); } else new = GET_PAYLOAD(new); return new; } ```