Malloc Lab Implementation
Contents
- Step 0: Fill in the team_t structure
- Step 1: Macros
- Step 1: List Functions
- Step 1: Debugging
- Step 1: GDB init file
- Step 1: mm_init
- Step 2: Modify find_fit
- Step 2: Modify place (and mm_malloc)
- Step 2: Should modify mm_free modify, but not yet
- Step 2: Test and/or Debug
- Step 2: Sample GDB execution
- Step 3: Modify coalesce
- Step 3: Modify mm_free
- Step 3: Test Again using gdb
- Step 3: Run Sample tests
- Step 3: Optimization CHUNKSIZE
- Step 4: Implement mm_realloc
- Step 5: Optimize place
- Step 6: Optimize mm_realloc
Step 0: Fill in the team_t structure[top]
... #include <stdio.h> #include "mm.h" #include "memlib.h" team_t team = {"","","","",""}; ...
The mm.h file has the definition of the team_t type:
typedef struct { char *teamname; /* ID1+ID2 or ID1 */ char *name1; /* full name of first member */ char *id1; /* login ID of first member */ char *name2; /* full name of second member (if any) */ char *id2; /* login ID of second member */ } team_t;
So, for example, I would fill in the value of the variable team like this:
team_t team = {"glancast","Glenn Lancaster","glancast","",""};
Step 1: Macros[top]
Define appropriate macros to be able to manipulate the next and prev pointers stored in the free blocks as well as the size/allocation values stored in the header and footer of each block.
Also the prolog block can function as a 'sentinel' guard node for a linked list of the free blocks. This will require expanding the prolog block to contain prev and next pointers. So you will need to redefine PROLOG (size of the prolog block) to account for the increased prolog size.
- GETSIZE(bp); size of block at bp
- GETALLOC(bp); allocation of the block at bp
- HD(bp) alias for block header; contains size and allocation
- FT(bp) alias for block footer; contains size and allocation
- PREV(bp) alias for pointer to previous free block
- NEXT(bp) alias for pointer to next free block
Step 1: List Functions[top]
Write functions (using the appropriate macros) to insert blocks into and remove blocks from the explicit free block list.
- void mm_insert(void *bp)
- void mm_remove(void *bp)
Step 1: Debugging[top]
- Modify mm_checkheap to use PROLOG instead of DSIZE
- Modify printblock to
- print EOL for the epilog block (no change)
- print the NEXT and PREV pointers in the prolog block (i.e., if bp == heap_listp) as well as the header and footer information.
- also print the NEXT and PREV pointers in free blocks as well as the header and footer information.
- print only the header and footer information if the block is allocated. (no change)
Step 1: GDB init file[top]
Create an initialization file, say gdbinit, for gdb.
- Define a new command, say hh, to call mm_checkheap(1)
- Set break points for mm_init, mm_malloc, mm_free, and mm_realloc
- Optionally add commands to the break points in mm_malloc and mm_free to print the heap.
Step 1: mm_init[top]
- Adjust for the new PROLOG
- Initialize the free list sentinel (PREV and NEXT members of the prolog block in the new prolog block format).
- Insert the free block returned by extend_heap into the free list (with mm_insert).
- Use gdb to test all these changes to mm_init; for
convenience, start gdb with
your initialization file so that hh and the break points are set
automatically.
gdb -x gdbinit mdriver ... (gdb) r -d 1 -f traces/trace0.rep
Step 2: Modify find_fit[top]
- Modify find_fit to search through the list of free blocks only
- If a suitable free block is found in find_fit, remove it from the free list before returning it.
Step 2: Modify place (and mm_malloc)[top]
The block found by find_fit (or by extend_heap) is passed to the place function.
The place function marks either the whole block as allocated or else splits it and marks one part as allocated and the other as free.
-
Change the declaration and the definition of place to return a pointer to the allocated part of the block.
-
If place splits the block into two blocks, the block that is
marked free should be inserted on the free list (with your
mm_insert function) and place should return the other,
allocated block.
If place doesn't split the block, just return a pointer to the whole block.
Step 2: Should modify mm_free modify, but not yet[top]
The mm_free function is not yet modified.
This function gets a pointer to a block that was allocated and marks it as free.
This function then calls coalesce to see if either or both the adjacent blocks are free. If so, the coalesce function merges these blocks into a bigger free block and returns a pointer to this merged free block.
The mm_free function should call mm_insert to insert this free block (whether merged or not) into the free list.
But don't do that quite yet. You will need to modify coalesce before making this last change to mm_free.
Step 2: Test and/or Debug[top]
- Use gdb with the following small input file to test the
changes made so far.
Create the file with an editor. I'll refer to this file as s3.rep
This input trace makes all its allocation requests first and then frees each request, but not in the order allocated.
Rather an attempt is made to avoid merging as long a possible. Remember that at this point, blocks that mm_free marks free are NOT being inserted in the free list.
Only mm_init and the place function are inserting free blocks into the list so far.
10000 4 8 1 a 0 2040 a 1 2040 a 2 48 a 3 48 f 0 f 2 f 1 f 3
Example
gdb -x gdbinit mdriver .... gdb boiler plate message printed .... break points set by your initialization file printed (gdb) r -d 1 -f s3.rep
Attach the hh command to the break points at mm_free and mm_malloc and debug using the continue command (c); this should print the heap at the beginning of each call to mm_free and mm_malloc
-
Notice what happens to the heap as calls are made to mm_free.
Since mm_free has not yet included calls to mm_insert,
those blocks are not yet being inserted back into the free
list.
What gets printed for the PREV and NEXT members of those blocks that are freed (but not yet inserted with mm_insert)?
Step 2: Sample GDB execution[top]
Here is the output I got when running gdb on the carefully constructed input file s3.rep after making the changes above to mm.c.
The input file s3.rep has been constructed so that all the allocations come first and so allows checking the changes to mm_malloc, find_fit, and place are correct. The subsequent calls to mm_free don't yet insert freed blocks into the free list, but this doesn't interfere with checking the previous calls to mm_malloc.
gdb -x gdbinit mdriver ... Breakpoint 1 at 0x804a992: file mm.c, line 98. Breakpoint 2 at 0x804aa86: file mm.c, line 148. Breakpoint 3 at 0x804ab43: file mm.c, line 178. Breakpoint 4 at 0x804ab8b: file mm.c, line 190. (gdb) r -d 1 -f s3.rep Starting program: /home/CSTCIS/glancast/374class/mdriver -d 1 -f s3.rep Team Name:glancast Member 1 :Glenn Lancaster:glancast Breakpoint 1, mm_init () at mm.c:98 98 if ((heap_listp = mem_sbrk(PAD + PROLOG + EPILOGSIZE)) == NULL) (gdb) c Continuing. Breakpoint 2, mm_malloc (size=2040) at mm.c:148 148 if (size <= 0) Heap (0xb6be6010): 0xb6be6010: header: [16:a]| prev: 0xb6be6020, next: 0xb6be6020 | footer: [16:a] 0xb6be6020: header: [4096:f]| prev: 0xb6be6010, next: 0xb6be6010 | footer: [4096:f] 0xb6be7020: EOL (gdb) c Continuing. Breakpoint 2, mm_malloc (size=2040) at mm.c:148 148 if (size <= 0) Heap (0xb6be6010): 0xb6be6010: header: [16:a]| prev: 0xb6be6820, next: 0xb6be6820 | footer: [16:a] 0xb6be6020: header: [2048:a] footer: [2048:a] 0xb6be6820: header: [2048:f]| prev: 0xb6be6010, next: 0xb6be6010 | footer: [2048:f] 0xb6be7020: EOL (gdb) c Continuing. Breakpoint 2, mm_malloc (size=48) at mm.c:148 148 if (size <= 0) Heap (0xb6be6010): 0xb6be6010: header: [16:a]| prev: 0xb6be6010, next: 0xb6be6010 | footer: [16:a] 0xb6be6020: header: [2048:a] footer: [2048:a] 0xb6be6820: header: [2048:a] footer: [2048:a] 0xb6be7020: EOL (gdb) c Continuing. Breakpoint 2, mm_malloc (size=48) at mm.c:148 148 if (size <= 0) Heap (0xb6be6010): 0xb6be6010: header: [16:a]| prev: 0xb6be7058, next: 0xb6be7058 | footer: [16:a] 0xb6be6020: header: [2048:a] footer: [2048:a] 0xb6be6820: header: [2048:a] footer: [2048:a] 0xb6be7020: header: [56:a] footer: [56:a] 0xb6be7058: header: [4040:f]| prev: 0xb6be6010, next: 0xb6be6010 | footer: [4040:f] 0xb6be8020: EOL (gdb) c Continuing. Breakpoint 3, mm_free (bp=0xb6be6020) at mm.c:178 178 size_t size = GET_SIZE(HDRP(bp)); Heap (0xb6be6010): 0xb6be6010: header: [16:a]| prev: 0xb6be7090, next: 0xb6be7090 | footer: [16:a] 0xb6be6020: header: [2048:a] footer: [2048:a] 0xb6be6820: header: [2048:a] footer: [2048:a] 0xb6be7020: header: [56:a] footer: [56:a] 0xb6be7058: header: [56:a] footer: [56:a] 0xb6be7090: header: [3984:f]| prev: 0xb6be6010, next: 0xb6be6010 | footer: [3984:f] 0xb6be8020: EOL (gdb) c Continuing. Breakpoint 3, mm_free (bp=0xb6be7020) at mm.c:178 178 size_t size = GET_SIZE(HDRP(bp)); Heap (0xb6be6010): 0xb6be6010: header: [16:a]| prev: 0xb6be7090, next: 0xb6be7090 | footer: [16:a] 0xb6be6020: header: [2048:f]| prev: (nil), next: (nil) | footer: [2048:f] 0xb6be6820: header: [2048:a] footer: [2048:a] 0xb6be7020: header: [56:a] footer: [56:a] 0xb6be7058: header: [56:a] footer: [56:a] 0xb6be7090: header: [3984:f]| prev: 0xb6be6010, next: 0xb6be6010 | footer: [3984:f] 0xb6be8020: EOL (gdb) c Continuing. Breakpoint 3, mm_free (bp=0xb6be6820) at mm.c:178 178 size_t size = GET_SIZE(HDRP(bp)); Heap (0xb6be6010): 0xb6be6010: header: [16:a]| prev: 0xb6be7090, next: 0xb6be7090 | footer: [16:a] 0xb6be6020: header: [2048:f]| prev: (nil), next: (nil) | footer: [2048:f] 0xb6be6820: header: [2048:a] footer: [2048:a] 0xb6be7020: header: [56:f]| prev: 0x2020202, next: 0x2020202 | footer: [56:f] 0xb6be7058: header: [56:a] footer: [56:a] 0xb6be7090: header: [3984:f]| prev: 0xb6be6010, next: 0xb6be6010 | footer: [3984:f] 0xb6be8020: EOL (gdb) c Continuing. Breakpoint 3, mm_free (bp=0xb6be7058) at mm.c:178 178 size_t size = GET_SIZE(HDRP(bp)); Heap (0xb6be6010): 0xb6be6010: header: [16:a]| prev: 0xb6be7090, next: 0xb6be7090 | footer: [16:a] 0xb6be6020: header: [4152:f]| prev: (nil), next: (nil) | footer: [4152:f] 0xb6be7058: header: [56:a] footer: [56:a] 0xb6be7090: header: [3984:f]| prev: 0xb6be6010, next: 0xb6be6010 | footer: [3984:f] 0xb6be8020: EOL (gdb) c Continuing. Terminated with 0 errors Program exited normally. (gdb)
Step 3: Modify coalesce[top]
The void * coalesce(void *bp) function is passed a block, bp, that is already marked free, but not on the free list..
It checks it adjacent blocks (using NEXT_BLKP and PREV_BLKP macros).
If an adjacent block to bp is free, the adjacent block is on the free list and must first be removed (using your mm_remove) before the coalesce code that merges it with bp.
Step 3: Modify mm_free[top]
The mm_free(bp) function is passed an allocated block bp.
It marks this block as free and then calls coalesce.
Now that coalesce has been fixed, you can modify mm_free to insert the possibly larger block returned by coalesce into the free list.
Step 3: Test Again using gdb[top]
You can use the same s3.rep function to test that the changes to coalesce and mm_free are what you expect.
Step 3: Run Sample tests[top]
Now you are ready to run the larger tests in the traces directory to see what changes in throughput (Kops) and utility your explicit free list implementation has made.
make tests
Step 3: Optimization CHUNKSIZE[top]
Try adding a small value (a multiple of 8) to CHUNKSIZE to see if it improves any of the trace utility scores.
(Remember to recompile before running the tests).
Step 4: Implement mm_realloc[top]
void * mm_realloc(void *ptr, size_t size)
- Special cases:
- If ptr is NULL, return mm_malloc(size)
- If size is 0, call mm_free(ptr) and return NULL
- May be able to return same ptr:
- Get oldsize of block at ptr
- Calculate the adjusted asize of the request
- If asize <= oldsize, just return ptr
-
Allocate new block and copy
- Call mm_malloc(size) to get a new block
- Copy the payload of the ptr block (oldsize - 8 bytes)
to the payload of the new block. Use memcpy:
void *memcpy(void *dest, void *src, size_t nbytes);
- call mm_free(ptr) to free the old block
- return pointer to the new block
Step 5: Optimize place[top]
Traces 7 and 8 have improved with the previous changes, but individual scores on these traces are still about 73 and 71.
An realtively small change to place can improve these scores to 98 and 93.
Step 6: Optimize mm_realloc[top]
Scores for traces 9 and 10 which call the initial implementation of mm_realloc in step 4 are pretty miserable: 22 and 58.
Several changes that avoid copying the payload can improve these scores to 99 and 91.