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