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