added huge pages tests

This commit is contained in:
2025-04-25 16:32:38 +01:00
parent 524057812c
commit 788aadfc9c
156 changed files with 97963 additions and 70 deletions

View File

@@ -166,7 +166,7 @@ void *malloc(size_t sz) {
// if (heap + sz > heap_start + HEAP_SIZE) return NULL;
// heap += sz;
// return heap - sz;
malloc_called += 1;
malloc_called += 2;
return MALLOCCHERI(sz);
}

View File

@@ -0,0 +1,220 @@
/**********************************
* bitmap_alloc.c
* Jeremy.Singer@glasgow.ac.uk
*
* This is a simple fixed-size bitmap allocator.
* It mmaps a large buffer of
* NUM_CHUNKS * CHUNK_SIZE bytes
* then allocates this space in equally-sized
* chunks to client code.
* A side bitmap is required to keep track of which
* chunks are in use (corresponding bit set to 1)
* and which chunks are free (corresponding bit
* set to 0). There is one bit per allocatable chunk.
*
* This is _not_ a clever allocator, since it
* does a linear scan of the bitmap to find the
* first free chunk, which is expensive!
* More efficient scans could be easily incorporated.
*
* This is _not_ a general-purpose allocator, since
* it only allocates chunks of a fixed size. Further,
* this size is constrained to be small enough to allow
* exact bounds representation in CHERI capabilities.
*
* This is an initial simple memory allocator test
* for CHERI / Capable VMs.
* We explore capability alignment,
* representable bounds, narrowing operations
* and compiler intrinsic support.
*/
#include <assert.h>
#include <cheriintrin.h>
#include <cheri/cheric.h>
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
// #include "bitmap_alloc.h"
#include "custom_alloc.h"
#define BITS_PER_BYTE 8
char *buffer = NULL; /* allocation buffer */
unsigned char *bitmap = NULL; /* bitmap for the buffer */
int buffer_size = 0; /* size of buffer (in bytes) */
int bitmap_size = 0; /* size of bitmap (in bytes) */
int bytes_per_chunk = 0; /* size of single chunk (in bytes) */
void init_alloc(int num_chunks, int chunk_size)
{
int i = 0;
/* we need to increase the num_chunks
* so every bit in bitmap will be used
*/
int adjusted_num_chunks = (num_chunks % BITS_PER_BYTE == 0)
? num_chunks
: (num_chunks + (BITS_PER_BYTE - (num_chunks % BITS_PER_BYTE)));
/* we need to increase the chunk_size
* so chunks will be CHERI aligned
* (i.e. 16 bytes for RISC-V 64-bit arch)
*/
int adjusted_chunk_size =
(chunk_size % (sizeof(void *)) == 0)
? chunk_size
: (chunk_size + (sizeof(void *)) - (chunk_size % (sizeof(void *))));
/* check this chunk size is small enough so we can represent
* bounds precisely with CHERI compressed representation
*/
adjusted_chunk_size = cheri_representable_length(adjusted_chunk_size);
/* request memory for our allocation buffer */
// char *res = mmap(NULL, adjusted_num_chunks * adjusted_chunk_size, PROT_READ | PROT_WRITE,
// MAP_ANON | MAP_PRIVATE, -1, 0);
char *res = (char *)MALLOCCHERI(adjusted_num_chunks * adjusted_chunk_size);
char *bitmap = (char *)MALLOCCHERI(adjusted_num_chunks / BITS_PER_BYTE);
/* request memory for our bitmap */
// bitmap = (unsigned char *) mmap(NULL, adjusted_num_chunks / BITS_PER_BYTE,
// PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
if (res == MAP_FAILED || bitmap == MAP_FAILED)
{
perror("error in initial mem allocation");
exit(-1);
}
/* NB mmap min bounds for capability is 1 page (4K) */
buffer = res;
/* check buffer is aligned */
assert((uintptr_t) buffer % sizeof(void *) == 0);
/* check bitmap is aligned */
assert((uintptr_t) bitmap % sizeof(void *) == 0);
bytes_per_chunk = adjusted_chunk_size;
buffer_size = adjusted_num_chunks * adjusted_chunk_size;
bitmap_size = adjusted_num_chunks / BITS_PER_BYTE;
/* zero bitmap, since all chunks are free initially */
for (i = 0; i < bitmap_size; i++)
{
bitmap[i] = 0;
}
// set exact bounds for buffer and bitmap?
buffer = cheri_setbounds(buffer, buffer_size);
bitmap = cheri_setbounds(bitmap, bitmap_size);
return;
}
/*
* allocate fixed size chunk with bitmap allocator
* this is our simplistic `malloc` function
*/
// Length is not used but just kept
// to keep the integrity of the
// malloc shape.
int notrun = 0;
void *malloc(size_t len)
{
if (notrun == 0){
init_alloc(500,1000);
notrun = 1;
}
unsigned char updated_byte = 0;
int chunk_index = 0;
char *chunk = NULL;
// iterate over all bits in bitmap, looking for a 0
// when we find a 0, set it to 1 and
// return the corresponding chunk
// (setting its capability bounds)
int i = 0;
while (bitmap[i] == (unsigned char) 0xff)
{
i++;
if (i >= bitmap_size)
break;
}
// do we have a 0?
if (i < bitmap_size && bitmap[i] != (unsigned char) 0xff)
{
// find the lowest 0 ...
int j = 0;
// right shift until bottom bit is 0
for (j = 0; j < BITS_PER_BYTE; j++)
{
int bit = (bitmap[i] >> j) & 1;
if (bit == 0)
{
break;
}
}
// now i is the word index, j is the bit index
// set this bit to 1 ...
// and work out the chunk to allocate
updated_byte = bitmap[i] + (unsigned char) (1 << j);
bitmap[i] = updated_byte;
chunk_index = i * BITS_PER_BYTE + j;
chunk = buffer + (chunk_index * bytes_per_chunk);
/* restrict capability range before returning ptr */
chunk = cheri_setbounds(chunk, bytes_per_chunk);
}
return chunk;
}
void free(void *chunk)
{
vaddr_t base = cheri_getbase(chunk);
vaddr_t buff_base = cheri_getbase(buffer);
/* calculate chunk index in buffer */
int chunk_index = (base - buff_base) / bytes_per_chunk;
assert(chunk_index >= 0);
/* calculate corresponding bitmap index */
int bitmap_index = chunk_index / BITS_PER_BYTE;
assert(bitmap_index < bitmap_size);
int bitmap_offset = chunk_index % BITS_PER_BYTE;
/* set this bitmap entry to 0 */
unsigned char updated_byte = bitmap[bitmap_index] & (unsigned char) (~(1 << bitmap_offset));
bitmap[bitmap_index] = updated_byte;
return;
}
int num_used_chunks()
{
int i = 0;
int used_chunks = 0;
while (i < bitmap_size)
{
unsigned char x = bitmap[i];
if (x != 0)
{
/* some used chunks here */
unsigned char j;
for (j = 1; j <= x; j = j << 1)
{
if (x & j)
{
used_chunks++;
}
}
}
i++;
}
return used_chunks;
}

View File

@@ -0,0 +1,10 @@
add_executable(kmeans kmeans-pthread.c)
target_link_libraries(kmeans PRIVATE pthread)
add_coz_run_target(run_kmeans_small COMMAND $<TARGET_FILE:kmeans> -d 3 -c 100 -p 10000 -s 100)
add_coz_run_target(run_kmeans_large COMMAND $<TARGET_FILE:kmeans> -d 3 -c 100 -p 100000 -s 1000)
add_test(
NAME test_run_kmeans
COMMAND ${PROJECT_SOURCE_DIR}/benchmarks/check-output.sh ${PROJECT_SOURCE_DIR}/coz run --- $<TARGET_FILE:kmeans>
WORKING_DIRECTORY ${PROJECT_BINARY_DIR})

View File

@@ -0,0 +1,395 @@
/*
* Copyright (c) 2011 Bharath Ramesh <bramesh.dev@gmail.com>
*
* Distributed under the terms of GNU LGPL, version 2.1
*/
#include <stdint.h>
#include <string.h>
// #include "brmalloc.h"
// ------------
// User defined
#define MIN_ALLOC_SIZE ((size_t)8U)
#define MAX_ALLOC_SIZE ((size_t)4096U)
#define MAX_NUM_ALLOCS 8192
#define NUM_ITERATIONS 32UNIMPLEMENTED
// ------------
#define BITS_PER_LONG 64
#define LOG_BITS_PER_BYTE 3
#define LOG_BITS_PER_LONG 6
#define LOG_BYTES_PER_LONG 3
#define MAX_LONG 0xffffffffffffffff
#define K ((size_t)1024U)
#define M ((size_t)1024U * K)
#define G ((size_t)1024U * M)
#define MALLOC_OVERHEAD ((size_t)16U)
#ifndef ABORT
#define ABORT() abort()
#endif
#ifndef EXIT_ERR
#define EXIT_ERR() exit(EXIT_FAILURE)
#endif
#ifndef DEBUG
#include <stdio.h>
#define DEBUG(fmt, args...) printf("%d, %s: " fmt, __LINE__, __FUNCTION__, ##args)
#endif
#ifndef MALLOC_QUANTA
#define MALLOC_QUANTA ((size_t)16U)
#endif
#ifndef MALLOC_THRESHOLD
#define MALLOC_THRESHOLD ((size_t)1U * M)
#endif
#ifndef MALLOC_ZONE_SIZE
#define MALLOC_ZONE_SIZE ((size_t)16U * M)
#endif
#ifndef MMAP
#include <sys/mman.h>
#define MMAP_FLAGS (MAP_ANONYMOUS | MAP_PRIVATE)
#define MMAP_PROT (PROT_READ | PROT_WRITE)
#define MMAP(s) mmap (0, (s), MMAP_PROT, MMAP_FLAGS, -1, 0)
#define MUNMAP(a, s) munmap ((a), (s))
#endif
struct __malloc_zone_type {
unsigned char *bmap;
size_t bmap_longs;
size_t free_size;
uint64_t id;
void *region;
uint64_t start_byte;
struct __malloc_zone_type *next;
};
typedef struct __malloc_zone_type malloc_zone_t;
static malloc_zone_t *mz_head = NULL, *mz_tail = NULL;
static uint8_t mqbits = 0;
static inline void
free_region (malloc_zone_t *mz, uint64_t loc, size_t nbytes)
{
unsigned char *bmap;
size_t bmap_longs;
uint64_t i, i_b, j_b, lbits, nbits, pattern, *wordptr;
lbits = nbits = nbytes >> mqbits;
i_b = loc >> LOG_BITS_PER_LONG;
j_b = loc - (i_b << LOG_BITS_PER_LONG);
bmap = mz->bmap;
bmap_longs = mz->bmap_longs;
for (i = i_b; i < bmap_longs; ++i) {
if (lbits == 0)
break;
wordptr = (uint64_t *) bmap + i;
if (i == i_b) {
if ((j_b + nbits) < BITS_PER_LONG) {
pattern = ((uint64_t) 1 << nbits) - 1;
pattern = ~(pattern << j_b);
*wordptr &= pattern;
return;
}
pattern = ~(MAX_LONG << j_b);
*wordptr &= pattern;
lbits -= (BITS_PER_LONG -j_b);
continue;
}
if (lbits >= BITS_PER_LONG) {
*wordptr = 0;
lbits -= BITS_PER_LONG;
continue;
}
pattern = ~(((uint64_t) 1 << lbits) - 1);
*wordptr &= pattern;
return;
}
return;
}
static inline uint64_t
get_region (malloc_zone_t *mz, size_t nbytes)
{
unsigned char *bmap;
size_t bmap_longs;
uint64_t i = 0, i_b, j, j_b, l, nbits, rbits, pattern, word, *wordptr;
int64_t loc = -1;
nbits = rbits = nbytes >> mqbits;
bmap = mz->bmap;
bmap_longs = mz->bmap_longs;
l = mz->start_byte;
while (i < bmap_longs) {
if (rbits == 0)
break;
word = *((uint64_t *) bmap + l);
if (word == MAX_LONG) {
loc = -1;
rbits = nbits;
++i;
++l;
if (l == bmap_longs)
l = 0;
continue;
}
if (word == 0) {
if (rbits >= BITS_PER_LONG) {
rbits -= BITS_PER_LONG;
if (loc == -1)
loc = l << LOG_BITS_PER_LONG;
++i;
++l;
if (l == bmap_longs) {
l = 0;
loc = -1;
rbits = nbits;
}
continue;
}
rbits = 0;
if (loc == -1)
loc = l << LOG_BITS_PER_LONG;
break;
}
if (rbits >= BITS_PER_LONG) {
loc = -1;
rbits = nbits;
++i;
++l;
if (l == bmap_longs)
l = 0;
continue;
}
for (j = 0; j < BITS_PER_LONG; ++j) {
if (rbits == 0)
break;
if ((word >> j) & 1) {
loc = -1;
rbits = nbits;
++i;
++l;
if (l == bmap_longs)
l = 0;
break;
}
--rbits;
if (loc == -1)
loc = (l << LOG_BITS_PER_LONG) + j;
}
}
if ((rbits != 0) || (loc == -1))
return -1;
mz->start_byte = (loc + nbits) >> LOG_BITS_PER_LONG;
i_b = loc >> LOG_BITS_PER_LONG;
j_b = loc - (i_b << LOG_BITS_PER_LONG);
rbits = nbits;
for (i = i_b; i < bmap_longs; ++i) {
if (rbits == 0)
break;
wordptr = (uint64_t *) bmap + i;
if (i == i_b) {
if ((j_b + nbits) < BITS_PER_LONG) {
pattern = ((uint64_t) 1 << nbits) - 1;
pattern = pattern << j_b;
*wordptr |= pattern;
return loc;
}
pattern = MAX_LONG << j_b;
*wordptr |= pattern;
rbits -= (BITS_PER_LONG - j_b);
continue;
}
if (rbits >= BITS_PER_LONG) {
*wordptr = MAX_LONG;
rbits -= BITS_PER_LONG;
continue;
}
pattern = ((uint64_t) 1 << rbits) - 1;
*wordptr |= pattern;
return loc;
}
return loc;
}
static inline malloc_zone_t *
new_malloc_zone (void)
{
size_t bmap_size, bmap_longs;
malloc_zone_t *mz;
void *region;
region = MMAP (MALLOC_ZONE_SIZE);
if (region == MAP_FAILED) {
DEBUG ("ERROR: MMAP failed.\n");
return NULL;
}
bmap_size = (MALLOC_ZONE_SIZE / MALLOC_QUANTA) >> LOG_BITS_PER_BYTE;
bmap_longs = bmap_size >> LOG_BYTES_PER_LONG;
mz = (malloc_zone_t *) malloc (sizeof (malloc_zone_t));
mz->bmap = (unsigned char *) malloc (bmap_size);
if (mz->bmap == NULL) {
DEBUG ("ERROR: Unable to allocate bitmap.\n");
free (mz);
MUNMAP (region, MALLOC_ZONE_SIZE);
return NULL;
}
memset (mz->bmap, 0, bmap_size);
mz->bmap_longs = bmap_longs;
mz->free_size = MALLOC_ZONE_SIZE;
mz->id = (uint64_t) mz;
mz->region = region;
mz->start_byte = 0;
mz->next = NULL;
if (mz_tail != NULL)
mz_tail->next = mz;
mz_tail = mz;
if (mz_head == NULL)
mz_head = mz;
return mz;
}
static void __attribute__ ((constructor))
brm_init (void)
{
uint8_t i, no_ones;
size_t size;
no_ones = 0;
size = MALLOC_QUANTA;
for (i = 0; i < BITS_PER_LONG; ++i) {
if ((size >> i) & 1) {
++no_ones;
if (no_ones > 1) {
DEBUG ("ERROR: MALLOC_QUANTA not power of "
"2.\n");
EXIT_ERR ();
}
mqbits = i;
}
}
if (no_ones == 0) {
DEBUG ("ERROR: MALLOC_QUANTA set to 0 (zero).\n");
EXIT_ERR ();
}
if (new_malloc_zone () == NULL) {
DEBUG ("ERROR: new_malloc_zone failed.\n");
EXIT_ERR ();
}
return;
}
void
brm_free (void *ptr)
{
uint64_t *base;
malloc_zone_t *mz;
int64_t offset;
size_t size;
base = (uint64_t *) ptr - 2;
mz = (malloc_zone_t *) *((uint64_t *) base);
size = (size_t) *((uint64_t *) base + 1);
if (mz->id != (uint64_t) mz) {
DEBUG ("ptr: %p, base: %p\n", ptr, base);
DEBUG ("ERROR: data corruption.\n");
ABORT ();
}
offset = ((void *) base - mz->region) >> mqbits;
free_region (mz, offset, size);
mz->free_size += size;
return;
}
void *
brm_malloc (size_t size)
{
uint64_t addr;
malloc_zone_t *mz;
size_t nbytes;
int64_t offset;
if (size == 0)
return NULL;
nbytes = (size + MALLOC_OVERHEAD + MALLOC_QUANTA - 1) &
~(MALLOC_QUANTA - 1);
// printf(nbytes);
// if (nbytes >= MALLOC_THRESHOLD) {
// DEBUG ("UNIMPLEMENTED\n");
// EXIT_ERR();
// }
mz = mz_head;
while (1) {
if (nbytes > mz->free_size) {
DEBUG ("UNIMPLEMENTED\n");
EXIT_ERR ();
}
offset = get_region (mz, nbytes);
if (offset == -1) {
mz = mz->next;
if (mz == NULL) {
if ((mz = new_malloc_zone ()) == NULL) {
DEBUG ("ERROR: new_malloc zone "
"failed.\n");
EXIT_ERR ();
}
}
continue;
}
mz->free_size -= nbytes;
addr = (uint64_t) mz->region + ((size_t) offset << mqbits);
*((uint64_t *) addr) = (uint64_t) mz;
*((uint64_t *) addr + 1) = (uint64_t) nbytes;
break;
}
return (void *) (addr + 2 * sizeof (uint64_t));
}

View File

@@ -0,0 +1,3 @@
# git pull origin main
cc -g -O2 -Wall -o kmeans-pthread.out -lpthread kmeans-pthread.c
# sudo time pmcstat -d -w 1 -p l1d_tlb_rd -p l2d_tlb_rd -p l1d_tlb_refill -p cpu_cycles -p dtlb_walk -p stall_backend ./kmeans-pthread.out

View File

@@ -0,0 +1,93 @@
/*
* Copyright (c) 2015, Charlie Curtsinger and Emery Berger,
* University of Massachusetts Amherst
* This file is part of the Coz project. See LICENSE.md file at the top-level
* directory of this distribution and at http://github.com/plasma-umass/coz.
*/
#if !defined(COZ_H)
#define COZ_H
#ifndef __USE_GNU
# define __USE_GNU
#endif
#ifndef _GNU_SOURCE
# define _GNU_SOURCE
#endif
#include <dlfcn.h>
#include <stdint.h>
#include <string.h> /* for memcpy hack below */
#if defined(__cplusplus)
extern "C" {
#endif
#define COZ_COUNTER_TYPE_THROUGHPUT 1
#define COZ_COUNTER_TYPE_BEGIN 2
#define COZ_COUNTER_TYPE_END 3
// Declare dlsym as a weak reference so libdl isn't required
void* dlsym(void* handle, const char* symbol) __attribute__((weak));
// Counter info struct, containing both a counter and backoff size
typedef struct {
size_t count; // The actual count
size_t backoff; // Used to batch updates to the shared counter. Currently unused.
} coz_counter_t;
// The type of the _coz_get_counter function
typedef coz_counter_t* (*coz_get_counter_t)(int, const char*);
// Locate and invoke _coz_get_counter
static coz_counter_t* _call_coz_get_counter(int type, const char* name) {
static unsigned char _initialized = 0;
static coz_get_counter_t fn; // The pointer to _coz_get_counter
if(!_initialized) {
if(dlsym) {
// Locate the _coz_get_counter method
void* p = dlsym(RTLD_DEFAULT, "_coz_get_counter");
// Use memcpy to avoid pedantic GCC complaint about storing function pointer in void*
memcpy(&fn, &p, sizeof(p));
}
_initialized = 1;
}
// Call the function, or return null if profiler is not found
if(fn) return fn(type, name);
else return 0;
}
// Macro to initialize and increment a counter
#define COZ_INCREMENT_COUNTER(type, name) \
if(1) { \
static unsigned char _initialized = 0; \
static coz_counter_t* _counter = 0; \
\
if(!_initialized) { \
_counter = _call_coz_get_counter(type, name); \
_initialized = 1; \
} \
if(_counter) { \
__atomic_add_fetch(&_counter->count, 1, __ATOMIC_RELAXED); \
} \
}
#define STR2(x) #x
#define STR(x) STR2(x)
#define COZ_PROGRESS_NAMED(name) COZ_INCREMENT_COUNTER(COZ_COUNTER_TYPE_THROUGHPUT, name)
#define COZ_PROGRESS COZ_INCREMENT_COUNTER(COZ_COUNTER_TYPE_THROUGHPUT, __FILE__ ":" STR(__LINE__))
#define COZ_BEGIN(name) COZ_INCREMENT_COUNTER(COZ_COUNTER_TYPE_BEGIN, name)
#define COZ_END(name) COZ_INCREMENT_COUNTER(COZ_COUNTER_TYPE_END, name)
#if defined(__cplusplus)
}
#endif
#endif

View File

@@ -0,0 +1,7 @@
Regular mmap:
723.17 real
modified mmap:
723.17 real
modified mmap:
156.65 real

View File

@@ -0,0 +1,407 @@
/* Copyright (c) 2007-2009, Stanford University
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Stanford University nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY STANFORD UNIVERSITY ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL STANFORD UNIVERSITY BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <strings.h>
#include <string.h>
#include <stddef.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <math.h>
#include <pthread.h>
#include "stddefines.h"
#include "coz.h"
// #define malloc my_malloc
// #define free my_free
#define DEF_NUM_POINTS 150000
#define DEF_NUM_MEANS 100
#define DEF_DIM 40
#define DEF_GRID_SIZE 100
// #define DEF_NUM_POINTS 100000
// #define DEF_NUM_MEANS 100
// #define DEF_DIM 40
// #define DEF_GRID_SIZE 1000
// #define DEF_NUM_POINTS 1000
// #define DEF_NUM_MEANS 10
// #define DEF_DIM 3
// #define DEF_GRID_SIZE 100
#define false 0
#define true 1
int num_points; // number of vectors
int dim; // Dimension of each vector
int num_means; // number of clusters
int grid_size; // size of each dimension of vector space
int modified;
int num_pts = 0;
int **points;
int **means;
int *clusters;
typedef struct {
int start_idx;
int num_pts;
int *sum;
} thread_arg;
/** dump_points()
* Helper function to print out the points
*/
void dump_points(int **vals, int rows)
{
int i, j;
for (i = 0; i < rows; i++)
{
for (j = 0; j < dim; j++)
{
dprintf("%5d ",vals[i][j]);
}
// dprintf("\n");
}
}
/** parse_args()
* Parse the user arguments
*/
void parse_args(int argc, char **argv)
{
int c;
extern char *optarg;
extern int optind;
num_points = DEF_NUM_POINTS;
num_means = DEF_NUM_MEANS;
dim = DEF_DIM;
grid_size = DEF_GRID_SIZE;
while ((c = getopt(argc, argv, "d:c:p:s:")) != EOF)
{
switch (c) {
case 'd':
dim = atoi(optarg);
break;
case 'c':
num_means = atoi(optarg);
break;
case 'p':
num_points = atoi(optarg);
break;
case 's':
grid_size = atoi(optarg);
break;
case '?':
printf("Usage: %s -d <vector dimension> -c <num clusters> -p <num points> -s <grid size>\n", argv[0]);
exit(1);
}
}
if (dim <= 0 || num_means <= 0 || num_points <= 0 || grid_size <= 0) {
printf("Illegal argument value. All values must be numeric and greater than 0\n");
exit(1);
}
printf("Dimension = %d\n", dim);
printf("Number of clusters = %d\n", num_means);
printf("Number of points = %d\n", num_points);
printf("Size of each dimension = %d\n", grid_size);
}
/** generate_points()
* Generate the points
*/
void generate_points(int **pts, int size)
{
int i, j;
for (i=0; i<size; i++)
{
for (j=0; j<dim; j++)
{
pts[i][j] = rand() % grid_size;
}
}
}
/** get_sq_dist()
* Get the squared distance between 2 points
*/
static inline unsigned int get_sq_dist(int *v1, int *v2)
{
int i;
unsigned int sum = 0;
for (i = 0; i < dim; i++)
{
sum += ((v1[i] - v2[i]) * (v1[i] - v2[i]));
}
return sum;
}
/** add_to_sum()
* Helper function to update the total distance sum
*/
void add_to_sum(int *sum, int *point)
{
int i;
for (i = 0; i < dim; i++)
{
sum[i] += point[i];
}
}
/** find_clusters()
* Find the cluster that is most suitable for a given set of points
*/
void *find_clusters(void *arg)
{
thread_arg *t_arg = (thread_arg *)arg;
int i, j;
unsigned int min_dist, cur_dist;
int min_idx;
int start_idx = t_arg->start_idx;
int end_idx = start_idx + t_arg->num_pts;
for (i = start_idx; i < end_idx; i++)
{
min_dist = get_sq_dist(points[i], means[0]);
min_idx = 0;
for (j = 1; j < num_means; j++)
{
cur_dist = get_sq_dist(points[i], means[j]);
if (cur_dist < min_dist)
{
min_dist = cur_dist;
min_idx = j;
}
}
if (clusters[i] != min_idx)
{
clusters[i] = min_idx;
modified = true;
}
COZ_PROGRESS_NAMED("clusters found");
}
return (void *)0;
}
/** calc_means()
* Compute the means for the various clusters
*/
void *calc_means(void *arg)
{
int i, j, grp_size;
int *sum;
thread_arg *t_arg = (thread_arg *)arg;
int start_idx = t_arg->start_idx;
int end_idx = start_idx + t_arg->num_pts;
sum = t_arg->sum;
for (i = start_idx; i < end_idx; i++)
{
memset(sum, 0, dim * sizeof(int));
grp_size = 0;
for (j = 0; j < num_points; j++)
{
if (clusters[j] == i)
{
add_to_sum(sum, points[j]);
grp_size++;
}
}
for (j = 0; j < dim; j++)
{
//dprintf("div sum = %d, grp size = %d\n", sum[j], grp_size);
if (grp_size != 0)
{
means[i][j] = sum[j] / grp_size;
}
}
}
// free(sum);
return (void *)0;
}
int main(int argc, char **argv)
{
// sleep(10);
// Extra code snippet added
// printf("Initial alloc called\n");
//INITAlloc();
// INITREGULARALLOC();
// init_malloc();
int num_procs, curr_point;
int i;
pthread_t *pid;
pthread_attr_t attr;
thread_arg *arg;
int num_per_thread, excess;
parse_args(argc, argv);
points = (int **)malloc(sizeof(int *) * num_points);
for (i=0; i<num_points; i++)
{
points[i] = (int *)malloc(sizeof(int) * dim);
}
printf("Generating points\n");
generate_points(points, num_points);
// printf("calling malloc after generate\n");
means = (int **)malloc(sizeof(int *) * num_means);
for (i=0; i<num_means; i++)
{
means[i] = (int *)malloc(sizeof(int) * dim);
}
// dprintf("Generating means\n");
generate_points(means, num_means);
clusters = (int *)malloc(sizeof(int) * num_points);
memset(clusters, -1, sizeof(int) * num_points);
pthread_attr_init(&attr);
pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
CHECK_ERROR((num_procs = sysconf(_SC_NPROCESSORS_ONLN)) <= 0);
CHECK_ERROR( (pid = (pthread_t *)malloc(sizeof(pthread_t) * num_procs)) == NULL);
modified = true;
printf("Starting iterative algorithm\n");
/* Create the threads to process the distances between the various
points and repeat until modified is no longer valid */
int num_threads;
while (modified)
{
// printf("Inside loop\n");
num_per_thread = num_points / num_procs;
excess = num_points % num_procs;
modified = false;
// printf("Modified set to false\n");
// printf(".");
// printf("Point printed\n");
curr_point = 0;
num_threads = 0;
while (curr_point < num_points) {
// printf("Inside secondary while loop\n");
CHECK_ERROR((arg = (thread_arg *)malloc(sizeof(thread_arg))) == NULL);
arg->start_idx = curr_point;
arg->num_pts = num_per_thread;
if (excess > 0) {
arg->num_pts++;
excess--;
}
CHECK_ERROR((pthread_create(&(pid[num_threads++]), &attr, find_clusters,
(void *)(arg))) != 0);
curr_point += arg->num_pts;
}
// printf("left while loop\n");
assert (num_threads == num_procs);
for (i = 0; i < num_threads; i++) {
pthread_join(pid[i], NULL);
}
num_per_thread = num_means / num_procs;
excess = num_means % num_procs;
curr_point = 0;
num_threads = 0;
// printf("reaches here \n");
while (curr_point < num_means) {
// printf("enters while loop \n");
CHECK_ERROR((arg = (thread_arg *)malloc(sizeof(thread_arg))) == NULL);
// printf("succesfully runs \n");
arg->start_idx = curr_point;
// printf("Running malloc \n");
arg->sum = (int *)malloc(dim * sizeof(int));
// printf("Finished malloc \n");
arg->num_pts = num_per_thread;
if (excess > 0) {
arg->num_pts++;
excess--;
}
// printf("Running create \n");
CHECK_ERROR((pthread_create(&(pid[num_threads++]), &attr, calc_means,
(void *)(arg))) != 0);
// printf("Create complete \n");
curr_point += arg->num_pts;
}
// printf("Running secondary join \n");
assert (num_threads == num_procs);
for (i = 0; i < num_threads; i++) {
pthread_join(pid[i], NULL);
}
// printf("Left while loop \n");
}
// dprintf("\n\nFinal means:\n");
dump_points(means, num_means);
// for (i = 0; i < num_points; i++)
// free(points[i]);
// free(points);
// for (i = 0; i < num_means; i++)
// {
// free(means[i]);
// }
// free(means);
// free(clusters);
// CLEARALLOC();
return 0;
}

View File

@@ -0,0 +1,46 @@
// Quick malloc implementation with mmap
// void* malloc(size_t sz)
// {
// sz = __builtin_align_up(sz, _Alignof(max_align_t));
// MallocCounter -= sz;
// void *ptrLink = &ptr[MallocCounter];
// ptrLink = cheri_setbounds(ptrLink, sz);
// return ptrLink;
// }
// // Quick cheri free implementation
// void free(void *ptr) {
// int len = cheri_getlen(ptr);
// munmap(ptr, len);
// }
// Init_alloc(void) {
// size_t sz;
// // Pre Allocate 400 MB
// sz = 1073741824;
// fd = shm_create_largepage(SHM_ANON, O_CREAT | O_RDWR, 1, SHM_LARGEPAGE_ALLOC_DEFAULT, 0);
// ptr = mmap(NULL, sz,
// PROT_READ|PROT_WRITE, MAP_SHARED,fd,0);
// MallocCounter = (int)sz;
// }
FUNCTION malloc(sz):
sz = ALIGN_UP(sz, MAX_ALIGNMENT) // Align size to max alignment
MallocCounter = MallocCounter - sz // Update remaining memory
ptrLink = &ptr[MallocCounter] // Calculate pointer address
ptrLink = SET_BOUNDS(ptrLink, sz) // Set bounds for memory safety and to track the length of the pointer
RETURN ptrLink // Return allocated memory pointer
FUNCTION free(ptr):
len = GET_LENGTH(ptr) // Get length of memory block from the defined bounds
UNMAP(ptr, len) // Release memory block
FUNCTION Init_alloc():
sz = 1 GB // Define pre-allocated memory size
fd = CREATE_LARGE_PAGE_MEMORY(sz) // Create shared memory
ptr = MAP_MEMORY(sz) // Map memory region
MallocCounter = sz // Initialize memory counter

View File

@@ -0,0 +1,18 @@
sh build.sh
LD_PRELOAD=./libjemalloc.so time pmcstat -d -w 1 -p l1d_tlb_rd -p l2d_tlb_rd -p l1d_tlb_refill -p cpu_cycles -p dtlb_walk -p stall_backend -p ll_cache_miss_rd -o kmeans-alloc-350000.txt ./kmeans-pthread.out -d 40 -c 100 -p 350000 -s 1000 > kmeans-bounds-350000-out.txt
LD_PRELOAD=./libjemalloc.so time pmcstat -d -w 1 -p l1d_tlb_rd -p l2d_tlb_rd -p l1d_tlb_refill -p cpu_cycles -p dtlb_walk -p stall_backend -p ll_cache_miss_rd -o kmeans-alloc-300000.txt ./kmeans-pthread.out -d 40 -c 100 -p 300000 -s 1000 > kmeans-bounds-300000-out.txt
LD_PRELOAD=./libjemalloc.so time pmcstat -d -w 1 -p l1d_tlb_rd -p l2d_tlb_rd -p l1d_tlb_refill -p cpu_cycles -p dtlb_walk -p stall_backend -p ll_cache_miss_rd -o kmeans-alloc-250000.txt ./kmeans-pthread.out -d 40 -c 100 -p 250000 -s 1000 > kmeans-bounds-250000-out.txt
LD_PRELOAD=./libjemalloc.so time pmcstat -d -w 1 -p l1d_tlb_rd -p l2d_tlb_rd -p l1d_tlb_refill -p cpu_cycles -p dtlb_walk -p stall_backend -p ll_cache_miss_rd -o kmeans-alloc-200000.txt ./kmeans-pthread.out -d 40 -c 100 -p 200000 -s 1000 > kmeans-bounds-200000-out.txt
LD_PRELOAD=./libjemalloc.so time pmcstat -d -w 1 -p l1d_tlb_rd -p l2d_tlb_rd -p l1d_tlb_refill -p cpu_cycles -p dtlb_walk -p stall_backend -p ll_cache_miss_rd -o kmeans-alloc-150000.txt ./kmeans-pthread.out -d 40 -c 100 -p 150000 -s 1000 > kmeans-bounds-150000-out.txt
LD_PRELOAD=./libjemalloc.so time pmcstat -d -w 1 -p l1d_tlb_rd -p l2d_tlb_rd -p l1d_tlb_refill -p cpu_cycles -p dtlb_walk -p stall_backend -p ll_cache_miss_rd -o kmeans-alloc-100000.txt ./kmeans-pthread.out -d 40 -c 100 -p 100000 -s 1000 > kmeans-bounds-100000-out.txt
LD_PRELOAD=./regularjemalloc.so time pmcstat -d -w 1 -p l1d_tlb_rd -p l2d_tlb_rd -p l1d_tlb_refill -p cpu_cycles -p dtlb_walk -p stall_backend -p ll_cache_miss_rd -o kmeans-regular-alloc-350000.txt ./kmeans-pthread.out -d 40 -c 100 -p 350000 -s 1000 > kmeans-bounds-regular-350000-out.txt
LD_PRELOAD=./regularjemalloc.so time pmcstat -d -w 1 -p l1d_tlb_rd -p l2d_tlb_rd -p l1d_tlb_refill -p cpu_cycles -p dtlb_walk -p stall_backend -p ll_cache_miss_rd -o kmeans-regular-alloc-300000.txt ./kmeans-pthread.out -d 40 -c 100 -p 300000 -s 1000 > kmeans-bounds-regular-300000-out.txt
LD_PRELOAD=./regularjemalloc.so time pmcstat -d -w 1 -p l1d_tlb_rd -p l2d_tlb_rd -p l1d_tlb_refill -p cpu_cycles -p dtlb_walk -p stall_backend -p ll_cache_miss_rd -o kmeans-regular-alloc-250000.txt ./kmeans-pthread.out -d 40 -c 100 -p 250000 -s 1000 > kmeans-bounds-regular-250000-out.txt
LD_PRELOAD=./regularjemalloc.so time pmcstat -d -w 1 -p l1d_tlb_rd -p l2d_tlb_rd -p l1d_tlb_refill -p cpu_cycles -p dtlb_walk -p stall_backend -p ll_cache_miss_rd -o kmeans-regular-alloc-200000.txt ./kmeans-pthread.out -d 40 -c 100 -p 200000 -s 1000 > kmeans-bounds-regular-200000-out.txt
LD_PRELOAD=./regularjemalloc.so time pmcstat -d -w 1 -p l1d_tlb_rd -p l2d_tlb_rd -p l1d_tlb_refill -p cpu_cycles -p dtlb_walk -p stall_backend -p ll_cache_miss_rd -o kmeans-regular-alloc-150000.txt ./kmeans-pthread.out -d 40 -c 100 -p 150000 -s 1000 > kmeans-bounds-regular-150000-out.txt
LD_PRELOAD=./regularjemalloc.so time pmcstat -d -w 1 -p l1d_tlb_rd -p l2d_tlb_rd -p l1d_tlb_refill -p cpu_cycles -p dtlb_walk -p stall_backend -p ll_cache_miss_rd -o kmeans-regular-alloc-100000.txt ./kmeans-pthread.out -d 40 -c 100 -p 100000 -s 1000 > kmeans-bounds-regular-100000-out.txt
# time pmcstat -d -w 1 -p l1d_tlb_rd -p l2d_tlb_rd -p l1d_tlb_refill -p cpu_cycles -p dtlb_walk -p stall_backend -p ll_cache_miss_rd -o kmeans-regular-alloc-10000.txt ./kmeans-pthread.out -d 40 -c 100 -p 10000 -s 1000 > kmeans-bounds-regular-10000-out.txt
# time pmcstat -d -w 1 -p l1d_tlb_rd -p l2d_tlb_rd -p l1d_tlb_refill -p cpu_cycles -p dtlb_walk -p stall_backend -p ll_cache_miss_rd -o kmeans-regular-alloc-1000.txt ./kmeans-pthread.out -d 40 -c 100 -p 1000 -s 1000 > kmeans-bounds-regular-1000-out.txt

View File

@@ -0,0 +1,177 @@
// /* Copyright (C) 2023. Shivashish Das. Licensed under the MIT License.*/
// #include <stdint.h>
// #include <stdlib.h>
// #include <stdio.h>
// #include <string.h>
// #include <time.h>
// // source: https://www.reddit.com/r/C_Programming/comments/1bt8dyz/github_dasshivamalloc_a_simple_memory_allocator/
// // #include "alloc.h"
// #ifndef _MSC_VER
// #include <sys/mman.h>
// #endif
// /* This is a simple memory allocator meant for use in single threaded applications.
// First we get memory from the system allocator which is defined by the pool size
// Larger the pool size, more is the amount you can allocate before running out of memory.
// On linux, mmap() is used for memory allocation while on windows good old calloc() is used as the system allocator
// Some basic definitions:
// Block - A memory region always of size 16 bytes. This is the basic unit of allocation
// All allocations are made in multiples of blocks. If any allocation request is not a multiple of 16 bytes, we return memory of a size
// that is the closest multiple to 16 and greater than the user requested size.
// Metadata blocks - For every allocation we allocate two extra blocks. These two blocks hold data about the allocation itself
// and serve to prevent buffer overflows too. Check the comment in alloc() to find out more. For example suppose that if the user asks for 80 bytes
// i.e 80 / 16 = 5 blocks, we will allocate 7 blocks but the pointer passed to the user will point to the seconf block so the user is unable to access
// these blocks. They do sound like a waste of some bytes but help provide protection from buffer overflows
// Posioning - This means that user code has overflown the buffer it was allocated. All memory allocated by alloc() is now invalid
// However this may also be caused by the user simply passing an invalid pointer to us i.e a pointer allocated by some other allocator etc.
// In this case the user can clear the poisoned state by calling clear_posion() but be absolutely sure as a user about this before doing so
// */
// static uint8_t* mem = 0;
// static uint8_t* bitmap = 0;
// static uint64_t blocks = 0;
// static uint8_t poison = 0;
// // Always call this before anything else.
// void alloc_init(uint64_t pool) {
// if (pool % 16 != 0) {
// int rem = pool % 16;
// pool += (16 - rem);
// }
// #ifndef _MSC_VER
// mem = mmap(0, pool, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
// if (mem == MAP_FAILED) {
// fprintf(stderr, "alloc_init(): could not allocate heap\n");
// perror("mmap");
// return;
// }
// #else
// mem = calloc(pool, 1);
// if (!mem) {
// fprintf(stderr, "alloc_init(): could not allocate heap\n");
// exit(1);
// }
// #endif
// // Each bitmap entry can represent 8 blocks and each block is 16 bytes
// // So space representable in one uint8_t is 16 * 8 = 128 bytes
// uint64_t sz = pool / 128;
// if (sz == 0)
// sz = 1; // allocate at least one to keep track of small pools
// #ifndef _MSC_VER
// bitmap = mmap(0, sz , PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
// if (bitmap == MAP_FAILED) {
// fprintf(stderr, "alloc_init(): could not allocate bitmap");
// munmap(mem, pool);
// }
// #else
// bitmap = calloc(sz, 1);
// if (!bitmap) {
// fprintf(stderr, "alloc_init(): could not allocate bitmap\n");
// exit(1);
// }
// #endif
// // Zero the entire bitmap
// memset(bitmap, 0, sz);
// blocks = pool / 16;
// }
// #define IS_FREE(blkid) (bitmap[blkid / 8] & (((uint8_t)1) << blkid)) == 0
// #define MARK(blkid) (bitmap[blkid / 8] ^= ((((uint8_t)1) << blkid) - 1))
// // Allocate sz bytes of memory. Caution: May allocate upto 15 bytes more than sz
// void* alloc(uint64_t sz) {
// if (sz % 16 != 0) {
// int rem = sz % 16;
// sz += (16 - rem);
// }
// // Allocate two extra blocks
// // First will be allocated at just behind the first user accessible block
// // This block will have the number of blocks allocated and a randomly generated magic number each 8 bytes long
// // The last block has the "magic number" present in the first block
// // If this magic number gets modified then when free() tries to free the memory
// // Buffer overruns will be caught and this allocator gets poisoned i.e it can no longer allocate memory
// // This is because all blocks are laid out sequentially and if the user overruns the blocks allocated
// // Then the user may have overwritten the contents of other blocks and it is not possible to estimate the damage caused
// // and data corrupted. All pointers to blocks allocated immediately become invalid and free() posions the allocator
// // This helps catch buffer overflows early on
// uint64_t blk = (sz / 16) + 2;
// // if we are posioned, all allocation requests will fail
// if (poison)
// return 0;
// // Loop through the entire bitmap. If a free block is found, check if there are at least blk free blocks after it.
// // If such a contigious group of blocks is found, take appropriate actions and return to user
// // Otherwise we have ran out of memory so inform the user about it
// for (uint64_t i = 0; i < blocks; i++) {
// if (IS_FREE(i)) {
// // Check for contigious free blocks
// for (uint64_t j = i; j < (i + blk); j++) {
// if (!IS_FREE(j))
// goto next;
// }
// // Mark all free blocks
// for (uint64_t j = i; j < (i + blk + 1); j++) {
// MARK(j);
// }
// uint64_t* ptr = mem + (i * 16);
// *ptr = blk;
// // I needed a number which was large enough to occupy 8 bytes so rand() is not enough as in most cases RAND_MAX is only USHORT_MAX
// // Instead use time() which returns a 64 bit value and is almost guaranteed to be unique on every call to alloc()
// uint64_t magic = time(0);
// *(ptr + 1) = magic;
// // Store a magic number in the last block. For the reason see free_mem()
// ptr = mem + (i * 16) + ((blk - 1) * 16);
// *ptr = magic;
// *(ptr + 1) = magic;
// // Return the user a pointer which points to the region just above our metadata block
// return mem + ((i + 1) * 16);
// }
// next:
// }
// fprintf(stderr, "Pool has been exhausted...Cannot allocate more memory");
// return 0;
// }
// // Frees memory allocated by alloc()
// void free_mem(void* data) {
// // First get the number of blocks allocated and magic from the metadata block (i.e the block right behind what alloc() returned)
// uint64_t* ptr = data;
// ptr -= 2;
// uint64_t blk = *ptr;
// ptr++;
// uint64_t magic = *ptr;
// // The magic is stored in the last block of the allocation
// // Compare the two magic values
// // If they are equal, this memory block was allocated by us and we can free this
// // Otherwise the buffer has been overflown which has overwritten the magic number or this was not allocated by alloc() and is not ours to deal with
// ptr = data + (blk - 2) * 16;
// if (magic != *ptr) {
// // If the buffer has overflown then mark this allocator posioned.
// // You may change the poison back to 0 in your code but be careful and do this only if you know that the buffer was not overrun
// fprintf(stderr, "Invalid pointer or buffer overrun detected..Poisoning ourself");
// poison = 1;
// return;
// }
// uint64_t offset = ((uint8_t*) data) - mem;
// offset -= 16;
// offset /= 16;
// // Clear all bits representing this block so next call to alloc() can use this
// for (uint64_t j = offset; j < offset + blk + 1; j++) {
// MARK(j);
// }
// }
// // Do not call this unless you are absolutely sure about the cause of poisoning
// void clear_posion() {
// poison = 0;
// }

View File

@@ -0,0 +1,298 @@
/* Copyright (c) 2007-2009, Stanford University
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Stanford University nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY STANFORD UNIVERSITY ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL STANFORD UNIVERSITY BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef STDDEFINES_H_
#define STDDEFINES_H_
#include <assert.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/errno.h>
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <cheriintrin.h>
#include <cheri/cheric.h>
#include <sys/stat.h>
#include <fcntl.h>
#define MAXPAGESIZES 2
//#define TIMING
/* Debug printf */
#define dprintf(...) fprintf(stdout, __VA_ARGS__)
/* Wrapper to check for errors */
#define CHECK_ERROR(a) \
if (a) \
{ \
perror("Error at line\n\t" #a "\nSystem Msg"); \
assert ((a) == 0); \
}
static inline void *MALLOC(size_t size)
{
void * temp = malloc(size);
assert(temp);
return temp;
}
static inline void *CALLOC(size_t num, size_t size)
{
void * temp = calloc(num, size);
assert(temp);
return temp;
}
static inline void *REALLOC(void *ptr, size_t size)
{
void * temp = realloc(ptr, size);
assert(temp);
return temp;
}
static inline char *GETENV(char *envstr)
{
char *env = getenv(envstr);
if (!env) return "0";
else return env;
}
#define GET_TIME(start, end, duration) \
duration.tv_sec = (end.tv_sec - start.tv_sec); \
if (end.tv_nsec >= start.tv_nsec) { \
duration.tv_nsec = (end.tv_nsec - start.tv_nsec); \
} \
else { \
duration.tv_nsec = (1000000000L - (start.tv_nsec - end.tv_nsec)); \
duration.tv_sec--; \
} \
if (duration.tv_nsec >= 1000000000L) { \
duration.tv_sec++; \
duration.tv_nsec -= 1000000000L; \
}
static inline unsigned int time_diff (
struct timeval *end, struct timeval *begin)
{
#ifdef TIMING
uint64_t result;
result = end->tv_sec - begin->tv_sec;
result *= 1000000; /* usec */
result += end->tv_usec - begin->tv_usec;
return result;
#else
return 0;
#endif
}
static inline void get_time (struct timeval *t)
{
#ifdef TIMING
gettimeofday (t, NULL);
#endif
}
// Expirement work
#define FILENAME "/dev/contigmem"
static char *heap_start;
static char *heap;
static size_t HEAP_SIZE = 1024 * 1024 * 1024;
void *ptr;
int MallocCounter;
size_t sizeUsed;
INITAlloc(void) {
size_t sz;
// Pre Allocate 600 MB
sz = 100000000;
int fd = open(FILENAME, O_RDWR, 0600);
if (fd < 0) {
perror("open");
exit(EXIT_FAILURE);
}
off_t offset = 0; // offset to seek to.
if (ftruncate(fd, sz) < 0) {
perror("ftruncate");
close(fd);
exit(EXIT_FAILURE);
}
// ptr = mmap(NULL, sz,
// PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON,-1,0);
ptr = mmap(NULL, sz,
PROT_READ|PROT_WRITE, MAP_SHARED,fd,0);
// Added error handling
if(ptr == MAP_FAILED)
{
perror("mmap");
exit(EXIT_FAILURE);
}
MallocCounter = (int)sz;
}
// Quick malloc implementation with mmap
void* MALLOCCHERI(size_t sz)
{
sz = __builtin_align_up(sz, _Alignof(max_align_t));
// printf("%d \n", sz);
// printf("%d Malloc counter\n", MallocCounter);
MallocCounter -= sz;
void *ptrLink = &ptr[MallocCounter];
ptrLink = cheri_setbounds(ptrLink, sz);
return ptrLink;
// if (heap + sz > heap_start + HEAP_SIZE) return NULL;
// heap += sz;
// return heap - sz;
}
// Quick cheri free implementation
void FREECHERI(void *ptr) {
// printf("free called \n");
// get bounds from
int len = cheri_getlen(ptr);
// printf("free len %d \n", len);
munmap(ptr, len);
}
static int
pagesizes(size_t ps[MAXPAGESIZES])
{
int pscnt;
pscnt = getpagesizes(ps, MAXPAGESIZES);
// ATF_REQUIRE_MSG(pscnt != -1, "getpagesizes failed; errno=%d", errno);
// ATF_REQUIRE_MSG(ps[0] != 0, "psind 0 is %zu", ps[0]);
// ATF_REQUIRE_MSG(pscnt <= MAXPAGESIZES, "invalid pscnt %d", pscnt);
// if (pscnt == 1){
// printf("pscnt is 1");
// }
// atf_tc_skip("no large page support");
return (pscnt);
}
INITREGULARALLOC(void) {
size_t sz;
// Pre Allocate 400 MB
sz = 1073741824;
int error, fd, pscnt, pn;
size_t ps[MAXPAGESIZES];
size_t size[3];
pn = getpagesizes(size, 3);
printf("page size is [%d]", size[2]);
pscnt = pagesizes(ps);
fd = shm_create_largepage(SHM_ANON, O_CREAT | O_RDWR, 1, SHM_LARGEPAGE_ALLOC_DEFAULT, 0);
if (fd < 0 && errno == ENOTTY) {
perror("sh_create_largepages");
close(fd);
exit(EXIT_FAILURE);
}
// if (fd < 0)
// perror("no large page supported");
// exit(EXIT_FAILURE);
// if (fd < 0 && errno == ENOTTY)
// atf_tc_skip("no large page support");
// ATF_REQUIRE_MSG(fd >= 0, "shm_create_largepage failed; errno=%d", errno);
if (ftruncate(fd, sz) < 0) {
perror("ftruncate");
close(fd);
exit(EXIT_FAILURE);
}
// if (error != 0 && errno == ENOMEM)
// /*
// * The test system might not have enough memory to accommodate
// * the request.
// */
// atf_tc_skip("failed to allocate %zu-byte superpage", sz);
// ATF_REQUIRE_MSG(error == 0, "ftruncate failed; errno=%d", errno);
ptr = mmap(NULL, sz,
PROT_READ|PROT_WRITE, MAP_SHARED,fd,0);
// Added error handling
if(ptr == MAP_FAILED)
{
perror("mmap");
exit(EXIT_FAILURE);
}
MallocCounter = (int)sz;
}
// Standard Alloc
// void* MALLOCREGULAR(size_t sz) {
// }
// void* CLEARALLOC(void) {
// /
// }
#endif // STDDEFINES_H_

View File

@@ -1,7 +1,7 @@
# build glibc
cc -g -Wall -o glibc-bench.out -march=morello -mabi=purecap -Xclang -morello-vararg=new -lpthread glibc.c
cc -g -Wall -o glibc-bench.out -march=morello -mabi=purecap -Xclang -morello-vararg=new -lpthread simple_c.c
# build shared object library
cc -O3 -g -W -Wall -shared -o ./malloc.so -mabi=purecap -Wno-unused-parameter -lpthread -fPIC alloc.c
cc -O3 -g -W -Wall -shared -o ./malloc.so -mabi=purecap -Wno-unused-parameter -lpthread -fPIC allocBitmap.c
LD_PRELOAD=malloc.so ./glibc-bench.out

View File

@@ -0,0 +1,175 @@
#include <errno.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <sys/types.h>
#include <cheriintrin.h>
#include <cheri/cheric.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#include <sys/time.h>
#include <sys/errno.h>
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
#define MAXPAGESIZES 2
static char *heap_start;
static char *heap;
static size_t HEAP_SIZE = 1024 * 1024 * 1024;
void *ptr;
int MallocCounter;
int malloc_called = 0;
size_t sizeUsed;
// Instrcutor allocator to create the huge page
// of 1 GB
// __attribute__((constructor))
static void INITREGULARALLOC() {
size_t sz;
// Hard-coded for 1GB huge page
sz = 1073741824;
int error, fd, pscnt, pn;
size_t ps[2];
size_t size[3];
// pn = getpagesizes(size, 3);
// printf("page size is [%d]", size[2]);
// pscnt = pagesizes(ps);
fd = shm_create_largepage(SHM_ANON, O_CREAT | O_RDWR, 1, SHM_LARGEPAGE_ALLOC_DEFAULT, 0);
if (fd < 0 && errno == ENOTTY) {
perror("sh_create_largepages");
close(fd);
exit(EXIT_FAILURE);
}
// if (fd < 0)
// perror("no large page supported");
// exit(EXIT_FAILURE);
// if (fd < 0 && errno == ENOTTY)
// atf_tc_skip("no large page support");
// ATF_REQUIRE_MSG(fd >= 0, "shm_create_largepage failed; errno=%d", errno);
if (ftruncate(fd, sz) < 0) {
perror("ftruncate");
close(fd);
exit(EXIT_FAILURE);
}
// if (error != 0 && errno == ENOMEM)
// /*
// * The test system might not have enough memory to accommodate
// * the request.
// */
// atf_tc_skip("failed to allocate %zu-byte superpage", sz);
// ATF_REQUIRE_MSG(error == 0, "ftruncate failed; errno=%d", errno);
ptr = mmap(NULL, sz,
PROT_READ|PROT_WRITE, MAP_SHARED,fd,0);
// Added error handling
if(ptr == MAP_FAILED)
{
perror("mmap");
exit(EXIT_FAILURE);
}
// fprintf(stderr, "heap used alloc %lu\n", heap - heap_start);
MallocCounter = (int)sz;
}
// -- Custom malloc and free functions written
// This will be replaced with mmap since we already
// do a initial mmap.
int notrun1 = 0;
void *MALLOCCHERI(size_t sz)
{
if (notrun1 == 0){
INITREGULARALLOC();
notrun1 = 1;
}
sz = __builtin_align_up(sz, _Alignof(max_align_t));
// printf("%d \n", sz);
// printf("%d Malloc counter\n", MallocCounter);
MallocCounter -= sz;
void *ptrLink = &ptr[MallocCounter];
ptrLink = cheri_setbounds(ptrLink, sz);
return ptrLink;
// if (heap + sz > heap_start + HEAP_SIZE) return NULL;
// heap += sz;
// return heap - sz;
}
// Quick cheri free implementation
void FREECHERI(void *ptr) {
// printf("free called \n");
// get bounds from
int len = cheri_getlen(ptr);
// printf("free len %d \n", len);
munmap(ptr, len);
}
__attribute__((destructor))
static void malloc_exit() {
fprintf(stderr, "heap used %lu\n", malloc_called);
}
// void *malloc(size_t sz) {
// // if (!heap) heap = heap_start = mmap(NULL, HEAP_SIZE,
// // PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON,-1,0);
// // char *new_ptr = __builtin_align_up(
// // heap, -cheri_representable_alignment_mask(sz));
// // size_t bounds = cheri_representable_length(sz);
// // sz = __builtin_align_up(sz, _Alignof(max_align_t));
// // if (new_ptr + sz > heap_start + HEAP_SIZE)
// // return NULL;
// // heap = new_ptr + sz;
// // return cheri_bounds_set_exact(new_ptr, bounds);
// return MALLOCCHERI(sz);
// }
// void *malloc(size_t sz) {
// // if (!heap) heap = heap_start = mmap(NULL, HEAP_SIZE,
// // PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON,-1,0);
// // sz = __builtin_align_up(sz, _Alignof(max_align_t));
// // if (heap + sz > heap_start + HEAP_SIZE) return NULL;
// // heap += sz;
// // return heap - sz;
// malloc_called += 1;
// return MALLOCCHERI(sz);
// }
// void free(void *ptr) {
// FREECHERI(ptr);
// }

View File

@@ -206,7 +206,7 @@ main (int argc, char **argv)
usage (argv[0]);
// bench (size);
bench (2*size);
bench (size);
printf("done");
//bench (4*size);
//bench (8*size);

View File

@@ -0,0 +1,215 @@
/**********************************
* bitmap_alloc.c
* Jeremy.Singer@glasgow.ac.uk
*
* This is a simple fixed-size bitmap allocator.
* It mmaps a large buffer of
* NUM_CHUNKS * CHUNK_SIZE bytes
* then allocates this space in equally-sized
* chunks to client code.
* A side bitmap is required to keep track of which
* chunks are in use (corresponding bit set to 1)
* and which chunks are free (corresponding bit
* set to 0). There is one bit per allocatable chunk.
*
* This is _not_ a clever allocator, since it
* does a linear scan of the bitmap to find the
* first free chunk, which is expensive!
* More efficient scans could be easily incorporated.
*
* This is _not_ a general-purpose allocator, since
* it only allocates chunks of a fixed size. Further,
* this size is constrained to be small enough to allow
* exact bounds representation in CHERI capabilities.
*
* This is an initial simple memory allocator test
* for CHERI / Capable VMs.
* We explore capability alignment,
* representable bounds, narrowing operations
* and compiler intrinsic support.
*/
#include <assert.h>
#include <cheriintrin.h>
#include <cheri/cheric.h>
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
// #include "bitmap_alloc.h"
#define BITS_PER_BYTE 8
char *buffer = NULL; /* allocation buffer */
unsigned char *bitmap = NULL; /* bitmap for the buffer */
int buffer_size = 0; /* size of buffer (in bytes) */
int bitmap_size = 0; /* size of bitmap (in bytes) */
int bytes_per_chunk = 0; /* size of single chunk (in bytes) */
void init_alloc(int num_chunks, int chunk_size)
{
int i = 0;
/* we need to increase the num_chunks
* so every bit in bitmap will be used
*/
int adjusted_num_chunks = (num_chunks % BITS_PER_BYTE == 0)
? num_chunks
: (num_chunks + (BITS_PER_BYTE - (num_chunks % BITS_PER_BYTE)));
/* we need to increase the chunk_size
* so chunks will be CHERI aligned
* (i.e. 16 bytes for RISC-V 64-bit arch)
*/
int adjusted_chunk_size =
(chunk_size % (sizeof(void *)) == 0)
? chunk_size
: (chunk_size + (sizeof(void *)) - (chunk_size % (sizeof(void *))));
/* check this chunk size is small enough so we can represent
* bounds precisely with CHERI compressed representation
*/
adjusted_chunk_size = cheri_representable_length(adjusted_chunk_size);
/* request memory for our allocation buffer */
char *res = mmap(NULL, adjusted_num_chunks * adjusted_chunk_size, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE, -1, 0);
/* request memory for our bitmap */
bitmap = (unsigned char *) mmap(NULL, adjusted_num_chunks / BITS_PER_BYTE,
PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
if (res == MAP_FAILED || bitmap == MAP_FAILED)
{
perror("error in initial mem allocation");
exit(-1);
}
/* NB mmap min bounds for capability is 1 page (4K) */
buffer = res;
/* check buffer is aligned */
assert((uintptr_t) buffer % sizeof(void *) == 0);
/* check bitmap is aligned */
assert((uintptr_t) bitmap % sizeof(void *) == 0);
bytes_per_chunk = adjusted_chunk_size;
buffer_size = adjusted_num_chunks * adjusted_chunk_size;
bitmap_size = adjusted_num_chunks / BITS_PER_BYTE;
/* zero bitmap, since all chunks are free initially */
for (i = 0; i < bitmap_size; i++)
{
bitmap[i] = 0;
}
// set exact bounds for buffer and bitmap?
buffer = cheri_setbounds(buffer, buffer_size);
bitmap = cheri_setbounds(bitmap, bitmap_size);
return;
}
/*
* allocate fixed size chunk with bitmap allocator
* this is our simplistic `malloc` function
*/
// Length is not used but just kept
// to keep the integrity of the
// malloc shape.
int notrun = 0;
void *malloc(size_t len)
{
if (notrun == 0){
init_alloc(500,1000);
notrun = 1;
}
unsigned char updated_byte = 0;
int chunk_index = 0;
char *chunk = NULL;
// iterate over all bits in bitmap, looking for a 0
// when we find a 0, set it to 1 and
// return the corresponding chunk
// (setting its capability bounds)
int i = 0;
while (bitmap[i] == (unsigned char) 0xff)
{
i++;
if (i >= bitmap_size)
break;
}
// do we have a 0?
if (i < bitmap_size && bitmap[i] != (unsigned char) 0xff)
{
// find the lowest 0 ...
int j = 0;
// right shift until bottom bit is 0
for (j = 0; j < BITS_PER_BYTE; j++)
{
int bit = (bitmap[i] >> j) & 1;
if (bit == 0)
{
break;
}
}
// now i is the word index, j is the bit index
// set this bit to 1 ...
// and work out the chunk to allocate
updated_byte = bitmap[i] + (unsigned char) (1 << j);
bitmap[i] = updated_byte;
chunk_index = i * BITS_PER_BYTE + j;
chunk = buffer + (chunk_index * bytes_per_chunk);
/* restrict capability range before returning ptr */
chunk = cheri_setbounds(chunk, bytes_per_chunk);
}
return chunk;
}
void free(void *chunk)
{
vaddr_t base = cheri_getbase(chunk);
vaddr_t buff_base = cheri_getbase(buffer);
/* calculate chunk index in buffer */
int chunk_index = (base - buff_base) / bytes_per_chunk;
assert(chunk_index >= 0);
/* calculate corresponding bitmap index */
int bitmap_index = chunk_index / BITS_PER_BYTE;
assert(bitmap_index < bitmap_size);
int bitmap_offset = chunk_index % BITS_PER_BYTE;
/* set this bitmap entry to 0 */
unsigned char updated_byte = bitmap[bitmap_index] & (unsigned char) (~(1 << bitmap_offset));
bitmap[bitmap_index] = updated_byte;
return;
}
int num_used_chunks()
{
int i = 0;
int used_chunks = 0;
while (i < bitmap_size)
{
unsigned char x = bitmap[i];
if (x != 0)
{
/* some used chunks here */
unsigned char j;
for (j = 1; j <= x; j = j << 1)
{
if (x & j)
{
used_chunks++;
}
}
}
i++;
}
return used_chunks;
}

View File

@@ -0,0 +1,40 @@
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr;
int n, i;
printf("Enter the number of elements: ");
scanf("%d", &n);
// Allocate memory for n integers
ptr = (int *)malloc(n * sizeof(int));
// Check if memory has been successfully allocated
if (ptr == NULL) {
printf("Memory not allocated.\n");
return 1; // Exit the program
}
// Memory has been successfully allocated
printf("Memory successfully allocated using malloc.\n");
// Get the elements
for (i = 0; i < n; i++) {
printf("Enter element %d: ", i + 1);
scanf("%d", &ptr[i]);
}
// Print the elements
printf("The elements are: ");
for (i = 0; i < n; i++) {
printf("%d ", ptr[i]);
}
printf("\n");
// Free the allocated memory
free(ptr);
printf("Memory successfully freed.\n");
return 0;
}