initial import of base files
This commit is contained in:
		
						commit
						92fa2c791a
					
				
							
								
								
									
										40
									
								
								Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								Makefile
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,40 @@ | ||||
| #
 | ||||
| # Students' Makefile for the Malloc Lab
 | ||||
| #
 | ||||
| VERSION = 1 | ||||
| 
 | ||||
| CC = gcc | ||||
| CFLAGS = -Wall -O3 -Werror -m32 | ||||
| # for debugging
 | ||||
| #CFLAGS = -Wall -g -Werror -m32
 | ||||
| 
 | ||||
| SHARED_OBJS = mdriver.o memlib.o fsecs.o fcyc.o clock.o ftimer.o list.o | ||||
| OBJS = $(SHARED_OBJS) mm.o | ||||
| BOOK_IMPL_OBJS = $(SHARED_OBJS) mm-book-implicit.o | ||||
| GBACK_IMPL_OBJS = $(SHARED_OBJS) mm-gback-implicit.o | ||||
| 
 | ||||
| mdriver: $(OBJS) | ||||
| 	$(CC) $(CFLAGS) -o mdriver $(OBJS) | ||||
| 
 | ||||
| mdriver-book: $(BOOK_IMPL_OBJS) | ||||
| 	$(CC) $(CFLAGS) -o $@ $(BOOK_IMPL_OBJS) | ||||
| 
 | ||||
| mdriver-gback: $(GBACK_IMPL_OBJS) | ||||
| 	$(CC) $(CFLAGS) -o $@ $(GBACK_IMPL_OBJS) | ||||
| 
 | ||||
| mdriver.o: mdriver.c fsecs.h fcyc.h clock.h memlib.h config.h mm.h | ||||
| memlib.o: memlib.c memlib.h | ||||
| mm.o: mm.c mm.h memlib.h | ||||
| fsecs.o: fsecs.c fsecs.h config.h | ||||
| fcyc.o: fcyc.c fcyc.h | ||||
| ftimer.o: ftimer.c ftimer.h config.h | ||||
| clock.o: clock.c clock.h | ||||
| list.o: list.c list.h | ||||
| 
 | ||||
| handin: | ||||
| 	/home/courses/cs3214/bin/submit.pl p3 mm.c | ||||
| 
 | ||||
| clean: | ||||
| 	rm -f *~ *.o mdriver | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										279
									
								
								clock.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										279
									
								
								clock.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,279 @@ | ||||
| /* 
 | ||||
|  * clock.c - Routines for using the cycle counters on x86,  | ||||
|  *           Alpha, and Sparc boxes. | ||||
|  *  | ||||
|  * Copyright (c) 2002, R. Bryant and D. O'Hallaron, All rights reserved. | ||||
|  * May not be used, modified, or copied without permission. | ||||
|  */ | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <unistd.h> | ||||
| #include <sys/times.h> | ||||
| #include "clock.h" | ||||
| 
 | ||||
| 
 | ||||
| /******************************************************* 
 | ||||
|  * Machine dependent functions  | ||||
|  * | ||||
|  * Note: the constants __i386__ and  __alpha | ||||
|  * are set by GCC when it calls the C preprocessor | ||||
|  * You can verify this for yourself using gcc -v. | ||||
|  *******************************************************/ | ||||
| 
 | ||||
| #if defined(__i386__)   | ||||
| /*******************************************************
 | ||||
|  * Pentium versions of start_counter() and get_counter() | ||||
|  *******************************************************/ | ||||
| 
 | ||||
| 
 | ||||
| /* $begin x86cyclecounter */ | ||||
| /* Initialize the cycle counter */ | ||||
| static unsigned cyc_hi = 0; | ||||
| static unsigned cyc_lo = 0; | ||||
| 
 | ||||
| 
 | ||||
| /* Set *hi and *lo to the high and low order bits  of the cycle counter.  
 | ||||
|    Implementation requires assembly code to use the rdtsc instruction. */ | ||||
| void access_counter(unsigned *hi, unsigned *lo) | ||||
| { | ||||
|     asm volatile ("rdtsc; movl %%edx,%0; movl %%eax,%1"   /* Read cycle counter */ | ||||
| 	: "=r" (*hi), "=r" (*lo)                /* and move results to */ | ||||
| 	: /* No input */                        /* the two outputs */ | ||||
| 	: "%edx", "%eax"); | ||||
| } | ||||
| 
 | ||||
| /* Record the current value of the cycle counter. */ | ||||
| void start_counter() | ||||
| { | ||||
|     access_counter(&cyc_hi, &cyc_lo); | ||||
| } | ||||
| 
 | ||||
| /* Return the number of cycles since the last call to start_counter. */ | ||||
| double get_counter() | ||||
| { | ||||
|     unsigned ncyc_hi, ncyc_lo; | ||||
|     unsigned hi, lo, borrow; | ||||
|     double result; | ||||
| 
 | ||||
|     /* Get cycle counter */ | ||||
|     access_counter(&ncyc_hi, &ncyc_lo); | ||||
| 
 | ||||
|     /* Do double precision subtraction */ | ||||
|     lo = ncyc_lo - cyc_lo; | ||||
|     borrow = lo > ncyc_lo; | ||||
|     hi = ncyc_hi - cyc_hi - borrow; | ||||
|     result = (double) hi * (1 << 30) * 4 + lo; | ||||
|     if (result < 0) { | ||||
| 	fprintf(stderr, "Error: counter returns neg value: %.0f\n", result); | ||||
|     } | ||||
|     return result; | ||||
| } | ||||
| /* $end x86cyclecounter */ | ||||
| 
 | ||||
| #elif defined(__alpha) | ||||
| 
 | ||||
| /****************************************************
 | ||||
|  * Alpha versions of start_counter() and get_counter() | ||||
|  ***************************************************/ | ||||
| 
 | ||||
| /* Initialize the cycle counter */ | ||||
| static unsigned cyc_hi = 0; | ||||
| static unsigned cyc_lo = 0; | ||||
| 
 | ||||
| 
 | ||||
| /* Use Alpha cycle timer to compute cycles.  Then use
 | ||||
|    measured clock speed to compute seconds  | ||||
| */ | ||||
| 
 | ||||
| /*
 | ||||
|  * counterRoutine is an array of Alpha instructions to access  | ||||
|  * the Alpha's processor cycle counter. It uses the rpcc  | ||||
|  * instruction to access the counter. This 64 bit register is  | ||||
|  * divided into two parts. The lower 32 bits are the cycles  | ||||
|  * used by the current process. The upper 32 bits are wall  | ||||
|  * clock cycles. These instructions read the counter, and  | ||||
|  * convert the lower 32 bits into an unsigned int - this is the  | ||||
|  * user space counter value. | ||||
|  * NOTE: The counter has a very limited time span. With a  | ||||
|  * 450MhZ clock the counter can time things for about 9  | ||||
|  * seconds. */ | ||||
| static unsigned int counterRoutine[] = | ||||
| { | ||||
|     0x601fc000u, | ||||
|     0x401f0000u, | ||||
|     0x6bfa8001u | ||||
| }; | ||||
| 
 | ||||
| /* Cast the above instructions into a function. */ | ||||
| static unsigned int (*counter)(void)= (void *)counterRoutine; | ||||
| 
 | ||||
| 
 | ||||
| void start_counter() | ||||
| { | ||||
|     /* Get cycle counter */ | ||||
|     cyc_hi = 0; | ||||
|     cyc_lo = counter(); | ||||
| } | ||||
| 
 | ||||
| double get_counter() | ||||
| { | ||||
|     unsigned ncyc_hi, ncyc_lo; | ||||
|     unsigned hi, lo, borrow; | ||||
|     double result; | ||||
|     ncyc_lo = counter(); | ||||
|     ncyc_hi = 0; | ||||
|     lo = ncyc_lo - cyc_lo; | ||||
|     borrow = lo > ncyc_lo; | ||||
|     hi = ncyc_hi - cyc_hi - borrow; | ||||
|     result = (double) hi * (1 << 30) * 4 + lo; | ||||
|     if (result < 0) { | ||||
| 	fprintf(stderr, "Error: Cycle counter returning negative value: %.0f\n", result); | ||||
|     } | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| #else | ||||
| 
 | ||||
| /****************************************************************
 | ||||
|  * All the other platforms for which we haven't implemented cycle | ||||
|  * counter routines. Newer models of sparcs (v8plus) have cycle | ||||
|  * counters that can be accessed from user programs, but since there | ||||
|  * are still many sparc boxes out there that don't support this, we | ||||
|  * haven't provided a Sparc version here. | ||||
|  ***************************************************************/ | ||||
| 
 | ||||
| void start_counter() | ||||
| { | ||||
|     printf("ERROR: You are trying to use a start_counter routine in clock.c\n"); | ||||
|     printf("that has not been implemented yet on this platform.\n"); | ||||
|     printf("Please choose another timing package in config.h.\n"); | ||||
|     exit(1); | ||||
| } | ||||
| 
 | ||||
| double get_counter()  | ||||
| { | ||||
|     printf("ERROR: You are trying to use a get_counter routine in clock.c\n"); | ||||
|     printf("that has not been implemented yet on this platform.\n"); | ||||
|     printf("Please choose another timing package in config.h.\n"); | ||||
|     exit(1); | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /*******************************
 | ||||
|  * Machine-independent functions | ||||
|  ******************************/ | ||||
| double ovhd() | ||||
| { | ||||
|     /* Do it twice to eliminate cache effects */ | ||||
|     int i; | ||||
|     double result; | ||||
| 
 | ||||
|     for (i = 0; i < 2; i++) { | ||||
| 	start_counter(); | ||||
| 	result = get_counter(); | ||||
|     } | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| /* $begin mhz */ | ||||
| /* Estimate the clock rate by measuring the cycles that elapse */  | ||||
| /* while sleeping for sleeptime seconds */ | ||||
| double mhz_full(int verbose, int sleeptime) | ||||
| { | ||||
|     double rate; | ||||
| 
 | ||||
|     start_counter(); | ||||
|     sleep(sleeptime); | ||||
|     rate = get_counter() / (1e6*sleeptime); | ||||
|     if (verbose)  | ||||
| 	printf("Processor clock rate ~= %.1f MHz\n", rate); | ||||
|     return rate; | ||||
| } | ||||
| /* $end mhz */ | ||||
| 
 | ||||
| /* Version using a default sleeptime */ | ||||
| double mhz(int verbose) | ||||
| { | ||||
|     return mhz_full(verbose, 2); | ||||
| } | ||||
| 
 | ||||
| /** Special counters that compensate for timer interrupt overhead */ | ||||
| 
 | ||||
| static double cyc_per_tick = 0.0; | ||||
| 
 | ||||
| #define NEVENT 100 | ||||
| #define THRESHOLD 1000 | ||||
| #define RECORDTHRESH 3000 | ||||
| 
 | ||||
| /* Attempt to see how much time is used by timer interrupt */ | ||||
| static void callibrate(int verbose) | ||||
| { | ||||
|     double oldt; | ||||
|     struct tms t; | ||||
|     clock_t oldc; | ||||
|     int e = 0; | ||||
| 
 | ||||
|     times(&t); | ||||
|     oldc = t.tms_utime; | ||||
|     start_counter(); | ||||
|     oldt = get_counter(); | ||||
|     while (e <NEVENT) { | ||||
| 	double newt = get_counter(); | ||||
| 
 | ||||
| 	if (newt-oldt >= THRESHOLD) { | ||||
| 	    clock_t newc; | ||||
| 	    times(&t); | ||||
| 	    newc = t.tms_utime; | ||||
| 	    if (newc > oldc) { | ||||
| 		double cpt = (newt-oldt)/(newc-oldc); | ||||
| 		if ((cyc_per_tick == 0.0 || cyc_per_tick > cpt) && cpt > RECORDTHRESH) | ||||
| 		    cyc_per_tick = cpt; | ||||
| 		/*
 | ||||
| 		  if (verbose) | ||||
| 		  printf("Saw event lasting %.0f cycles and %d ticks.  Ratio = %f\n", | ||||
| 		  newt-oldt, (int) (newc-oldc), cpt); | ||||
| 		*/ | ||||
| 		e++; | ||||
| 		oldc = newc; | ||||
| 	    } | ||||
| 	    oldt = newt; | ||||
| 	} | ||||
|     } | ||||
|     if (verbose) | ||||
| 	printf("Setting cyc_per_tick to %f\n", cyc_per_tick); | ||||
| } | ||||
| 
 | ||||
| static clock_t start_tick = 0; | ||||
| 
 | ||||
| void start_comp_counter()  | ||||
| { | ||||
|     struct tms t; | ||||
| 
 | ||||
|     if (cyc_per_tick == 0.0) | ||||
| 	callibrate(0); | ||||
|     times(&t); | ||||
|     start_tick = t.tms_utime; | ||||
|     start_counter(); | ||||
| } | ||||
| 
 | ||||
| double get_comp_counter()  | ||||
| { | ||||
|     double time = get_counter(); | ||||
|     double ctime; | ||||
|     struct tms t; | ||||
|     clock_t ticks; | ||||
| 
 | ||||
|     times(&t); | ||||
|     ticks = t.tms_utime - start_tick; | ||||
|     ctime = time - ticks*cyc_per_tick; | ||||
|     /*
 | ||||
|       printf("Measured %.0f cycles.  Ticks = %d.  Corrected %.0f cycles\n", | ||||
|       time, (int) ticks, ctime); | ||||
|     */ | ||||
|     return ctime; | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										22
									
								
								clock.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								clock.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | ||||
| /* Routines for using cycle counter */ | ||||
| 
 | ||||
| /* Start the counter */ | ||||
| void start_counter(); | ||||
| 
 | ||||
| /* Get # cycles since counter started */ | ||||
| double get_counter(); | ||||
| 
 | ||||
| /* Measure overhead for counter */ | ||||
| double ovhd(); | ||||
| 
 | ||||
| /* Determine clock rate of processor (using a default sleeptime) */ | ||||
| double mhz(int verbose); | ||||
| 
 | ||||
| /* Determine clock rate of processor, having more control over accuracy */ | ||||
| double mhz_full(int verbose, int sleeptime); | ||||
| 
 | ||||
| /** Special counters that compensate for timer interrupt overhead */ | ||||
| 
 | ||||
| void start_comp_counter(); | ||||
| 
 | ||||
| double get_comp_counter(); | ||||
							
								
								
									
										76
									
								
								config.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								config.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,76 @@ | ||||
| #ifndef __CONFIG_H_ | ||||
| #define __CONFIG_H_ | ||||
| 
 | ||||
| /*
 | ||||
|  * config.h - malloc lab configuration file | ||||
|  * | ||||
|  * Copyright (c) 2002, R. Bryant and D. O'Hallaron, All rights reserved. | ||||
|  * May not be used, modified, or copied without permission. | ||||
|  */ | ||||
| 
 | ||||
| /*
 | ||||
|  * This is the default path where the driver will look for the | ||||
|  * default tracefiles. You can override it at runtime with the -t flag. | ||||
|  */ | ||||
| #define TRACEDIR "/home/courses/cs3214/malloclab/traces/" | ||||
| 
 | ||||
| /*
 | ||||
|  * This is the list of default tracefiles in TRACEDIR that the driver | ||||
|  * will use for testing. Modify this if you want to add or delete | ||||
|  * traces from the driver's test suite. For example, if you don't want | ||||
|  * your students to implement realloc, you can delete the last two | ||||
|  * traces. | ||||
|  */ | ||||
| #define DEFAULT_TRACEFILES \ | ||||
|   "amptjp-bal.rep",\ | ||||
|   "cccp-bal.rep",\ | ||||
|   "cp-decl-bal.rep",\ | ||||
|   "expr-bal.rep",\ | ||||
|   "coalescing-bal.rep",\ | ||||
|   "random-bal.rep",\ | ||||
|   "random2-bal.rep",\ | ||||
|   "binary-bal.rep",\ | ||||
|   "binary2-bal.rep",\ | ||||
|   "realloc-bal.rep",\ | ||||
|   "realloc2-bal.rep" | ||||
| 
 | ||||
| /*
 | ||||
|  * This constant gives the estimated performance of the libc malloc | ||||
|  * package using our traces on some reference system, typically the | ||||
|  * same kind of system the students use. Its purpose is to cap the | ||||
|  * contribution of throughput to the performance index. Once the | ||||
|  * students surpass the AVG_LIBC_THRUPUT, they get no further benefit | ||||
|  * to their score.  This deters students from building extremely fast, | ||||
|  * but extremely stupid malloc packages. | ||||
|  * | ||||
|  * gback@cs.vt.edu: I set this to a value that is achieved by a r/b  | ||||
|  * tree-based implementation on our rlogin cluster as of Fall 2014;  | ||||
|  * regardless of the speed of the actual libc | ||||
|  */ | ||||
| #define AVG_LIBC_THRUPUT      14.6E6 /* 14600 Kops/sec */ | ||||
| 
 | ||||
|  /* 
 | ||||
|   * This constant determines the contributions of space utilization | ||||
|   * (UTIL_WEIGHT) and throughput (1 - UTIL_WEIGHT) to the performance | ||||
|   * index.   | ||||
|   */ | ||||
| #define UTIL_WEIGHT .60 | ||||
| 
 | ||||
| /* 
 | ||||
|  * Alignment requirement in bytes (either 4 or 8)  | ||||
|  */ | ||||
| #define ALIGNMENT 8 | ||||
| 
 | ||||
| /* 
 | ||||
|  * Maximum heap size in bytes  | ||||
|  */ | ||||
| #define MAX_HEAP (20*(1<<20))  /* 20 MB */ | ||||
| 
 | ||||
| /*****************************************************************************
 | ||||
|  * Set exactly one of these USE_xxx constants to "1" to select a timing method | ||||
|  *****************************************************************************/ | ||||
| #define USE_FCYC   1   /* cycle counter w/K-best scheme (x86 & Alpha only) */ | ||||
| #define USE_ITIMER 0   /* interval timer (any Unix box) */ | ||||
| #define USE_GETTOD 0   /* gettimeofday (any Unix box) */ | ||||
| 
 | ||||
| #endif /* __CONFIG_H */ | ||||
							
								
								
									
										251
									
								
								fcyc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										251
									
								
								fcyc.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,251 @@ | ||||
| /*
 | ||||
|  * fcyc.c - Estimate the time (in CPU cycles) used by a function f  | ||||
|  *  | ||||
|  * Copyright (c) 2002, R. Bryant and D. O'Hallaron, All rights reserved. | ||||
|  * May not be used, modified, or copied without permission. | ||||
|  * | ||||
|  * Uses the cycle timer routines in clock.c to estimate the | ||||
|  * the time in CPU cycles for a function f. | ||||
|  */ | ||||
| #include <stdlib.h> | ||||
| #include <sys/times.h> | ||||
| #include <stdio.h> | ||||
| 
 | ||||
| #include "fcyc.h" | ||||
| #include "clock.h" | ||||
| 
 | ||||
| /* Default values */ | ||||
| #define K 3                  /* Value of K in K-best scheme */ | ||||
| #define MAXSAMPLES 20        /* Give up after MAXSAMPLES */ | ||||
| #define EPSILON 0.01         /* K samples should be EPSILON of each other*/ | ||||
| #define COMPENSATE 0         /* 1-> try to compensate for clock ticks */ | ||||
| #define CLEAR_CACHE 0        /* Clear cache before running test function */ | ||||
| #define CACHE_BYTES (1<<19)  /* Max cache size in bytes */ | ||||
| #define CACHE_BLOCK 32       /* Cache block size in bytes */ | ||||
| 
 | ||||
| static int kbest = K; | ||||
| static int maxsamples = MAXSAMPLES; | ||||
| static double epsilon = EPSILON; | ||||
| static int compensate = COMPENSATE; | ||||
| static int clear_cache = CLEAR_CACHE; | ||||
| static int cache_bytes = CACHE_BYTES; | ||||
| static int cache_block = CACHE_BLOCK; | ||||
| 
 | ||||
| static int *cache_buf = NULL; | ||||
| 
 | ||||
| static double *values = NULL; | ||||
| static int samplecount = 0; | ||||
| 
 | ||||
| /* for debugging only */ | ||||
| #define KEEP_VALS 0 | ||||
| #define KEEP_SAMPLES 0 | ||||
| 
 | ||||
| #if KEEP_SAMPLES | ||||
| static double *samples = NULL; | ||||
| #endif | ||||
| 
 | ||||
| /* 
 | ||||
|  * init_sampler - Start new sampling process  | ||||
|  */ | ||||
| static void init_sampler() | ||||
| { | ||||
|     if (values) | ||||
| 	free(values); | ||||
|     values = calloc(kbest, sizeof(double)); | ||||
| #if KEEP_SAMPLES | ||||
|     if (samples) | ||||
| 	free(samples); | ||||
|     /* Allocate extra for wraparound analysis */ | ||||
|     samples = calloc(maxsamples+kbest, sizeof(double)); | ||||
| #endif | ||||
|     samplecount = 0; | ||||
| } | ||||
| 
 | ||||
| /* 
 | ||||
|  * add_sample - Add new sample   | ||||
|  */ | ||||
| static void add_sample(double val) | ||||
| { | ||||
|     int pos = 0; | ||||
|     if (samplecount < kbest) { | ||||
| 	pos = samplecount; | ||||
| 	values[pos] = val; | ||||
|     } else if (val < values[kbest-1]) { | ||||
| 	pos = kbest-1; | ||||
| 	values[pos] = val; | ||||
|     } | ||||
| #if KEEP_SAMPLES | ||||
|     samples[samplecount] = val; | ||||
| #endif | ||||
|     samplecount++; | ||||
|     /* Insertion sort */ | ||||
|     while (pos > 0 && values[pos-1] > values[pos]) { | ||||
| 	double temp = values[pos-1]; | ||||
| 	values[pos-1] = values[pos]; | ||||
| 	values[pos] = temp; | ||||
| 	pos--; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /* 
 | ||||
|  * has_converged- Have kbest minimum measurements converged within epsilon?  | ||||
|  */ | ||||
| static int has_converged() | ||||
| { | ||||
|     return | ||||
| 	(samplecount >= kbest) && | ||||
| 	((1 + epsilon)*values[0] >= values[kbest-1]); | ||||
| } | ||||
| 
 | ||||
| /* 
 | ||||
|  * clear - Code to clear cache  | ||||
|  */ | ||||
| static volatile int sink = 0; | ||||
| 
 | ||||
| static void clear() | ||||
| { | ||||
|     int x = sink; | ||||
|     int *cptr, *cend; | ||||
|     int incr = cache_block/sizeof(int); | ||||
|     if (!cache_buf) { | ||||
| 	cache_buf = malloc(cache_bytes); | ||||
| 	if (!cache_buf) { | ||||
| 	    fprintf(stderr, "Fatal error.  Malloc returned null when trying to clear cache\n"); | ||||
| 	    exit(1); | ||||
| 	} | ||||
|     } | ||||
|     cptr = (int *) cache_buf; | ||||
|     cend = cptr + cache_bytes/sizeof(int); | ||||
|     while (cptr < cend) { | ||||
| 	x += *cptr; | ||||
| 	cptr += incr; | ||||
|     } | ||||
|     sink = x; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * fcyc - Use K-best scheme to estimate the running time of function f | ||||
|  */ | ||||
| double fcyc(test_funct f, void *argp) | ||||
| { | ||||
|     double result; | ||||
|     init_sampler(); | ||||
|     if (compensate) { | ||||
| 	do { | ||||
| 	    double cyc; | ||||
| 	    if (clear_cache) | ||||
| 		clear(); | ||||
| 	    start_comp_counter(); | ||||
| 	    f(argp); | ||||
| 	    cyc = get_comp_counter(); | ||||
| 	    add_sample(cyc); | ||||
| 	} while (!has_converged() && samplecount < maxsamples); | ||||
|     } else { | ||||
| 	do { | ||||
| 	    double cyc; | ||||
| 	    if (clear_cache) | ||||
| 		clear(); | ||||
| 	    start_counter(); | ||||
| 	    f(argp); | ||||
| 	    cyc = get_counter(); | ||||
| 	    add_sample(cyc); | ||||
| 	} while (!has_converged() && samplecount < maxsamples); | ||||
|     } | ||||
| #ifdef DEBUG | ||||
|     { | ||||
| 	int i; | ||||
| 	printf(" %d smallest values: [", kbest); | ||||
| 	for (i = 0; i < kbest; i++) | ||||
| 	    printf("%.0f%s", values[i], i==kbest-1 ? "]\n" : ", "); | ||||
|     } | ||||
| #endif | ||||
|     result = values[0]; | ||||
| #if !KEEP_VALS | ||||
|     free(values);  | ||||
|     values = NULL; | ||||
| #endif | ||||
|     return result;   | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /*************************************************************
 | ||||
|  * Set the various parameters used by the measurement routines  | ||||
|  ************************************************************/ | ||||
| 
 | ||||
| /* 
 | ||||
|  * set_fcyc_clear_cache - When set, will run code to clear cache  | ||||
|  *     before each measurement.  | ||||
|  *     Default = 0 | ||||
|  */ | ||||
| void set_fcyc_clear_cache(int clear) | ||||
| { | ||||
|     clear_cache = clear; | ||||
| } | ||||
| 
 | ||||
| /* 
 | ||||
|  * set_fcyc_cache_size - Set size of cache to use when clearing cache  | ||||
|  *     Default = 1<<19 (512KB) | ||||
|  */ | ||||
| void set_fcyc_cache_size(int bytes) | ||||
| { | ||||
|     if (bytes != cache_bytes) { | ||||
| 	cache_bytes = bytes; | ||||
| 	if (cache_buf) { | ||||
| 	    free(cache_buf); | ||||
| 	    cache_buf = NULL; | ||||
| 	} | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /* 
 | ||||
|  * set_fcyc_cache_block - Set size of cache block  | ||||
|  *     Default = 32 | ||||
|  */ | ||||
| void set_fcyc_cache_block(int bytes) { | ||||
|     cache_block = bytes; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* 
 | ||||
|  * set_fcyc_compensate- When set, will attempt to compensate for  | ||||
|  *     timer interrupt overhead  | ||||
|  *     Default = 0 | ||||
|  */ | ||||
| void set_fcyc_compensate(int compensate_arg) | ||||
| { | ||||
|     compensate = compensate_arg; | ||||
| } | ||||
| 
 | ||||
| /* 
 | ||||
|  * set_fcyc_k - Value of K in K-best measurement scheme | ||||
|  *     Default = 3 | ||||
|  */ | ||||
| void set_fcyc_k(int k) | ||||
| { | ||||
|     kbest = k; | ||||
| } | ||||
| 
 | ||||
| /* 
 | ||||
|  * set_fcyc_maxsamples - Maximum number of samples attempting to find  | ||||
|  *     K-best within some tolerance. | ||||
|  *     When exceeded, just return best sample found. | ||||
|  *     Default = 20 | ||||
|  */ | ||||
| void set_fcyc_maxsamples(int maxsamples_arg) | ||||
| { | ||||
|     maxsamples = maxsamples_arg; | ||||
| } | ||||
| 
 | ||||
| /* 
 | ||||
|  * set_fcyc_epsilon - Tolerance required for K-best | ||||
|  *     Default = 0.01 | ||||
|  */ | ||||
| void set_fcyc_epsilon(double epsilon_arg) | ||||
| { | ||||
|     epsilon = epsilon_arg; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										68
									
								
								fcyc.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								fcyc.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,68 @@ | ||||
| /*
 | ||||
|  * fcyc.h - prototypes for the routines in fcyc.c that estimate the | ||||
|  *     time in CPU cycles used by a test function f | ||||
|  *  | ||||
|  * Copyright (c) 2002, R. Bryant and D. O'Hallaron, All rights reserved. | ||||
|  * May not be used, modified, or copied without permission. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| /* The test function takes a generic pointer as input */ | ||||
| typedef void (*test_funct)(void *); | ||||
| 
 | ||||
| /* Compute number of cycles used by test function f */ | ||||
| double fcyc(test_funct f, void* argp); | ||||
| 
 | ||||
| /*********************************************************
 | ||||
|  * Set the various parameters used by measurement routines  | ||||
|  *********************************************************/ | ||||
| 
 | ||||
| /* 
 | ||||
|  * set_fcyc_clear_cache - When set, will run code to clear cache  | ||||
|  *     before each measurement.  | ||||
|  *     Default = 0 | ||||
|  */ | ||||
| void set_fcyc_clear_cache(int clear); | ||||
| 
 | ||||
| /* 
 | ||||
|  * set_fcyc_cache_size - Set size of cache to use when clearing cache  | ||||
|  *     Default = 1<<19 (512KB) | ||||
|  */ | ||||
| void set_fcyc_cache_size(int bytes); | ||||
| 
 | ||||
| /* 
 | ||||
|  * set_fcyc_cache_block - Set size of cache block  | ||||
|  *     Default = 32 | ||||
|  */ | ||||
| void set_fcyc_cache_block(int bytes); | ||||
| 
 | ||||
| /* 
 | ||||
|  * set_fcyc_compensate- When set, will attempt to compensate for  | ||||
|  *     timer interrupt overhead  | ||||
|  *     Default = 0 | ||||
|  */ | ||||
| void set_fcyc_compensate(int compensate_arg); | ||||
| 
 | ||||
| /* 
 | ||||
|  * set_fcyc_k - Value of K in K-best measurement scheme | ||||
|  *     Default = 3 | ||||
|  */ | ||||
| void set_fcyc_k(int k); | ||||
| 
 | ||||
| /* 
 | ||||
|  * set_fcyc_maxsamples - Maximum number of samples attempting to find  | ||||
|  *     K-best within some tolerance. | ||||
|  *     When exceeded, just return best sample found. | ||||
|  *     Default = 20 | ||||
|  */ | ||||
| void set_fcyc_maxsamples(int maxsamples_arg); | ||||
| 
 | ||||
| /* 
 | ||||
|  * set_fcyc_epsilon - Tolerance required for K-best | ||||
|  *     Default = 0.01 | ||||
|  */ | ||||
| void set_fcyc_epsilon(double epsilon_arg); | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										57
									
								
								fsecs.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								fsecs.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,57 @@ | ||||
| /****************************
 | ||||
|  * High-level timing wrappers | ||||
|  ****************************/ | ||||
| #include <stdio.h> | ||||
| #include "fsecs.h" | ||||
| #include "fcyc.h" | ||||
| #include "clock.h" | ||||
| #include "ftimer.h" | ||||
| #include "config.h" | ||||
| 
 | ||||
| static double Mhz;  /* estimated CPU clock frequency */ | ||||
| 
 | ||||
| extern int verbose; /* -v option in mdriver.c */ | ||||
| 
 | ||||
| /*
 | ||||
|  * init_fsecs - initialize the timing package | ||||
|  */ | ||||
| void init_fsecs(void) | ||||
| { | ||||
|     Mhz = 0; /* keep gcc -Wall happy */ | ||||
| 
 | ||||
| #if USE_FCYC | ||||
|     if (verbose) | ||||
| 	printf("Measuring performance with a cycle counter.\n"); | ||||
| 
 | ||||
|     /* set key parameters for the fcyc package */ | ||||
|     set_fcyc_maxsamples(20);  | ||||
|     set_fcyc_clear_cache(1); | ||||
|     set_fcyc_compensate(1); | ||||
|     set_fcyc_epsilon(0.01); | ||||
|     set_fcyc_k(3); | ||||
|     Mhz = mhz(verbose > 0); | ||||
| #elif USE_ITIMER | ||||
|     if (verbose) | ||||
| 	printf("Measuring performance with the interval timer.\n"); | ||||
| #elif USE_GETTOD | ||||
|     if (verbose) | ||||
| 	printf("Measuring performance with gettimeofday().\n"); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * fsecs - Return the running time of a function f (in seconds) | ||||
|  */ | ||||
| double fsecs(fsecs_test_funct f, void *argp)  | ||||
| { | ||||
| #if USE_FCYC | ||||
|     double cycles = fcyc(f, argp); | ||||
|     return cycles/(Mhz*1e6); | ||||
| #elif USE_ITIMER | ||||
|     return ftimer_itimer(f, argp, 10); | ||||
| #elif USE_GETTOD | ||||
|     return ftimer_gettod(f, argp, 10); | ||||
| #endif  | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										4
									
								
								fsecs.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								fsecs.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | ||||
| typedef void (*fsecs_test_funct)(void *); | ||||
| 
 | ||||
| void init_fsecs(void); | ||||
| double fsecs(fsecs_test_funct f, void *argp); | ||||
							
								
								
									
										106
									
								
								ftimer.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								ftimer.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,106 @@ | ||||
| /*
 | ||||
|  * ftimer.c - Estimate the time (in seconds) used by a function f  | ||||
|  *  | ||||
|  * Copyright (c) 2002, R. Bryant and D. O'Hallaron, All rights reserved. | ||||
|  * May not be used, modified, or copied without permission. | ||||
|  * | ||||
|  * Function timers that estimate the running time (in seconds) of a function f. | ||||
|  *    ftimer_itimer: version that uses the interval timer | ||||
|  *    ftimer_gettod: version that uses gettimeofday | ||||
|  */ | ||||
| #include <stdio.h> | ||||
| #include <sys/time.h> | ||||
| #include "ftimer.h" | ||||
| 
 | ||||
| /* function prototypes */ | ||||
| static void init_etime(void); | ||||
| static double get_etime(void); | ||||
| 
 | ||||
| /* 
 | ||||
|  * ftimer_itimer - Use the interval timer to estimate the running time | ||||
|  * of f(argp). Return the average of n runs.   | ||||
|  */ | ||||
| double ftimer_itimer(ftimer_test_funct f, void *argp, int n) | ||||
| { | ||||
|     double start, tmeas; | ||||
|     int i; | ||||
| 
 | ||||
|     init_etime(); | ||||
|     start = get_etime(); | ||||
|     for (i = 0; i < n; i++)  | ||||
| 	f(argp); | ||||
|     tmeas = get_etime() - start; | ||||
|     return tmeas / n; | ||||
| } | ||||
| 
 | ||||
| /* 
 | ||||
|  * ftimer_gettod - Use gettimeofday to estimate the running time of | ||||
|  * f(argp). Return the average of n runs.   | ||||
|  */ | ||||
| double ftimer_gettod(ftimer_test_funct f, void *argp, int n) | ||||
| { | ||||
|     int i; | ||||
|     struct timeval stv, etv; | ||||
|     double diff; | ||||
| 
 | ||||
|     gettimeofday(&stv, NULL); | ||||
|     for (i = 0; i < n; i++)  | ||||
| 	f(argp); | ||||
|     gettimeofday(&etv,NULL); | ||||
|     diff = 1E3*(etv.tv_sec - stv.tv_sec) + 1E-3*(etv.tv_usec-stv.tv_usec); | ||||
|     diff /= n; | ||||
|     return (1E-3*diff); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * Routines for manipulating the Unix interval timer | ||||
|  */ | ||||
| 
 | ||||
| /* The initial value of the interval timer */ | ||||
| #define MAX_ETIME 86400    | ||||
| 
 | ||||
| /* static variables that hold the initial value of the interval timer */ | ||||
| static struct itimerval first_u; /* user time */ | ||||
| static struct itimerval first_r; /* real time */ | ||||
| static struct itimerval first_p; /* prof time*/ | ||||
| 
 | ||||
| /* init the timer */ | ||||
| static void init_etime(void) | ||||
| { | ||||
|     first_u.it_interval.tv_sec = 0; | ||||
|     first_u.it_interval.tv_usec = 0; | ||||
|     first_u.it_value.tv_sec = MAX_ETIME; | ||||
|     first_u.it_value.tv_usec = 0; | ||||
|     setitimer(ITIMER_VIRTUAL, &first_u, NULL); | ||||
| 
 | ||||
|     first_r.it_interval.tv_sec = 0; | ||||
|     first_r.it_interval.tv_usec = 0; | ||||
|     first_r.it_value.tv_sec = MAX_ETIME; | ||||
|     first_r.it_value.tv_usec = 0; | ||||
|     setitimer(ITIMER_REAL, &first_r, NULL); | ||||
|     | ||||
|     first_p.it_interval.tv_sec = 0; | ||||
|     first_p.it_interval.tv_usec = 0; | ||||
|     first_p.it_value.tv_sec = MAX_ETIME; | ||||
|     first_p.it_value.tv_usec = 0; | ||||
|     setitimer(ITIMER_PROF, &first_p, NULL); | ||||
| } | ||||
| 
 | ||||
| /* return elapsed real seconds since call to init_etime */ | ||||
| static double get_etime(void) { | ||||
|     struct itimerval v_curr; | ||||
|     struct itimerval r_curr; | ||||
|     struct itimerval p_curr; | ||||
| 
 | ||||
|     getitimer(ITIMER_VIRTUAL, &v_curr); | ||||
|     getitimer(ITIMER_REAL,&r_curr); | ||||
|     getitimer(ITIMER_PROF,&p_curr); | ||||
| 
 | ||||
|     return (double) ((first_p.it_value.tv_sec - r_curr.it_value.tv_sec) + | ||||
| 		     (first_p.it_value.tv_usec - r_curr.it_value.tv_usec)*1e-6); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										14
									
								
								ftimer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								ftimer.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | ||||
| /* 
 | ||||
|  * Function timers  | ||||
|  */ | ||||
| typedef void (*ftimer_test_funct)(void *);  | ||||
| 
 | ||||
| /* Estimate the running time of f(argp) using the Unix interval timer.
 | ||||
|    Return the average of n runs */ | ||||
| double ftimer_itimer(ftimer_test_funct f, void *argp, int n); | ||||
| 
 | ||||
| 
 | ||||
| /* Estimate the running time of f(argp) using gettimeofday 
 | ||||
|    Return the average of n runs */ | ||||
| double ftimer_gettod(ftimer_test_funct f, void *argp, int n); | ||||
| 
 | ||||
							
								
								
									
										532
									
								
								list.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										532
									
								
								list.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,532 @@ | ||||
| #include "list.h" | ||||
| #include <assert.h> | ||||
| 
 | ||||
| /* Our doubly linked lists have two header elements: the "head"
 | ||||
|    just before the first element and the "tail" just after the | ||||
|    last element.  The `prev' link of the front header is null, as | ||||
|    is the `next' link of the back header.  Their other two links | ||||
|    point toward each other via the interior elements of the list. | ||||
| 
 | ||||
|    An empty list looks like this: | ||||
| 
 | ||||
|                       +------+     +------+ | ||||
|                   <---| head |<--->| tail |---> | ||||
|                       +------+     +------+ | ||||
| 
 | ||||
|    A list with two elements in it looks like this: | ||||
| 
 | ||||
|         +------+     +-------+     +-------+     +------+ | ||||
|     <---| head |<--->|   1   |<--->|   2   |<--->| tail |<---> | ||||
|         +------+     +-------+     +-------+     +------+ | ||||
| 
 | ||||
|    The symmetry of this arrangement eliminates lots of special | ||||
|    cases in list processing.  For example, take a look at | ||||
|    list_remove(): it takes only two pointer assignments and no | ||||
|    conditionals.  That's a lot simpler than the code would be | ||||
|    without header elements. | ||||
| 
 | ||||
|    (Because only one of the pointers in each header element is used, | ||||
|    we could in fact combine them into a single header element | ||||
|    without sacrificing this simplicity.  But using two separate | ||||
|    elements allows us to do a little bit of checking on some | ||||
|    operations, which can be valuable.) */ | ||||
| 
 | ||||
| static bool is_sorted (struct list_elem *a, struct list_elem *b, | ||||
|                        list_less_func *less, void *aux); | ||||
| 
 | ||||
| /* Returns true if ELEM is a head, false otherwise. */ | ||||
| static inline bool | ||||
| is_head (struct list_elem *elem) | ||||
| { | ||||
|   return elem != NULL && elem->prev == NULL && elem->next != NULL; | ||||
| } | ||||
| 
 | ||||
| /* Returns true if ELEM is an interior element,
 | ||||
|    false otherwise. */ | ||||
| static inline bool | ||||
| is_interior (struct list_elem *elem) | ||||
| { | ||||
|   return elem != NULL && elem->prev != NULL && elem->next != NULL; | ||||
| } | ||||
| 
 | ||||
| /* Returns true if ELEM is a tail, false otherwise. */ | ||||
| static inline bool | ||||
| is_tail (struct list_elem *elem) | ||||
| { | ||||
|   return elem != NULL && elem->prev != NULL && elem->next == NULL; | ||||
| } | ||||
| 
 | ||||
| /* Initializes LIST as an empty list. */ | ||||
| void | ||||
| list_init (struct list *list) | ||||
| { | ||||
|   assert (list != NULL); | ||||
|   list->head.prev = NULL; | ||||
|   list->head.next = &list->tail; | ||||
|   list->tail.prev = &list->head; | ||||
|   list->tail.next = NULL; | ||||
| } | ||||
| 
 | ||||
| /* Returns the beginning of LIST.  */ | ||||
| struct list_elem * | ||||
| list_begin (struct list *list) | ||||
| { | ||||
|   assert (list != NULL); | ||||
|   return list->head.next; | ||||
| } | ||||
| 
 | ||||
| /* Returns the element after ELEM in its list.  If ELEM is the
 | ||||
|    last element in its list, returns the list tail.  Results are | ||||
|    undefined if ELEM is itself a list tail. */ | ||||
| struct list_elem * | ||||
| list_next (struct list_elem *elem) | ||||
| { | ||||
|   assert (is_head (elem) || is_interior (elem)); | ||||
|   return elem->next; | ||||
| } | ||||
| 
 | ||||
| /* Returns LIST's tail.
 | ||||
| 
 | ||||
|    list_end() is often used in iterating through a list from | ||||
|    front to back.  See the big comment at the top of list.h for | ||||
|    an example. */ | ||||
| struct list_elem * | ||||
| list_end (struct list *list) | ||||
| { | ||||
|   assert (list != NULL); | ||||
|   return &list->tail; | ||||
| } | ||||
| 
 | ||||
| /* Returns the LIST's reverse beginning, for iterating through
 | ||||
|    LIST in reverse order, from back to front. */ | ||||
| struct list_elem * | ||||
| list_rbegin (struct list *list)  | ||||
| { | ||||
|   assert (list != NULL); | ||||
|   return list->tail.prev; | ||||
| } | ||||
| 
 | ||||
| /* Returns the element before ELEM in its list.  If ELEM is the
 | ||||
|    first element in its list, returns the list head.  Results are | ||||
|    undefined if ELEM is itself a list head. */ | ||||
| struct list_elem * | ||||
| list_prev (struct list_elem *elem) | ||||
| { | ||||
|   assert (is_interior (elem) || is_tail (elem)); | ||||
|   return elem->prev; | ||||
| } | ||||
| 
 | ||||
| /* Returns LIST's head.
 | ||||
| 
 | ||||
|    list_rend() is often used in iterating through a list in | ||||
|    reverse order, from back to front.  Here's typical usage, | ||||
|    following the example from the top of list.h: | ||||
| 
 | ||||
|       for (e = list_rbegin (&foo_list); e != list_rend (&foo_list); | ||||
|            e = list_prev (e)) | ||||
|         { | ||||
|           struct foo *f = list_entry (e, struct foo, elem); | ||||
|           ...do something with f... | ||||
|         } | ||||
| */ | ||||
| struct list_elem * | ||||
| list_rend (struct list *list)  | ||||
| { | ||||
|   assert (list != NULL); | ||||
|   return &list->head; | ||||
| } | ||||
| 
 | ||||
| /* Return's LIST's head.
 | ||||
| 
 | ||||
|    list_head() can be used for an alternate style of iterating | ||||
|    through a list, e.g.: | ||||
| 
 | ||||
|       e = list_head (&list); | ||||
|       while ((e = list_next (e)) != list_end (&list))  | ||||
|         { | ||||
|           ... | ||||
|         } | ||||
| */ | ||||
| struct list_elem * | ||||
| list_head (struct list *list)  | ||||
| { | ||||
|   assert (list != NULL); | ||||
|   return &list->head; | ||||
| } | ||||
| 
 | ||||
| /* Return's LIST's tail. */ | ||||
| struct list_elem * | ||||
| list_tail (struct list *list)  | ||||
| { | ||||
|   assert (list != NULL); | ||||
|   return &list->tail; | ||||
| } | ||||
| 
 | ||||
| /* Inserts ELEM just before BEFORE, which may be either an
 | ||||
|    interior element or a tail.  The latter case is equivalent to | ||||
|    list_push_back(). */ | ||||
| void | ||||
| list_insert (struct list_elem *before, struct list_elem *elem) | ||||
| { | ||||
|   assert (is_interior (before) || is_tail (before)); | ||||
|   assert (elem != NULL); | ||||
| 
 | ||||
|   elem->prev = before->prev; | ||||
|   elem->next = before; | ||||
|   before->prev->next = elem; | ||||
|   before->prev = elem; | ||||
| } | ||||
| 
 | ||||
| /* Removes elements FIRST though LAST (exclusive) from their
 | ||||
|    current list, then inserts them just before BEFORE, which may | ||||
|    be either an interior element or a tail. */ | ||||
| void | ||||
| list_splice (struct list_elem *before, | ||||
|              struct list_elem *first, struct list_elem *last) | ||||
| { | ||||
|   assert (is_interior (before) || is_tail (before)); | ||||
|   if (first == last) | ||||
|     return; | ||||
|   last = list_prev (last); | ||||
| 
 | ||||
|   assert (is_interior (first)); | ||||
|   assert (is_interior (last)); | ||||
| 
 | ||||
|   /* Cleanly remove FIRST...LAST from its current list. */ | ||||
|   first->prev->next = last->next; | ||||
|   last->next->prev = first->prev; | ||||
| 
 | ||||
|   /* Splice FIRST...LAST into new list. */ | ||||
|   first->prev = before->prev; | ||||
|   last->next = before; | ||||
|   before->prev->next = first; | ||||
|   before->prev = last; | ||||
| } | ||||
| 
 | ||||
| /* Inserts ELEM at the beginning of LIST, so that it becomes the
 | ||||
|    front in LIST. */ | ||||
| void | ||||
| list_push_front (struct list *list, struct list_elem *elem) | ||||
| { | ||||
|   list_insert (list_begin (list), elem); | ||||
| } | ||||
| 
 | ||||
| /* Inserts ELEM at the end of LIST, so that it becomes the
 | ||||
|    back in LIST. */ | ||||
| void | ||||
| list_push_back (struct list *list, struct list_elem *elem) | ||||
| { | ||||
|   list_insert (list_end (list), elem); | ||||
| } | ||||
| 
 | ||||
| /* Removes ELEM from its list and returns the element that
 | ||||
|    followed it.  Undefined behavior if ELEM is not in a list. | ||||
| 
 | ||||
|    It's not safe to treat ELEM as an element in a list after | ||||
|    removing it.  In particular, using list_next() or list_prev() | ||||
|    on ELEM after removal yields undefined behavior.  This means | ||||
|    that a naive loop to remove the elements in a list will fail: | ||||
| 
 | ||||
|    ** DON'T DO THIS ** | ||||
|    for (e = list_begin (&list); e != list_end (&list); e = list_next (e)) | ||||
|      { | ||||
|        ...do something with e... | ||||
|        list_remove (e); | ||||
|      } | ||||
|    ** DON'T DO THIS ** | ||||
| 
 | ||||
|    Here is one correct way to iterate and remove elements from a | ||||
|    list: | ||||
| 
 | ||||
|    for (e = list_begin (&list); e != list_end (&list); e = list_remove (e)) | ||||
|      { | ||||
|        ...do something with e... | ||||
|      } | ||||
| 
 | ||||
|    If you need to free() elements of the list then you need to be | ||||
|    more conservative.  Here's an alternate strategy that works | ||||
|    even in that case: | ||||
| 
 | ||||
|    while (!list_empty (&list)) | ||||
|      { | ||||
|        struct list_elem *e = list_pop_front (&list); | ||||
|        ...do something with e... | ||||
|      } | ||||
| */ | ||||
| struct list_elem * | ||||
| list_remove (struct list_elem *elem) | ||||
| { | ||||
|   assert (is_interior (elem)); | ||||
|   elem->prev->next = elem->next; | ||||
|   elem->next->prev = elem->prev; | ||||
|   return elem->next; | ||||
| } | ||||
| 
 | ||||
| /* Removes the front element from LIST and returns it.
 | ||||
|    Undefined behavior if LIST is empty before removal. */ | ||||
| struct list_elem * | ||||
| list_pop_front (struct list *list) | ||||
| { | ||||
|   struct list_elem *front = list_front (list); | ||||
|   list_remove (front); | ||||
|   return front; | ||||
| } | ||||
| 
 | ||||
| /* Removes the back element from LIST and returns it.
 | ||||
|    Undefined behavior if LIST is empty before removal. */ | ||||
| struct list_elem * | ||||
| list_pop_back (struct list *list) | ||||
| { | ||||
|   struct list_elem *back = list_back (list); | ||||
|   list_remove (back); | ||||
|   return back; | ||||
| } | ||||
| 
 | ||||
| /* Returns the front element in LIST.
 | ||||
|    Undefined behavior if LIST is empty. */ | ||||
| struct list_elem * | ||||
| list_front (struct list *list) | ||||
| { | ||||
|   assert (!list_empty (list)); | ||||
|   return list->head.next; | ||||
| } | ||||
| 
 | ||||
| /* Returns the back element in LIST.
 | ||||
|    Undefined behavior if LIST is empty. */ | ||||
| struct list_elem * | ||||
| list_back (struct list *list) | ||||
| { | ||||
|   assert (!list_empty (list)); | ||||
|   return list->tail.prev; | ||||
| } | ||||
| 
 | ||||
| /* Returns the number of elements in LIST.
 | ||||
|    Runs in O(n) in the number of elements. */ | ||||
| size_t | ||||
| list_size (struct list *list) | ||||
| { | ||||
|   struct list_elem *e; | ||||
|   size_t cnt = 0; | ||||
| 
 | ||||
|   for (e = list_begin (list); e != list_end (list); e = list_next (e)) | ||||
|     cnt++; | ||||
|   return cnt; | ||||
| } | ||||
| 
 | ||||
| /* Returns true if LIST is empty, false otherwise. */ | ||||
| bool | ||||
| list_empty (struct list *list) | ||||
| { | ||||
|   return list_begin (list) == list_end (list); | ||||
| } | ||||
| 
 | ||||
| /* Swaps the `struct list_elem *'s that A and B point to. */ | ||||
| static void | ||||
| swap (struct list_elem **a, struct list_elem **b)  | ||||
| { | ||||
|   struct list_elem *t = *a; | ||||
|   *a = *b; | ||||
|   *b = t; | ||||
| } | ||||
| 
 | ||||
| /* Reverses the order of LIST. */ | ||||
| void | ||||
| list_reverse (struct list *list) | ||||
| { | ||||
|   if (!list_empty (list))  | ||||
|     { | ||||
|       struct list_elem *e; | ||||
| 
 | ||||
|       for (e = list_begin (list); e != list_end (list); e = e->prev) | ||||
|         swap (&e->prev, &e->next); | ||||
|       swap (&list->head.next, &list->tail.prev); | ||||
|       swap (&list->head.next->prev, &list->tail.prev->next); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /* Returns true only if the list elements A through B (exclusive)
 | ||||
|    are in order according to LESS given auxiliary data AUX. */ | ||||
| static bool | ||||
| is_sorted (struct list_elem *a, struct list_elem *b, | ||||
|            list_less_func *less, void *aux) | ||||
| { | ||||
|   if (a != b) | ||||
|     while ((a = list_next (a)) != b)  | ||||
|       if (less (a, list_prev (a), aux)) | ||||
|         return false; | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
| /* Finds a run, starting at A and ending not after B, of list
 | ||||
|    elements that are in nondecreasing order according to LESS | ||||
|    given auxiliary data AUX.  Returns the (exclusive) end of the | ||||
|    run. | ||||
|    A through B (exclusive) must form a non-empty range. */ | ||||
| static struct list_elem * | ||||
| find_end_of_run (struct list_elem *a, struct list_elem *b, | ||||
|                  list_less_func *less, void *aux) | ||||
| { | ||||
|   assert (a != NULL); | ||||
|   assert (b != NULL); | ||||
|   assert (less != NULL); | ||||
|   assert (a != b); | ||||
|    | ||||
|   do  | ||||
|     { | ||||
|       a = list_next (a); | ||||
|     } | ||||
|   while (a != b && !less (a, list_prev (a), aux)); | ||||
|   return a; | ||||
| } | ||||
| 
 | ||||
| /* Merges A0 through A1B0 (exclusive) with A1B0 through B1
 | ||||
|    (exclusive) to form a combined range also ending at B1 | ||||
|    (exclusive).  Both input ranges must be nonempty and sorted in | ||||
|    nondecreasing order according to LESS given auxiliary data | ||||
|    AUX.  The output range will be sorted the same way. */ | ||||
| static void | ||||
| inplace_merge (struct list_elem *a0, struct list_elem *a1b0, | ||||
|                struct list_elem *b1, | ||||
|                list_less_func *less, void *aux) | ||||
| { | ||||
|   assert (a0 != NULL); | ||||
|   assert (a1b0 != NULL); | ||||
|   assert (b1 != NULL); | ||||
|   assert (less != NULL); | ||||
|   assert (is_sorted (a0, a1b0, less, aux)); | ||||
|   assert (is_sorted (a1b0, b1, less, aux)); | ||||
| 
 | ||||
|   while (a0 != a1b0 && a1b0 != b1) | ||||
|     if (!less (a1b0, a0, aux))  | ||||
|       a0 = list_next (a0); | ||||
|     else  | ||||
|       { | ||||
|         a1b0 = list_next (a1b0); | ||||
|         list_splice (a0, list_prev (a1b0), a1b0); | ||||
|       } | ||||
| } | ||||
| 
 | ||||
| /* Sorts LIST according to LESS given auxiliary data AUX, using a
 | ||||
|    natural iterative merge sort that runs in O(n lg n) time and | ||||
|    O(1) space in the number of elements in LIST. */ | ||||
| void | ||||
| list_sort (struct list *list, list_less_func *less, void *aux) | ||||
| { | ||||
|   size_t output_run_cnt;        /* Number of runs output in current pass. */ | ||||
| 
 | ||||
|   assert (list != NULL); | ||||
|   assert (less != NULL); | ||||
| 
 | ||||
|   /* Pass over the list repeatedly, merging adjacent runs of
 | ||||
|      nondecreasing elements, until only one run is left. */ | ||||
|   do | ||||
|     { | ||||
|       struct list_elem *a0;     /* Start of first run. */ | ||||
|       struct list_elem *a1b0;   /* End of first run, start of second. */ | ||||
|       struct list_elem *b1;     /* End of second run. */ | ||||
| 
 | ||||
|       output_run_cnt = 0; | ||||
|       for (a0 = list_begin (list); a0 != list_end (list); a0 = b1) | ||||
|         { | ||||
|           /* Each iteration produces one output run. */ | ||||
|           output_run_cnt++; | ||||
| 
 | ||||
|           /* Locate two adjacent runs of nondecreasing elements
 | ||||
|              A0...A1B0 and A1B0...B1. */ | ||||
|           a1b0 = find_end_of_run (a0, list_end (list), less, aux); | ||||
|           if (a1b0 == list_end (list)) | ||||
|             break; | ||||
|           b1 = find_end_of_run (a1b0, list_end (list), less, aux); | ||||
| 
 | ||||
|           /* Merge the runs. */ | ||||
|           inplace_merge (a0, a1b0, b1, less, aux); | ||||
|         } | ||||
|     } | ||||
|   while (output_run_cnt > 1); | ||||
| 
 | ||||
|   assert (is_sorted (list_begin (list), list_end (list), less, aux)); | ||||
| } | ||||
| 
 | ||||
| /* Inserts ELEM in the proper position in LIST, which must be
 | ||||
|    sorted according to LESS given auxiliary data AUX. | ||||
|    Runs in O(n) average case in the number of elements in LIST. */ | ||||
| void | ||||
| list_insert_ordered (struct list *list, struct list_elem *elem, | ||||
|                      list_less_func *less, void *aux) | ||||
| { | ||||
|   struct list_elem *e; | ||||
| 
 | ||||
|   assert (list != NULL); | ||||
|   assert (elem != NULL); | ||||
|   assert (less != NULL); | ||||
| 
 | ||||
|   for (e = list_begin (list); e != list_end (list); e = list_next (e)) | ||||
|     if (less (elem, e, aux)) | ||||
|       break; | ||||
|   return list_insert (e, elem); | ||||
| } | ||||
| 
 | ||||
| /* Iterates through LIST and removes all but the first in each
 | ||||
|    set of adjacent elements that are equal according to LESS | ||||
|    given auxiliary data AUX.  If DUPLICATES is non-null, then the | ||||
|    elements from LIST are appended to DUPLICATES. */ | ||||
| void | ||||
| list_unique (struct list *list, struct list *duplicates, | ||||
|              list_less_func *less, void *aux) | ||||
| { | ||||
|   struct list_elem *elem, *next; | ||||
| 
 | ||||
|   assert (list != NULL); | ||||
|   assert (less != NULL); | ||||
|   if (list_empty (list)) | ||||
|     return; | ||||
| 
 | ||||
|   elem = list_begin (list); | ||||
|   while ((next = list_next (elem)) != list_end (list)) | ||||
|     if (!less (elem, next, aux) && !less (next, elem, aux))  | ||||
|       { | ||||
|         list_remove (next); | ||||
|         if (duplicates != NULL) | ||||
|           list_push_back (duplicates, next); | ||||
|       } | ||||
|     else | ||||
|       elem = next; | ||||
| } | ||||
| 
 | ||||
| /* Returns the element in LIST with the largest value according
 | ||||
|    to LESS given auxiliary data AUX.  If there is more than one | ||||
|    maximum, returns the one that appears earlier in the list.  If | ||||
|    the list is empty, returns its tail. */ | ||||
| struct list_elem * | ||||
| list_max (struct list *list, list_less_func *less, void *aux) | ||||
| { | ||||
|   struct list_elem *max = list_begin (list); | ||||
|   if (max != list_end (list))  | ||||
|     { | ||||
|       struct list_elem *e; | ||||
|        | ||||
|       for (e = list_next (max); e != list_end (list); e = list_next (e)) | ||||
|         if (less (max, e, aux)) | ||||
|           max = e;  | ||||
|     } | ||||
|   return max; | ||||
| } | ||||
| 
 | ||||
| /* Returns the element in LIST with the smallest value according
 | ||||
|    to LESS given auxiliary data AUX.  If there is more than one | ||||
|    minimum, returns the one that appears earlier in the list.  If | ||||
|    the list is empty, returns its tail. */ | ||||
| struct list_elem * | ||||
| list_min (struct list *list, list_less_func *less, void *aux) | ||||
| { | ||||
|   struct list_elem *min = list_begin (list); | ||||
|   if (min != list_end (list))  | ||||
|     { | ||||
|       struct list_elem *e; | ||||
|        | ||||
|       for (e = list_next (min); e != list_end (list); e = list_next (e)) | ||||
|         if (less (e, min, aux)) | ||||
|           min = e;  | ||||
|     } | ||||
|   return min; | ||||
| } | ||||
							
								
								
									
										170
									
								
								list.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										170
									
								
								list.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,170 @@ | ||||
| #ifndef __LIST_H | ||||
| #define __LIST_H | ||||
| /* This code is taken from the Pintos education OS.
 | ||||
|  * For copyright information, see www.pintos-os.org */ | ||||
| 
 | ||||
| /* Doubly linked list.
 | ||||
| 
 | ||||
|    This implementation of a doubly linked list does not require | ||||
|    use of dynamically allocated memory.  Instead, each structure | ||||
|    that is a potential list element must embed a struct list_elem | ||||
|    member.  All of the list functions operate on these `struct | ||||
|    list_elem's.  The list_entry macro allows conversion from a | ||||
|    struct list_elem back to a structure object that contains it. | ||||
| 
 | ||||
|    For example, suppose there is a needed for a list of `struct | ||||
|    foo'.  `struct foo' should contain a `struct list_elem' | ||||
|    member, like so: | ||||
| 
 | ||||
|       struct foo | ||||
|         { | ||||
|           struct list_elem elem; | ||||
|           int bar; | ||||
|           ...other members... | ||||
|         }; | ||||
| 
 | ||||
|    Then a list of `struct foo' can be be declared and initialized | ||||
|    like so: | ||||
| 
 | ||||
|       struct list foo_list; | ||||
| 
 | ||||
|       list_init (&foo_list); | ||||
| 
 | ||||
|    Iteration is a typical situation where it is necessary to | ||||
|    convert from a struct list_elem back to its enclosing | ||||
|    structure.  Here's an example using foo_list: | ||||
| 
 | ||||
|       struct list_elem *e; | ||||
| 
 | ||||
|       for (e = list_begin (&foo_list); e != list_end (&foo_list); | ||||
|            e = list_next (e)) | ||||
|         { | ||||
|           struct foo *f = list_entry (e, struct foo, elem); | ||||
|           ...do something with f... | ||||
|         } | ||||
| 
 | ||||
|    You can find real examples of list usage throughout the | ||||
|    source; for example, malloc.c, palloc.c, and thread.c in the | ||||
|    threads directory all use lists. | ||||
| 
 | ||||
|    The interface for this list is inspired by the list<> template | ||||
|    in the C++ STL.  If you're familiar with list<>, you should | ||||
|    find this easy to use.  However, it should be emphasized that | ||||
|    these lists do *no* type checking and can't do much other | ||||
|    correctness checking.  If you screw up, it will bite you. | ||||
| 
 | ||||
|    Glossary of list terms: | ||||
| 
 | ||||
|      - "front": The first element in a list.  Undefined in an | ||||
|        empty list.  Returned by list_front(). | ||||
| 
 | ||||
|      - "back": The last element in a list.  Undefined in an empty | ||||
|        list.  Returned by list_back(). | ||||
| 
 | ||||
|      - "tail": The element figuratively just after the last | ||||
|        element of a list.  Well defined even in an empty list. | ||||
|        Returned by list_end().  Used as the end sentinel for an | ||||
|        iteration from front to back. | ||||
| 
 | ||||
|      - "beginning": In a non-empty list, the front.  In an empty | ||||
|        list, the tail.  Returned by list_begin().  Used as the | ||||
|        starting point for an iteration from front to back. | ||||
| 
 | ||||
|      - "head": The element figuratively just before the first | ||||
|        element of a list.  Well defined even in an empty list. | ||||
|        Returned by list_rend().  Used as the end sentinel for an | ||||
|        iteration from back to front. | ||||
| 
 | ||||
|      - "reverse beginning": In a non-empty list, the back.  In an | ||||
|        empty list, the head.  Returned by list_rbegin().  Used as | ||||
|        the starting point for an iteration from back to front. | ||||
| 
 | ||||
|      - "interior element": An element that is not the head or | ||||
|        tail, that is, a real list element.  An empty list does | ||||
|        not have any interior elements. | ||||
| */ | ||||
| 
 | ||||
| #include <stdbool.h> | ||||
| #include <stddef.h> | ||||
| #include <stdint.h> | ||||
| 
 | ||||
| /* List element. */ | ||||
| struct list_elem  | ||||
|   { | ||||
|     struct list_elem *prev;     /* Previous list element. */ | ||||
|     struct list_elem *next;     /* Next list element. */ | ||||
|   }; | ||||
| 
 | ||||
| /* List. */ | ||||
| struct list  | ||||
|   { | ||||
|     struct list_elem head;      /* List head. */ | ||||
|     struct list_elem tail;      /* List tail. */ | ||||
|   }; | ||||
| 
 | ||||
| /* Converts pointer to list element LIST_ELEM into a pointer to
 | ||||
|    the structure that LIST_ELEM is embedded inside.  Supply the | ||||
|    name of the outer structure STRUCT and the member name MEMBER | ||||
|    of the list element.  See the big comment at the top of the | ||||
|    file for an example. */ | ||||
| #define list_entry(LIST_ELEM, STRUCT, MEMBER)           \ | ||||
|         ((STRUCT *) ((uint8_t *) &(LIST_ELEM)->next     \ | ||||
|                      - offsetof (STRUCT, MEMBER.next))) | ||||
| 
 | ||||
| void list_init (struct list *); | ||||
| 
 | ||||
| /* List traversal. */ | ||||
| struct list_elem *list_begin (struct list *); | ||||
| struct list_elem *list_next (struct list_elem *); | ||||
| struct list_elem *list_end (struct list *); | ||||
| 
 | ||||
| struct list_elem *list_rbegin (struct list *); | ||||
| struct list_elem *list_prev (struct list_elem *); | ||||
| struct list_elem *list_rend (struct list *); | ||||
| 
 | ||||
| struct list_elem *list_head (struct list *); | ||||
| struct list_elem *list_tail (struct list *); | ||||
| 
 | ||||
| /* List insertion. */ | ||||
| void list_insert (struct list_elem *, struct list_elem *); | ||||
| void list_splice (struct list_elem *before, | ||||
|                   struct list_elem *first, struct list_elem *last); | ||||
| void list_push_front (struct list *, struct list_elem *); | ||||
| void list_push_back (struct list *, struct list_elem *); | ||||
| 
 | ||||
| /* List removal. */ | ||||
| struct list_elem *list_remove (struct list_elem *); | ||||
| struct list_elem *list_pop_front (struct list *); | ||||
| struct list_elem *list_pop_back (struct list *); | ||||
| 
 | ||||
| /* List elements. */ | ||||
| struct list_elem *list_front (struct list *); | ||||
| struct list_elem *list_back (struct list *); | ||||
| 
 | ||||
| /* List properties. */ | ||||
| size_t list_size (struct list *); | ||||
| bool list_empty (struct list *); | ||||
| 
 | ||||
| /* Miscellaneous. */ | ||||
| void list_reverse (struct list *); | ||||
|  | ||||
| /* Compares the value of two list elements A and B, given
 | ||||
|    auxiliary data AUX.  Returns true if A is less than B, or | ||||
|    false if A is greater than or equal to B. */ | ||||
| typedef bool list_less_func (const struct list_elem *a, | ||||
|                              const struct list_elem *b, | ||||
|                              void *aux); | ||||
| 
 | ||||
| /* Operations on lists with ordered elements. */ | ||||
| void list_sort (struct list *, | ||||
|                 list_less_func *, void *aux); | ||||
| void list_insert_ordered (struct list *, struct list_elem *, | ||||
|                           list_less_func *, void *aux); | ||||
| void list_unique (struct list *, struct list *duplicates, | ||||
|                   list_less_func *, void *aux); | ||||
| 
 | ||||
| /* Max and min. */ | ||||
| struct list_elem *list_max (struct list *, list_less_func *, void *aux); | ||||
| struct list_elem *list_min (struct list *, list_less_func *, void *aux); | ||||
| 
 | ||||
| #endif /* list.h */ | ||||
							
								
								
									
										126
									
								
								memlib.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								memlib.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,126 @@ | ||||
| /*
 | ||||
|  * memlib.c - a module that simulates the memory system.  Needed because it  | ||||
|  *            allows us to interleave calls from the student's malloc package  | ||||
|  *            with the system's malloc package in libc. | ||||
|  */ | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <assert.h> | ||||
| #include <unistd.h> | ||||
| #include <sys/mman.h> | ||||
| #include <string.h> | ||||
| #include <errno.h> | ||||
| 
 | ||||
| #include "memlib.h" | ||||
| #include "config.h" | ||||
| 
 | ||||
| /* private variables */ | ||||
| static char *mem_start_brk;  /* points to first byte of heap */ | ||||
| static char *mem_brk;        /* points to last byte of heap */ | ||||
| static char *mem_max_addr;   /* largest legal heap address */  | ||||
| static int use_mmap;         /* Use mmap instead of malloc */ | ||||
| static void * mmap_addr = (void *)0x58000000; | ||||
| 
 | ||||
| /* 
 | ||||
|  * mem_init - initialize the memory system model | ||||
|  */ | ||||
| void mem_init(int _use_mmap) | ||||
| { | ||||
|     use_mmap = _use_mmap; | ||||
| 
 | ||||
|     /* allocate the storage we will use to model the available VM */ | ||||
|     if (use_mmap) { | ||||
|         mem_start_brk = (char *)mmap(mmap_addr, MAX_HEAP, PROT_READ|PROT_WRITE,  | ||||
|                                  MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); | ||||
|         if (mem_start_brk == MAP_FAILED) { | ||||
|             perror("mem_init_vm: mmap error:"); | ||||
|             exit(1); | ||||
|         } | ||||
|         if (mem_start_brk != mmap_addr) { | ||||
|             perror("mmap"); | ||||
|             fprintf(stderr,  | ||||
|                 "mem_init_vm: could not obtain memory at address %p\n",  | ||||
|                 mmap_addr); | ||||
|             exit(1); | ||||
|         } | ||||
|     } else { | ||||
|         if ((mem_start_brk = (char *)malloc(MAX_HEAP)) == NULL) { | ||||
|             fprintf(stderr, "mem_init_vm: malloc error\n"); | ||||
|             exit(1); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     mem_max_addr = mem_start_brk + MAX_HEAP;  /* max legal heap address */ | ||||
|     mem_brk = mem_start_brk;                  /* heap is empty initially */ | ||||
| } | ||||
| 
 | ||||
| /* 
 | ||||
|  * mem_deinit - free the storage used by the memory system model | ||||
|  */ | ||||
| void mem_deinit(void) | ||||
| { | ||||
|     if (use_mmap) { | ||||
|         if (munmap(mem_start_brk, MAX_HEAP)) | ||||
|             perror("munmap"); | ||||
|     } else { | ||||
|         free(mem_start_brk); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * mem_reset_brk - reset the simulated brk pointer to make an empty heap | ||||
|  */ | ||||
| void mem_reset_brk() | ||||
| { | ||||
|     mem_brk = mem_start_brk; | ||||
| } | ||||
| 
 | ||||
| /* 
 | ||||
|  * mem_sbrk - simple model of the sbrk function. Extends the heap  | ||||
|  *    by incr bytes and returns the start address of the new area. In | ||||
|  *    this model, the heap cannot be shrunk. | ||||
|  */ | ||||
| void *mem_sbrk(int incr)  | ||||
| { | ||||
|     char *old_brk = mem_brk; | ||||
| 
 | ||||
|     if ( (incr < 0) || ((mem_brk + incr) > mem_max_addr)) { | ||||
| 	errno = ENOMEM; | ||||
| 	fprintf(stderr, "ERROR: mem_sbrk failed. Ran out of memory...\n"); | ||||
| 	return NULL; | ||||
|     } | ||||
|     mem_brk += incr; | ||||
|     return (void *)old_brk; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * mem_heap_lo - return address of the first heap byte | ||||
|  */ | ||||
| void *mem_heap_lo() | ||||
| { | ||||
|     return (void *)mem_start_brk; | ||||
| } | ||||
| 
 | ||||
| /* 
 | ||||
|  * mem_heap_hi - return address of last heap byte | ||||
|  */ | ||||
| void *mem_heap_hi() | ||||
| { | ||||
|     return (void *)(mem_brk - 1); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * mem_heapsize() - returns the heap size in bytes | ||||
|  */ | ||||
| size_t mem_heapsize()  | ||||
| { | ||||
|     return (size_t)(mem_brk - mem_start_brk); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * mem_pagesize() - returns the page size of the system | ||||
|  */ | ||||
| size_t mem_pagesize() | ||||
| { | ||||
|     return (size_t)getpagesize(); | ||||
| } | ||||
							
								
								
									
										11
									
								
								memlib.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								memlib.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | ||||
| #include <unistd.h> | ||||
| 
 | ||||
| void mem_init(int use_mmap);                | ||||
| void mem_deinit(void); | ||||
| void *mem_sbrk(int incr); | ||||
| void mem_reset_brk(void);  | ||||
| void *mem_heap_lo(void); | ||||
| void *mem_heap_hi(void); | ||||
| size_t mem_heapsize(void); | ||||
| size_t mem_pagesize(void); | ||||
| 
 | ||||
							
								
								
									
										400
									
								
								mm-book-implicit.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										400
									
								
								mm-book-implicit.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,400 @@ | ||||
| /* 
 | ||||
|  * Simple, 32-bit and 64-bit clean allocator based on implicit free | ||||
|  * lists, first fit placement, and boundary tag coalescing, as described | ||||
|  * in the CS:APP2e text. Blocks must be aligned to doubleword (8 byte)  | ||||
|  * boundaries. Minimum block size is 16 bytes.  | ||||
|  */ | ||||
| #include <stdio.h> | ||||
| #include <string.h> | ||||
| #include <stdlib.h> | ||||
| 
 | ||||
| #include "mm.h" | ||||
| #include "memlib.h" | ||||
| 
 | ||||
| /*
 | ||||
|  * If NEXT_FIT defined use next fit search, else use first fit search  | ||||
|  */ | ||||
| #define NEXT_FITx | ||||
| 
 | ||||
| /* $begin mallocmacros */ | ||||
| /* Basic constants and macros */ | ||||
| #define WSIZE       4       /* Word and header/footer size (bytes) */ //line:vm:mm:beginconst
 | ||||
| #define DSIZE       8       /* Doubleword size (bytes) */ | ||||
| #define CHUNKSIZE  (1<<12)  /* Extend heap by this amount (bytes) */  //line:vm:mm:endconst 
 | ||||
| 
 | ||||
| #define MAX(x, y) ((x) > (y)? (x) : (y))   | ||||
| 
 | ||||
| /* Pack a size and allocated bit into a word */ | ||||
| #define PACK(size, alloc)  ((size) | (alloc)) //line:vm:mm:pack
 | ||||
| 
 | ||||
| /* Read and write a word at address p */ | ||||
| #define GET(p)       (*(unsigned int *)(p))            //line:vm:mm:get
 | ||||
| #define PUT(p, val)  (*(unsigned int *)(p) = (val))    //line:vm:mm:put
 | ||||
| 
 | ||||
| /* Read the size and allocated fields from address p */ | ||||
| #define GET_SIZE(p)  (GET(p) & ~0x7)                   //line:vm:mm:getsize
 | ||||
| #define GET_ALLOC(p) (GET(p) & 0x1)                    //line:vm:mm:getalloc
 | ||||
| 
 | ||||
| /* Given block ptr bp, compute address of its header and footer */ | ||||
| #define HDRP(bp)       ((char *)(bp) - WSIZE)                      //line:vm:mm:hdrp
 | ||||
| #define FTRP(bp)       ((char *)(bp) + GET_SIZE(HDRP(bp)) - DSIZE) //line:vm:mm:ftrp
 | ||||
| 
 | ||||
| /* Given block ptr bp, compute address of next and previous blocks */ | ||||
| #define NEXT_BLKP(bp)  ((char *)(bp) + GET_SIZE(((char *)(bp) - WSIZE))) //line:vm:mm:nextblkp
 | ||||
| #define PREV_BLKP(bp)  ((char *)(bp) - GET_SIZE(((char *)(bp) - DSIZE))) //line:vm:mm:prevblkp
 | ||||
| /* $end mallocmacros */ | ||||
| 
 | ||||
| /* Global variables */ | ||||
| static char *heap_listp = 0;  /* Pointer to first block */   | ||||
| #ifdef NEXT_FIT | ||||
| static char *rover;           /* Next fit rover */ | ||||
| #endif | ||||
| 
 | ||||
| /* Function prototypes for internal helper routines */ | ||||
| static void *extend_heap(size_t words); | ||||
| static void place(void *bp, size_t asize); | ||||
| static void *find_fit(size_t asize); | ||||
| static void *coalesce(void *bp); | ||||
| static void printblock(void *bp);  | ||||
| static void checkheap(int verbose); | ||||
| static void checkblock(void *bp); | ||||
| 
 | ||||
| /* 
 | ||||
|  * mm_init - Initialize the memory manager  | ||||
|  */ | ||||
| /* $begin mminit */ | ||||
| int mm_init(void)  | ||||
| { | ||||
|     /* Create the initial empty heap */ | ||||
|     if ((heap_listp = mem_sbrk(4*WSIZE)) == (void *)-1) //line:vm:mm:begininit
 | ||||
| 	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);                     //line:vm:mm:endinit  
 | ||||
| /* $end mminit */ | ||||
| 
 | ||||
| #ifdef NEXT_FIT | ||||
|     rover = heap_listp; | ||||
| #endif | ||||
| /* $begin mminit */ | ||||
| 
 | ||||
|     /* Extend the empty heap with a free block of CHUNKSIZE bytes */ | ||||
|     if (extend_heap(CHUNKSIZE/WSIZE) == NULL)  | ||||
| 	return -1; | ||||
|     return 0; | ||||
| } | ||||
| /* $end mminit */ | ||||
| 
 | ||||
| /* 
 | ||||
|  * mm_malloc - Allocate a block with at least size bytes of payload  | ||||
|  */ | ||||
| /* $begin mmmalloc */ | ||||
| void *mm_malloc(size_t size)  | ||||
| { | ||||
|     size_t asize;      /* Adjusted block size */ | ||||
|     size_t extendsize; /* Amount to extend heap if no fit */ | ||||
|     char *bp;       | ||||
| 
 | ||||
| /* $end mmmalloc */ | ||||
|     if (heap_listp == 0){ | ||||
| 	mm_init(); | ||||
|     } | ||||
| /* $begin mmmalloc */ | ||||
|     /* Ignore spurious requests */ | ||||
|     if (size == 0) | ||||
| 	return NULL; | ||||
| 
 | ||||
|     /* Adjust block size to include overhead and alignment reqs. */ | ||||
|     if (size <= DSIZE)                                          //line:vm:mm:sizeadjust1
 | ||||
| 	asize = 2*DSIZE;                                        //line:vm:mm:sizeadjust2
 | ||||
|     else | ||||
| 	asize = DSIZE * ((size + (DSIZE) + (DSIZE-1)) / DSIZE); //line:vm:mm:sizeadjust3
 | ||||
| 
 | ||||
|     /* Search the free list for a fit */ | ||||
|     if ((bp = find_fit(asize)) != NULL) {  //line:vm:mm:findfitcall
 | ||||
| 	place(bp, asize);                  //line:vm:mm:findfitplace
 | ||||
| 	return bp; | ||||
|     } | ||||
| 
 | ||||
|     /* No fit found. Get more memory and place the block */ | ||||
|     extendsize = MAX(asize,CHUNKSIZE);                 //line:vm:mm:growheap1
 | ||||
|     if ((bp = extend_heap(extendsize/WSIZE)) == NULL)   | ||||
| 	return NULL;                                  //line:vm:mm:growheap2
 | ||||
|     place(bp, asize);                                 //line:vm:mm:growheap3
 | ||||
|     return bp; | ||||
| }  | ||||
| /* $end mmmalloc */ | ||||
| 
 | ||||
| /* 
 | ||||
|  * mm_free - Free a block  | ||||
|  */ | ||||
| /* $begin mmfree */ | ||||
| void mm_free(void *bp) | ||||
| { | ||||
| /* $end mmfree */ | ||||
|     if(bp == 0)  | ||||
| 	return; | ||||
| 
 | ||||
| /* $begin mmfree */ | ||||
|     size_t size = GET_SIZE(HDRP(bp)); | ||||
| /* $end mmfree */ | ||||
|     if (heap_listp == 0){ | ||||
| 	mm_init(); | ||||
|     } | ||||
| /* $begin mmfree */ | ||||
| 
 | ||||
|     PUT(HDRP(bp), PACK(size, 0)); | ||||
|     PUT(FTRP(bp), PACK(size, 0)); | ||||
|     coalesce(bp); | ||||
| } | ||||
| 
 | ||||
| /* $end mmfree */ | ||||
| /*
 | ||||
|  * coalesce - Boundary tag coalescing. Return ptr to coalesced block | ||||
|  */ | ||||
| /* $begin mmfree */ | ||||
| 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) {            /* Case 1 */ | ||||
| 	return bp; | ||||
|     } | ||||
| 
 | ||||
|     else if (prev_alloc && !next_alloc) {      /* Case 2 */ | ||||
| 	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) {      /* Case 3 */ | ||||
| 	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 {                                     /* Case 4 */ | ||||
| 	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); | ||||
|     } | ||||
| /* $end mmfree */ | ||||
| #ifdef NEXT_FIT | ||||
|     /* Make sure the rover isn't pointing into the free block */ | ||||
|     /* that we just coalesced */ | ||||
|     if ((rover > (char *)bp) && (rover < NEXT_BLKP(bp)))  | ||||
| 	rover = bp; | ||||
| #endif | ||||
| /* $begin mmfree */ | ||||
|     return bp; | ||||
| } | ||||
| /* $end mmfree */ | ||||
| 
 | ||||
| /*
 | ||||
|  * mm_realloc - Naive implementation of realloc | ||||
|  */ | ||||
| void *mm_realloc(void *ptr, size_t size) | ||||
| { | ||||
|     size_t oldsize; | ||||
|     void *newptr; | ||||
| 
 | ||||
|     /* If size == 0 then this is just free, and we return NULL. */ | ||||
|     if(size == 0) { | ||||
| 	mm_free(ptr); | ||||
| 	return 0; | ||||
|     } | ||||
| 
 | ||||
|     /* If oldptr is NULL, then this is just malloc. */ | ||||
|     if(ptr == NULL) { | ||||
| 	return mm_malloc(size); | ||||
|     } | ||||
| 
 | ||||
|     newptr = mm_malloc(size); | ||||
| 
 | ||||
|     /* If realloc() fails the original block is left untouched  */ | ||||
|     if(!newptr) { | ||||
| 	return 0; | ||||
|     } | ||||
| 
 | ||||
|     /* Copy the old data. */ | ||||
|     oldsize = GET_SIZE(HDRP(ptr)); | ||||
|     if(size < oldsize) oldsize = size; | ||||
|     memcpy(newptr, ptr, oldsize); | ||||
| 
 | ||||
|     /* Free the old block. */ | ||||
|     mm_free(ptr); | ||||
| 
 | ||||
|     return newptr; | ||||
| } | ||||
| 
 | ||||
| /* 
 | ||||
|  * checkheap - We don't check anything right now.  | ||||
|  */ | ||||
| void mm_checkheap(int verbose)   | ||||
| {  | ||||
| } | ||||
| 
 | ||||
| /* 
 | ||||
|  * The remaining routines are internal helper routines  | ||||
|  */ | ||||
| 
 | ||||
| /* 
 | ||||
|  * extend_heap - Extend heap with free block and return its block pointer | ||||
|  */ | ||||
| /* $begin mmextendheap */ | ||||
| static void *extend_heap(size_t words)  | ||||
| { | ||||
|     char *bp; | ||||
|     size_t size; | ||||
| 
 | ||||
|     /* Allocate an even number of words to maintain alignment */ | ||||
|     size = (words % 2) ? (words+1) * WSIZE : words * WSIZE; //line:vm:mm:beginextend
 | ||||
|     if ((long)(bp = mem_sbrk(size)) == -1)   | ||||
| 	return NULL;                                        //line:vm:mm:endextend
 | ||||
| 
 | ||||
|     /* Initialize free block header/footer and the epilogue header */ | ||||
|     PUT(HDRP(bp), PACK(size, 0));         /* Free block header */   //line:vm:mm:freeblockhdr
 | ||||
|     PUT(FTRP(bp), PACK(size, 0));         /* Free block footer */   //line:vm:mm:freeblockftr
 | ||||
|     PUT(HDRP(NEXT_BLKP(bp)), PACK(0, 1)); /* New epilogue header */ //line:vm:mm:newepihdr
 | ||||
| 
 | ||||
|     /* Coalesce if the previous block was free */ | ||||
|     return coalesce(bp);                                          //line:vm:mm:returnblock
 | ||||
| } | ||||
| /* $end mmextendheap */ | ||||
| 
 | ||||
| /* 
 | ||||
|  * place - Place block of asize bytes at start of free block bp  | ||||
|  *         and split if remainder would be at least minimum block size | ||||
|  */ | ||||
| /* $begin mmplace */ | ||||
| /* $begin mmplace-proto */ | ||||
| static void place(void *bp, size_t asize) | ||||
|      /* $end mmplace-proto */ | ||||
| { | ||||
|     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)); | ||||
|     } | ||||
| } | ||||
| /* $end mmplace */ | ||||
| 
 | ||||
| /* 
 | ||||
|  * find_fit - Find a fit for a block with asize bytes  | ||||
|  */ | ||||
| /* $begin mmfirstfit */ | ||||
| /* $begin mmfirstfit-proto */ | ||||
| static void *find_fit(size_t asize) | ||||
| /* $end mmfirstfit-proto */ | ||||
| { | ||||
| /* $end mmfirstfit */ | ||||
| 
 | ||||
| #ifdef NEXT_FIT  | ||||
|     /* Next fit search */ | ||||
|     char *oldrover = rover; | ||||
| 
 | ||||
|     /* Search from the rover to the end of list */ | ||||
|     for ( ; GET_SIZE(HDRP(rover)) > 0; rover = NEXT_BLKP(rover)) | ||||
| 	if (!GET_ALLOC(HDRP(rover)) && (asize <= GET_SIZE(HDRP(rover)))) | ||||
| 	    return rover; | ||||
| 
 | ||||
|     /* search from start of list to old rover */ | ||||
|     for (rover = heap_listp; rover < oldrover; rover = NEXT_BLKP(rover)) | ||||
| 	if (!GET_ALLOC(HDRP(rover)) && (asize <= GET_SIZE(HDRP(rover)))) | ||||
| 	    return rover; | ||||
| 
 | ||||
|     return NULL;  /* no fit found */ | ||||
| #else  | ||||
| /* $begin mmfirstfit */ | ||||
|     /* First fit search */ | ||||
|     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; /* No fit */ | ||||
| /* $end mmfirstfit */ | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| static void printblock(void *bp)  | ||||
| { | ||||
|     size_t hsize, halloc, fsize, falloc; | ||||
| 
 | ||||
|     checkheap(0); | ||||
|     hsize = GET_SIZE(HDRP(bp)); | ||||
|     halloc = GET_ALLOC(HDRP(bp));   | ||||
|     fsize = GET_SIZE(FTRP(bp)); | ||||
|     falloc = GET_ALLOC(FTRP(bp));   | ||||
| 
 | ||||
|     if (hsize == 0) { | ||||
| 	printf("%p: EOL\n", bp); | ||||
| 	return; | ||||
|     } | ||||
| 
 | ||||
|     /*  printf("%p: header: [%p:%c] footer: [%p:%c]\n", bp, 
 | ||||
| 	hsize, (halloc ? 'a' : 'f'),  | ||||
| 	fsize, (falloc ? 'a' : 'f')); */ | ||||
| } | ||||
| 
 | ||||
| static void checkblock(void *bp)  | ||||
| { | ||||
|     if ((size_t)bp % 8) | ||||
| 	printf("Error: %p is not doubleword aligned\n", bp); | ||||
|     if (GET(HDRP(bp)) != GET(FTRP(bp))) | ||||
| 	printf("Error: header does not match footer\n"); | ||||
| } | ||||
| 
 | ||||
| /* 
 | ||||
|  * checkheap - Minimal check of the heap for consistency  | ||||
|  */ | ||||
| void checkheap(int verbose)  | ||||
| { | ||||
|     char *bp = heap_listp; | ||||
| 
 | ||||
|     if (verbose) | ||||
| 	printf("Heap (%p):\n", heap_listp); | ||||
| 
 | ||||
|     if ((GET_SIZE(HDRP(heap_listp)) != DSIZE) || !GET_ALLOC(HDRP(heap_listp))) | ||||
| 	printf("Bad prologue header\n"); | ||||
|     checkblock(heap_listp); | ||||
| 
 | ||||
|     for (bp = heap_listp; GET_SIZE(HDRP(bp)) > 0; bp = NEXT_BLKP(bp)) { | ||||
| 	if (verbose)  | ||||
| 	    printblock(bp); | ||||
| 	checkblock(bp); | ||||
|     } | ||||
| 
 | ||||
|     if (verbose) | ||||
| 	printblock(bp); | ||||
|     if ((GET_SIZE(HDRP(bp)) != 0) || !(GET_ALLOC(HDRP(bp)))) | ||||
| 	printf("Bad epilogue header\n"); | ||||
| } | ||||
| 
 | ||||
| team_t team = { | ||||
|     /* Team name */ | ||||
|     "CSApp Authors", | ||||
|     /* First member's full name */ | ||||
|     "Randy Bryant", | ||||
|     "randy@cs.cmu.edu", | ||||
|     /* Second member's full name (leave blank if none) */ | ||||
|     "David O'Hallaron", | ||||
|     "dave@cs.cmu.edu", | ||||
| }; | ||||
							
								
								
									
										374
									
								
								mm-gback-implicit.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										374
									
								
								mm-gback-implicit.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,374 @@ | ||||
| /* 
 | ||||
|  * Simple, 32-bit and 64-bit clean allocator based on implicit free | ||||
|  * lists, first fit placement, and boundary tag coalescing, as described | ||||
|  * in the CS:APP2e text. Blocks must be aligned to doubleword (8 byte)  | ||||
|  * boundaries. Minimum block size is 16 bytes.  | ||||
|  * | ||||
|  * This version is loosely based on  | ||||
|  * http://csapp.cs.cmu.edu/public/ics2/code/vm/malloc/mm.c
 | ||||
|  * but unlike the book's version, it does not use C preprocessor  | ||||
|  * macros or explicit bit operations. | ||||
|  */ | ||||
| #include <stdio.h> | ||||
| #include <string.h> | ||||
| #include <stdlib.h> | ||||
| #include <stdbool.h> | ||||
| #include <stdint.h> | ||||
| #include <stddef.h> | ||||
| #include <assert.h> | ||||
| 
 | ||||
| #include "mm.h" | ||||
| #include "memlib.h" | ||||
| 
 | ||||
| struct boundary_tag { | ||||
|     int inuse:1;        // inuse bit
 | ||||
|     int size:31;        // size of block, in words
 | ||||
| }; | ||||
| 
 | ||||
| /* FENCE is used for heap prologue/epilogue. */ | ||||
| const struct boundary_tag FENCE = { .inuse = 1, .size = 0 }; | ||||
| 
 | ||||
| /* A C struct describing the beginning of each block. 
 | ||||
|  * For implicit lists, used and free blocks have the same  | ||||
|  * structure, so one struct will suffice for this example. | ||||
|  * If each block is aligned at 4 mod 8, each payload will | ||||
|  * be aligned at 0 mod 8. | ||||
|  */ | ||||
| struct block { | ||||
|     struct boundary_tag header; /* offset 0, at address 4 mod 8 */ | ||||
|     char payload[0];            /* offset 4, at address 0 mod 8 */ | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * If NEXT_FIT defined use next fit search, else use first fit search  | ||||
|  */ | ||||
| #define NEXT_FITx | ||||
| 
 | ||||
| /* Basic constants and macros */ | ||||
| #define WSIZE       4       /* Word and header/footer size (bytes) */ | ||||
| #define DSIZE       8       /* Doubleword size (bytes) */ | ||||
| #define MIN_BLOCK_SIZE_WORDS 4 /* Minimum block size in words */ | ||||
| #define CHUNKSIZE  (1<<10)  /* Extend heap by this amount (words) */ | ||||
| 
 | ||||
| #define MAX(x, y) ((x) > (y)? (x) : (y))   | ||||
| 
 | ||||
| /* Global variables */ | ||||
| static struct block *heap_listp = 0;  /* Pointer to first block */   | ||||
| #ifdef NEXT_FIT | ||||
| static struct block *rover;           /* Next fit rover */ | ||||
| #endif | ||||
| 
 | ||||
| /* Function prototypes for internal helper routines */ | ||||
| static struct block *extend_heap(size_t words); | ||||
| static void place(struct block *bp, size_t asize); | ||||
| static struct block *find_fit(size_t asize); | ||||
| static struct block *coalesce(struct block *bp); | ||||
| 
 | ||||
| /* Given a block, obtain previous's block footer.
 | ||||
|    Works for left-most block also. */ | ||||
| static struct boundary_tag * prev_blk_footer(struct block *blk) { | ||||
|     return &blk->header - 1; | ||||
| } | ||||
| 
 | ||||
| /* Return if block is free */ | ||||
| static bool blk_free(struct block *blk) {  | ||||
|     return !blk->header.inuse;  | ||||
| } | ||||
| 
 | ||||
| /* Return size of block is free */ | ||||
| static size_t blk_size(struct block *blk) {  | ||||
|     return blk->header.size;  | ||||
| } | ||||
| 
 | ||||
| /* Given a block, obtain pointer to previous block.
 | ||||
|    Not meaningful for left-most block. */ | ||||
| static struct block *prev_blk(struct block *blk) { | ||||
|     struct boundary_tag *prevfooter = prev_blk_footer(blk); | ||||
|     assert(prevfooter->size != 0); | ||||
|     return (struct block *)((size_t *)blk - prevfooter->size); | ||||
| } | ||||
| 
 | ||||
| /* Given a block, obtain pointer to next block.
 | ||||
|    Not meaningful for right-most block. */ | ||||
| static struct block *next_blk(struct block *blk) { | ||||
|     assert(blk_size(blk) != 0); | ||||
|     return (struct block *)((size_t *)blk + blk->header.size); | ||||
| } | ||||
| 
 | ||||
| /* Given a block, obtain its footer boundary tag */ | ||||
| static struct boundary_tag * get_footer(struct block *blk) { | ||||
|     return (void *)((size_t *)blk + blk->header.size)  | ||||
|                    - sizeof(struct boundary_tag); | ||||
| } | ||||
| 
 | ||||
| /* Set a block's size and inuse bit in header and footer */ | ||||
| static void set_header_and_footer(struct block *blk, int size, int inuse) { | ||||
|     blk->header.inuse = inuse; | ||||
|     blk->header.size = size; | ||||
|     * get_footer(blk) = blk->header;    /* Copy header to footer */ | ||||
| } | ||||
| 
 | ||||
| /* Mark a block as used and set its size. */ | ||||
| static void mark_block_used(struct block *blk, int size) { | ||||
|     set_header_and_footer(blk, size, 1); | ||||
| } | ||||
| 
 | ||||
| /* Mark a block as free and set its size. */ | ||||
| static void mark_block_free(struct block *blk, int size) { | ||||
|     set_header_and_footer(blk, size, 0); | ||||
| } | ||||
| 
 | ||||
| /* 
 | ||||
|  * mm_init - Initialize the memory manager  | ||||
|  */ | ||||
| int mm_init(void)  | ||||
| { | ||||
|     /* Create the initial empty heap */ | ||||
|     struct boundary_tag * initial = mem_sbrk(2 * sizeof(struct boundary_tag)); | ||||
|     if (initial == (void *)-1) | ||||
|         return -1; | ||||
| 
 | ||||
|     /* We use a slightly different strategy than suggested in the book.
 | ||||
|      * Rather than placing a min-sized prologue block at the beginning | ||||
|      * of the heap, we simply place two fences. | ||||
|      * The consequence is that coalesce() must call prev_blk_footer() | ||||
|      * and not prev_blk() - prev_blk() cannot be called on the left-most | ||||
|      * block. | ||||
|      */ | ||||
|     initial[0] = FENCE;                     /* Prologue footer */ | ||||
|     heap_listp = (struct block *)&initial[1]; | ||||
|     initial[1] = FENCE;                     /* Epilogue header */ | ||||
| 
 | ||||
| #ifdef NEXT_FIT | ||||
|     rover = heap_listp; | ||||
| #endif | ||||
| 
 | ||||
|     /* Extend the empty heap with a free block of CHUNKSIZE bytes */ | ||||
|     if (extend_heap(CHUNKSIZE) == NULL)  | ||||
|         return -1; | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| /* 
 | ||||
|  * mm_malloc - Allocate a block with at least size bytes of payload  | ||||
|  */ | ||||
| void *mm_malloc(size_t size) | ||||
| { | ||||
|     size_t awords;      /* Adjusted block size in words */ | ||||
|     size_t extendwords;  /* Amount to extend heap if no fit */ | ||||
|     struct block *bp;       | ||||
| 
 | ||||
|     if (heap_listp == 0){ | ||||
|         mm_init(); | ||||
|     } | ||||
|     /* Ignore spurious requests */ | ||||
|     if (size == 0) | ||||
|         return NULL; | ||||
| 
 | ||||
|     /* Adjust block size to include overhead and alignment reqs. */ | ||||
|     size += 2 * sizeof(struct boundary_tag);    /* account for tags */ | ||||
|     size = (size + DSIZE - 1) & ~(DSIZE - 1);   /* align to double word */ | ||||
|     awords = MAX(MIN_BLOCK_SIZE_WORDS, size/WSIZE); | ||||
|                                                 /* respect minimum size */ | ||||
| 
 | ||||
|     /* Search the free list for a fit */ | ||||
|     if ((bp = find_fit(awords)) != NULL) { | ||||
|         place(bp, awords); | ||||
|         return bp->payload; | ||||
|     } | ||||
| 
 | ||||
|     /* No fit found. Get more memory and place the block */ | ||||
|     extendwords = MAX(awords,CHUNKSIZE); | ||||
|     if ((bp = extend_heap(extendwords)) == NULL)   | ||||
|         return NULL; | ||||
|     place(bp, awords); | ||||
|     return bp->payload; | ||||
| }  | ||||
| 
 | ||||
| /* 
 | ||||
|  * mm_free - Free a block  | ||||
|  */ | ||||
| void mm_free(void *bp) | ||||
| { | ||||
|     if (bp == 0)  | ||||
|         return; | ||||
| 
 | ||||
|     /* Find block from user pointer */ | ||||
|     struct block *blk = bp - offsetof(struct block, payload); | ||||
|     if (heap_listp == 0) { | ||||
|         mm_init(); | ||||
|     } | ||||
| 
 | ||||
|     mark_block_free(blk, blk_size(blk)); | ||||
|     coalesce(blk); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * coalesce - Boundary tag coalescing. Return ptr to coalesced block | ||||
|  */ | ||||
| static struct block *coalesce(struct block *bp)  | ||||
| { | ||||
|     bool prev_alloc = prev_blk_footer(bp)->inuse; | ||||
|     bool next_alloc = ! blk_free(next_blk(bp)); | ||||
|     size_t size = blk_size(bp); | ||||
| 
 | ||||
|     if (prev_alloc && next_alloc) {            /* Case 1 */ | ||||
|         return bp; | ||||
|     } | ||||
| 
 | ||||
|     else if (prev_alloc && !next_alloc) {      /* Case 2 */ | ||||
|         mark_block_free(bp, size + blk_size(next_blk(bp))); | ||||
|     } | ||||
| 
 | ||||
|     else if (!prev_alloc && next_alloc) {      /* Case 3 */ | ||||
|         bp = prev_blk(bp); | ||||
|         mark_block_free(bp, size + blk_size(bp)); | ||||
|     } | ||||
| 
 | ||||
|     else {                                     /* Case 4 */ | ||||
|         mark_block_free(prev_blk(bp),  | ||||
|                         size + blk_size(next_blk(bp)) + blk_size(prev_blk(bp))); | ||||
|         bp = prev_blk(bp); | ||||
|     } | ||||
| #ifdef NEXT_FIT | ||||
|     /* Make sure the rover isn't pointing into the free block */ | ||||
|     /* that we just coalesced */ | ||||
|     if ((rover > bp) && (rover < next_blk(bp)))  | ||||
|         rover = bp; | ||||
| #endif | ||||
|     return bp; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * mm_realloc - Naive implementation of realloc | ||||
|  */ | ||||
| void *mm_realloc(void *ptr, size_t size) | ||||
| { | ||||
|     size_t oldsize; | ||||
|     void *newptr; | ||||
| 
 | ||||
|     /* If size == 0 then this is just free, and we return NULL. */ | ||||
|     if(size == 0) { | ||||
|         mm_free(ptr); | ||||
|         return 0; | ||||
|     } | ||||
| 
 | ||||
|     /* If oldptr is NULL, then this is just malloc. */ | ||||
|     if(ptr == NULL) { | ||||
|         return mm_malloc(size); | ||||
|     } | ||||
| 
 | ||||
|     newptr = mm_malloc(size); | ||||
| 
 | ||||
|     /* If realloc() fails the original block is left untouched  */ | ||||
|     if(!newptr) { | ||||
|         return 0; | ||||
|     } | ||||
| 
 | ||||
|     /* Copy the old data. */ | ||||
|     struct block *oldblock = ptr - offsetof(struct block, payload); | ||||
|     oldsize = blk_size(oldblock) * WSIZE; | ||||
|     if(size < oldsize) oldsize = size; | ||||
|     memcpy(newptr, ptr, oldsize); | ||||
| 
 | ||||
|     /* Free the old block. */ | ||||
|     mm_free(ptr); | ||||
| 
 | ||||
|     return newptr; | ||||
| } | ||||
| 
 | ||||
| /* 
 | ||||
|  * checkheap - We don't check anything right now.  | ||||
|  */ | ||||
| void mm_checkheap(int verbose)   | ||||
| {  | ||||
| } | ||||
| 
 | ||||
| /* 
 | ||||
|  * The remaining routines are internal helper routines  | ||||
|  */ | ||||
| 
 | ||||
| /* 
 | ||||
|  * extend_heap - Extend heap with free block and return its block pointer | ||||
|  */ | ||||
| static struct block *extend_heap(size_t words)  | ||||
| { | ||||
|     void *bp; | ||||
| 
 | ||||
|     /* Allocate an even number of words to maintain alignment */ | ||||
|     words = (words + 1) & ~1; | ||||
|     if ((long)(bp = mem_sbrk(words * WSIZE)) == -1)   | ||||
|         return NULL; | ||||
| 
 | ||||
|     /* Initialize free block header/footer and the epilogue header.
 | ||||
|      * Note that we scoop up the previous epilogue here. */ | ||||
|     struct block * blk = bp - sizeof(FENCE); | ||||
|     mark_block_free(blk, words); | ||||
|     next_blk(blk)->header = FENCE; | ||||
| 
 | ||||
|     /* Coalesce if the previous block was free */ | ||||
|     return coalesce(blk); | ||||
| } | ||||
| 
 | ||||
| /* 
 | ||||
|  * place - Place block of asize words at start of free block bp  | ||||
|  *         and split if remainder would be at least minimum block size | ||||
|  */ | ||||
| static void place(struct block *bp, size_t asize) | ||||
| { | ||||
|     size_t csize = blk_size(bp); | ||||
| 
 | ||||
|     if ((csize - asize) >= MIN_BLOCK_SIZE_WORDS) {  | ||||
|         mark_block_used(bp, asize); | ||||
|         bp = next_blk(bp); | ||||
|         mark_block_free(bp, csize-asize); | ||||
|     } | ||||
|     else {  | ||||
|         mark_block_used(bp, csize); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /* 
 | ||||
|  * find_fit - Find a fit for a block with asize words  | ||||
|  */ | ||||
| static struct block *find_fit(size_t asize) | ||||
| { | ||||
| 
 | ||||
| #ifdef NEXT_FIT  | ||||
|     /* Next fit search */ | ||||
|     struct block *oldrover = rover; | ||||
| 
 | ||||
|     /* Search from the rover to the end of list */ | ||||
|     for ( ; blk_size(rover) > 0; rover = next_blk(rover)) | ||||
|         if (blk_free(rover) && (asize <= blk_size(rover))) | ||||
|             return rover; | ||||
| 
 | ||||
|     /* search from start of list to old rover */ | ||||
|     for (rover = heap_listp; rover < oldrover; rover = next_blk(rover)) | ||||
|         if (blk_free(rover) && (asize <= blk_size(rover))) | ||||
|             return rover; | ||||
| 
 | ||||
|     return NULL;  /* no fit found */ | ||||
| #else  | ||||
|     /* First fit search */ | ||||
|     struct block *bp; | ||||
| 
 | ||||
|     for (bp = heap_listp; blk_size(bp) > 0; bp = next_blk(bp)) { | ||||
|         if (blk_free(bp) && asize <= blk_size(bp)) { | ||||
|             return bp; | ||||
|         } | ||||
|     } | ||||
|     return NULL; /* No fit */ | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| team_t team = { | ||||
|     /* Team name */ | ||||
|     "Sample allocator using implicit lists", | ||||
|     /* First member's full name */ | ||||
|     "Godmar Back", | ||||
|     "gback@cs.vt.edu", | ||||
|     /* Second member's full name (leave blank if none) */ | ||||
|     "", | ||||
|     "", | ||||
| }; | ||||
							
								
								
									
										23
									
								
								mm.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								mm.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | ||||
| #include <stdio.h> | ||||
| 
 | ||||
| extern int mm_init (void); | ||||
| extern void *mm_malloc (size_t size); | ||||
| extern void mm_free (void *ptr); | ||||
| extern void *mm_realloc(void *ptr, size_t size); | ||||
| 
 | ||||
| 
 | ||||
| /* 
 | ||||
|  * Students work in teams of one or two.  Teams enter their team name,  | ||||
|  * personal names and login IDs in a struct of this | ||||
|  * type in their bits.c file. | ||||
|  */ | ||||
| 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; | ||||
| 
 | ||||
| extern team_t team; | ||||
| 
 | ||||
							
								
								
									
										16
									
								
								short1-bal.rep
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								short1-bal.rep
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | ||||
| 20000 | ||||
| 6 | ||||
| 12 | ||||
| 1 | ||||
| a 0 2040 | ||||
| a 1 2040 | ||||
| f 1 | ||||
| a 2 48 | ||||
| a 3 4072 | ||||
| f 3 | ||||
| a 4 4072 | ||||
| f 0 | ||||
| f 2 | ||||
| a 5 4072 | ||||
| f 4 | ||||
| f 5 | ||||
							
								
								
									
										16
									
								
								short2-bal.rep
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								short2-bal.rep
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | ||||
| 20000 | ||||
| 6 | ||||
| 12 | ||||
| 1 | ||||
| a 0 2040 | ||||
| a 1 4010 | ||||
| a 2 48 | ||||
| a 3 4072 | ||||
| a 4 4072 | ||||
| a 5 4072 | ||||
| f 0 | ||||
| f 1 | ||||
| f 2 | ||||
| f 3 | ||||
| f 4 | ||||
| f 5 | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user