added XSBench

This commit is contained in:
2024-12-12 17:18:16 +00:00
parent c9ba5e5d8c
commit 60de350372
30 changed files with 2713 additions and 224 deletions

View File

@@ -1,20 +1,116 @@
add_compile_options(-g -O3 -DNDEBUG -gdwarf-3)
cmake_minimum_required(VERSION 3.0)
project(mimalloc-bench CXX C)
set(CMAKE_CXX_STANDARD 17)
function(add_coz_run_target name)
set(one_value_args "")
set(multi_value_args COMMAND)
set(options "")
cmake_parse_arguments(COZ_RUN "${options}" "${one_value_args}" "${multi_value_args}" ${ARGN})
if (NOT CMAKE_BUILD_TYPE)
message(STATUS "No build type selected, default to *** Release ***")
set(CMAKE_BUILD_TYPE "Release")
endif()
add_custom_target(${name}
COMMENT "Running coz for ${name}"
COMMAND ${PROJECT_SOURCE_DIR}/coz run --- ${COZ_RUN_COMMAND}
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
DEPENDS coz)
endfunction()
FUNCTION(PREPEND var prefix)
SET(listVar "")
FOREACH(f ${ARGN})
LIST(APPEND listVar "${prefix}/${f}")
ENDFOREACH(f)
SET(${var} "${listVar}" PARENT_SCOPE)
ENDFUNCTION(PREPEND)
file(GLOB cmake_files */CMakeLists.txt)
foreach(filepath ${cmake_files})
get_filename_component(dir ${filepath} DIRECTORY)
add_subdirectory(${dir})
endforeach(filepath)
set(cfrac_sources
cfrac.c
pops.c pconst.c pio.c
pabs.c pneg.c pcmp.c podd.c phalf.c
padd.c psub.c pmul.c pdivmod.c psqrt.c ppowmod.c
atop.c ptoa.c itop.c utop.c ptou.c errorp.c
pfloat.c pidiv.c pimod.c picmp.c
primes.c pcfrac.c pgcd.c)
PREPEND(cfrac_sources cfrac/ ${cfrac_sources})
set(espresso_sources
cofactor.c cols.c compl.c contain.c cubestr.c cvrin.c cvrm.c cvrmisc.c cvrout.c
dominate.c equiv.c espresso.c essen.c exact.c expand.c gasp.c getopt.c gimpel.c
globals.c hack.c indep.c irred.c main.c map.c matrix.c mincov.c opo.c pair.c part.c
primes.c reduce.c rows.c set.c setc.c sharp.c sminterf.c solution.c sparse.c unate.c
utility.c verify.c)
PREPEND(espresso_sources espresso/ ${espresso_sources})
set(barnes_sources
code.c code_io.c load.c grav.c getparam.c util.c)
PREPEND(barnes_sources barnes/ ${barnes_sources})
# turn off warnings..
message(STATUS "${CMAKE_C_COMPILER_ID}")
if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|Clang|GNU")
set(FLAGS " -w -Wno-implicit-function-declaration -Wno-implicit-int -Wno-int-conversion -Wno-return-mismatch -Wno-incompatible-pointer-types -mabi=purecap-benchmark")
string(APPEND CMAKE_C_FLAGS ${FLAGS})
string(APPEND CMAKE_CXX_FLAGS ${FLAGS})
endif()
add_executable(cfrac ${cfrac_sources})
target_compile_options(cfrac PRIVATE $<$<C_COMPILER_ID:GNU>:-std=gnu89>)
target_compile_definitions(cfrac PRIVATE NOMEMOPT=1)
target_link_libraries(cfrac m)
add_executable(espresso ${espresso_sources})
target_compile_options(espresso PRIVATE $<$<C_COMPILER_ID:GNU>:-std=gnu89>)
target_link_libraries(espresso m)
# add_executable(barnes ${barnes_sources})
# target_link_libraries(barnes m)
# add_executable(larson larson/larson.cpp)
# target_compile_options(larson PRIVATE -Wno-unused-result)
# target_compile_definitions(larson PRIVATE CPP=1)
# target_link_libraries(larson pthread)
# add_executable(larson-sized larson/larson.cpp)
# target_compile_options(larson-sized PRIVATE -Wno-unused-result -fsized-deallocation)
# target_compile_definitions(larson-sized PRIVATE CPP=1 SIZED=1)
# target_link_libraries(larson-sized pthread)
# add_executable(alloc-test alloc-test/test_common.cpp alloc-test/allocator_tester.cpp)
# target_compile_definitions(alloc-test PRIVATE BENCH=4)
# target_link_libraries(alloc-test pthread)
# if(NOT APPLE)
# add_executable(sh6bench shbench/sh6bench-new.c)
# target_compile_definitions(sh6bench PRIVATE BENCH=1 SYS_MULTI_THREAD=1)
# target_link_libraries(sh6bench pthread)
# add_executable(sh8bench shbench/sh8bench-new.c)
# target_compile_definitions(sh8bench PRIVATE BENCH=1 SYS_MULTI_THREAD=1)
# target_link_libraries(sh8bench pthread)
# endif()
# add_executable(cache-scratch cache-scratch/cache-scratch.cpp)
# target_link_libraries(cache-scratch pthread)
# add_executable(cache-thrash cache-thrash/cache-thrash.cpp)
# target_link_libraries(cache-thrash pthread)
add_executable(xmalloc-test xmalloc-test/xmalloc-test.c)
target_link_libraries(xmalloc-test pthread)
# add_executable(malloc-large-old malloc-large/malloc-large-old.cpp)
# target_link_libraries(malloc-large-old pthread)
# add_executable(malloc-large malloc-large/malloc-large.cpp)
# target_link_libraries(malloc-large pthread)
# add_executable(mstress mstress/mstress.c)
# target_link_libraries(mstress pthread)
# add_executable(mleak mleak/mleak.c)
# target_link_libraries(mleak pthread)
# add_executable(rptest rptest/rptest.c rptest/thread.c rptest/timer.c)
# target_compile_options(rptest PRIVATE -fpermissive)
# target_include_directories(rptest PRIVATE rptest)
# target_link_libraries(rptest pthread m)
# add_executable(glibc-simple glibc-bench/bench-malloc-simple.c)
# target_link_libraries(glibc-simple pthread)
# add_executable(glibc-thread glibc-bench/bench-malloc-thread.c)
# target_link_libraries(glibc-thread pthread)
#

View File

@@ -24,8 +24,8 @@
#include <sys/resource.h>
#include "malloc.h"
#define malloc MALLOCCHERI
#define free FREECHERI
// #define malloc MALLOCCHERI
// #define free FREECHERI
// #include "bench-timing.h"
// #include "json-lib.h"
@@ -197,7 +197,7 @@ static void usage (const char *name)
int
main (int argc, char **argv)
{
INITREGULARALLOC();
//INITREGULARALLOC();
long size = 16;
if (argc == 2)
size = strtol (argv[1], NULL, 0);
@@ -205,9 +205,12 @@ main (int argc, char **argv)
if (argc > 2 || size <= 0)
usage (argv[0]);
bench (size);
// bench (size);
bench (2*size);
bench (4*size);
//bench (4*size);
//bench (8*size);
// bench (16*size);
// bench (32*size);
return 0;
}

View File

@@ -1,2 +1,2 @@
sh build.sh
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 glibc-cheri.stat ./glibc-bench.out
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 glibc-regular-2.stat ./glibc-bench.out

View File

@@ -2,8 +2,8 @@
#include <stdlib.h>
#include "malloc.h"
#define malloc MALLOCCHERI
#define free FREECHERI
// #define malloc MALLOCCHERI
// #define free FREECHERI
// performing various sizes of mallocs are free
void
@@ -22,21 +22,25 @@ bench (unsigned long n) {
for (i = 0; i < n; i++){
ptr[i] = 1;
}
printf("Sleep 10 seconds......\n");
sleep(10);
printf("Free memory.\n");
// printf("Sleep 10 seconds......\n");
// sleep(10);
// printf("Free memory.\n");
free(ptr);
}
int main(){
INITREGULARALLOC();
bench(8);
bench(30);
// INITREGULARALLOC();
// bench(8);
// bench(30);
// bench(100);
// bench(600);
// Run one by one
bench(10);
bench(100);
bench(600);
bench(1000);
bench(7000);
bench(10000);
bench(100000);
// int *ptr;
// unsigned long i,n;

View File

@@ -20,8 +20,8 @@ All rights reserved.
#include "malloc.h"
#define malloc MALLOCCHERI
#define free FREECHERI
// #define malloc MALLOCCHERI
// #define free FREECHERI
@@ -266,7 +266,7 @@ void * walk(void *param)
int main(int argc,char **argv)
{
INITREGULARALLOC();
// INITREGULARALLOC();
#ifdef DEBUG
printf("DEBUG:sizeof(struct l)=%ld\n",sizeof(struct l));
#endif

View File

@@ -1,2 +1,2 @@
sh build.sh
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 memaccesstest-cheri.stat ./memaccesstest.out
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 memaccesstest.stat ./memaccesstest.out

View File

@@ -1,2 +1,2 @@
sh build.sh
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 loadmem-cheri.stats ./loadmem.out
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 loadmem-regular.stat ./loadmem.out

View File

@@ -0,0 +1,230 @@
#include "XSbench_header.h"
SimulationData grid_init_do_not_profile( Inputs in, int mype )
{
// Structure to hold all allocated simuluation data arrays
SimulationData SD;
// Keep track of how much data we're allocating
size_t nbytes = 0;
// Set the initial seed value
uint64_t seed = 42;
// loop variable
long e = 0;
////////////////////////////////////////////////////////////////////
// Initialize Nuclide Grids
////////////////////////////////////////////////////////////////////
if(mype == 0) printf("Intializing nuclide grids...\n");
// First, we need to initialize our nuclide grid. This comes in the form
// of a flattened 2D array that hold all the information we need to define
// the cross sections for all isotopes in the simulation.
// The grid is composed of "NuclideGridPoint" structures, which hold the
// energy level of the grid point and all associated XS data at that level.
// An array of structures (AOS) is used instead of
// a structure of arrays, as the grid points themselves are accessed in
// a random order, but all cross section interaction channels and the
// energy level are read whenever the gridpoint is accessed, meaning the
// AOS is more cache efficient.
// Initialize Nuclide Grid
SD.length_nuclide_grid = in.n_isotopes * in.n_gridpoints;
SD.nuclide_grid = (NuclideGridPoint *) malloc( SD.length_nuclide_grid * sizeof(NuclideGridPoint));
assert(SD.nuclide_grid != NULL);
nbytes += SD.length_nuclide_grid * sizeof(NuclideGridPoint);
for( int i = 0; i < SD.length_nuclide_grid; i++ )
{
SD.nuclide_grid[i].energy = LCG_random_double(&seed);
SD.nuclide_grid[i].total_xs = LCG_random_double(&seed);
SD.nuclide_grid[i].elastic_xs = LCG_random_double(&seed);
SD.nuclide_grid[i].absorbtion_xs = LCG_random_double(&seed);
SD.nuclide_grid[i].fission_xs = LCG_random_double(&seed);
SD.nuclide_grid[i].nu_fission_xs = LCG_random_double(&seed);
}
// Sort so that each nuclide has data stored in ascending energy order.
for( int i = 0; i < in.n_isotopes; i++ )
qsort( &SD.nuclide_grid[i*in.n_gridpoints], in.n_gridpoints, sizeof(NuclideGridPoint), NGP_compare);
// error debug check
/*
for( int i = 0; i < in.n_isotopes; i++ )
{
printf("NUCLIDE %d ==============================\n", i);
for( int j = 0; j < in.n_gridpoints; j++ )
printf("E%d = %lf\n", j, SD.nuclide_grid[i * in.n_gridpoints + j].energy);
}
*/
////////////////////////////////////////////////////////////////////
// Initialize Acceleration Structure
////////////////////////////////////////////////////////////////////
if( in.grid_type == NUCLIDE )
{
SD.length_unionized_energy_array = 0;
SD.length_index_grid = 0;
}
if( in.grid_type == UNIONIZED )
{
if(mype == 0) printf("Intializing unionized grid...\n");
// Allocate space to hold the union of all nuclide energy data
SD.length_unionized_energy_array = in.n_isotopes * in.n_gridpoints;
SD.unionized_energy_array = (double *) malloc( SD.length_unionized_energy_array * sizeof(double));
assert(SD.unionized_energy_array != NULL );
nbytes += SD.length_unionized_energy_array * sizeof(double);
// Copy energy data over from the nuclide energy grid
for( int i = 0; i < SD.length_unionized_energy_array; i++ )
SD.unionized_energy_array[i] = SD.nuclide_grid[i].energy;
// Sort unionized energy array
qsort( SD.unionized_energy_array, SD.length_unionized_energy_array, sizeof(double), double_compare);
// Allocate space to hold the acceleration grid indices
SD.length_index_grid = SD.length_unionized_energy_array * in.n_isotopes;
SD.index_grid = (int *) malloc( SD.length_index_grid * sizeof(int));
assert(SD.index_grid != NULL);
nbytes += SD.length_index_grid * sizeof(int);
// Generates the double indexing grid
int * idx_low = (int *) calloc( in.n_isotopes, sizeof(int));
assert(idx_low != NULL );
double * energy_high = (double *) malloc( in.n_isotopes * sizeof(double));
assert(energy_high != NULL );
for( int i = 0; i < in.n_isotopes; i++ )
energy_high[i] = SD.nuclide_grid[i * in.n_gridpoints + 1].energy;
for( long e = 0; e < SD.length_unionized_energy_array; e++ )
{
double unionized_energy = SD.unionized_energy_array[e];
for( long i = 0; i < in.n_isotopes; i++ )
{
if( unionized_energy < energy_high[i] )
SD.index_grid[e * in.n_isotopes + i] = idx_low[i];
else if( idx_low[i] == in.n_gridpoints - 2 )
SD.index_grid[e * in.n_isotopes + i] = idx_low[i];
else
{
idx_low[i]++;
SD.index_grid[e * in.n_isotopes + i] = idx_low[i];
energy_high[i] = SD.nuclide_grid[i * in.n_gridpoints + idx_low[i] + 1].energy;
}
}
}
free(idx_low);
free(energy_high);
}
if( in.grid_type == HASH )
{
if(mype == 0) printf("Intializing hash grid...\n");
SD.length_unionized_energy_array = 0;
SD.length_index_grid = in.hash_bins * in.n_isotopes;
SD.index_grid = (int *) malloc( SD.length_index_grid * sizeof(int));
assert(SD.index_grid != NULL);
nbytes += SD.length_index_grid * sizeof(int);
double du = 1.0 / in.hash_bins;
// For each energy level in the hash table
#pragma omp parallel for
for( e = 0; e < in.hash_bins; e++ )
{
double energy = e * du;
// We need to determine the bounding energy levels for all isotopes
for( long i = 0; i < in.n_isotopes; i++ )
{
SD.index_grid[e * in.n_isotopes + i] = grid_search_nuclide( in.n_gridpoints, energy, SD.nuclide_grid + i * in.n_gridpoints, 0, in.n_gridpoints-1);
}
}
}
////////////////////////////////////////////////////////////////////
// Initialize Materials and Concentrations
////////////////////////////////////////////////////////////////////
if(mype == 0) printf("Intializing material data...\n");
// Set the number of nuclides in each material
SD.num_nucs = load_num_nucs(in.n_isotopes);
SD.length_num_nucs = 12; // There are always 12 materials in XSBench
// Intialize the flattened 2D grid of material data. The grid holds
// a list of nuclide indices for each of the 12 material types. The
// grid is allocated as a full square grid, even though not all
// materials have the same number of nuclides.
SD.mats = load_mats(SD.num_nucs, in.n_isotopes, &SD.max_num_nucs);
SD.length_mats = SD.length_num_nucs * SD.max_num_nucs;
// Intialize the flattened 2D grid of nuclide concentration data. The grid holds
// a list of nuclide concentrations for each of the 12 material types. The
// grid is allocated as a full square grid, even though not all
// materials have the same number of nuclides.
SD.concs = load_concs(SD.num_nucs, SD.max_num_nucs);
SD.length_concs = SD.length_mats;
// Allocate and initialize replicas
#ifdef AML
// num_nucs
aml_replicaset_hwloc_create(&(SD.num_nucs_replica),
SD.length_num_nucs * sizeof(*(SD.num_nucs)),
HWLOC_OBJ_CORE,
HWLOC_DISTANCES_KIND_FROM_OS |
HWLOC_DISTANCES_KIND_MEANS_LATENCY);
nbytes += (SD.num_nucs_replica)->n * (SD.num_nucs_replica)->size;
aml_replicaset_init(SD.num_nucs_replica, SD.num_nucs);
// concs
aml_replicaset_hwloc_create(&(SD.concs_replica),
SD.length_concs * sizeof(*(SD.concs)),
HWLOC_OBJ_CORE,
HWLOC_DISTANCES_KIND_FROM_OS |
HWLOC_DISTANCES_KIND_MEANS_LATENCY);
nbytes += (SD.concs_replica)->n * (SD.concs_replica)->size;
aml_replicaset_init(SD.concs_replica, SD.concs);
// unionized_energy_array
if( in.grid_type == UNIONIZED ){
aml_replicaset_hwloc_create(&(SD.unionized_energy_array_replica),
SD.length_unionized_energy_array * sizeof(*(SD.unionized_energy_array)),
HWLOC_OBJ_CORE,
HWLOC_DISTANCES_KIND_FROM_OS |
HWLOC_DISTANCES_KIND_MEANS_LATENCY);
nbytes += (SD.unionized_energy_array_replica)->n * (SD.unionized_energy_array_replica)->size;
aml_replicaset_init(SD.unionized_energy_array_replica, SD.unionized_energy_array);
}
// index grid
if( in.grid_type == UNIONIZED || in.grid_type == HASH ){
aml_replicaset_hwloc_create(&(SD.index_grid_replica),
SD.length_index_grid * sizeof(*(SD.index_grid)),
HWLOC_OBJ_CORE,
HWLOC_DISTANCES_KIND_FROM_OS |
HWLOC_DISTANCES_KIND_MEANS_LATENCY);
nbytes += (SD.index_grid_replica)->n * (SD.index_grid_replica)->size;
aml_replicaset_init(SD.index_grid_replica, SD.index_grid);
}
// nuclide grid
aml_replicaset_hwloc_create(&(SD.nuclide_grid_replica),
SD.length_nuclide_grid * sizeof(*(SD.nuclide_grid)),
HWLOC_OBJ_CORE,
HWLOC_DISTANCES_KIND_FROM_OS |
HWLOC_DISTANCES_KIND_MEANS_LATENCY);
nbytes += (SD.nuclide_grid_replica)->n * (SD.nuclide_grid_replica)->size;
aml_replicaset_init(SD.nuclide_grid_replica, SD.nuclide_grid);
#endif
if(mype == 0) printf("Intialization complete. Allocated %.0lf MB of data.\n", nbytes/1024.0/1024.0 );
return SD;
}

Binary file not shown.

View File

@@ -0,0 +1,123 @@
#include "XSbench_header.h"
#ifdef MPI
#include<mpi.h>
#endif
int main( int argc, char* argv[] )
{
// =====================================================================
// Initialization & Command Line Read-In
// =====================================================================
int version = 20;
int mype = 0;
double omp_start, omp_end;
int nprocs = 1;
unsigned long long verification;
#ifdef MPI
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &nprocs);
MPI_Comm_rank(MPI_COMM_WORLD, &mype);
#endif
#ifdef AML
aml_init(&argc, &argv);
#endif
// Process CLI Fields -- store in "Inputs" structure
Inputs in = read_CLI( argc, argv );
// Set number of OpenMP Threads
#ifdef OPENMP
omp_set_num_threads(in.nthreads);
#endif
// Print-out of Input Summary
if( mype == 0 )
print_inputs( in, nprocs, version );
// =====================================================================
// Prepare Nuclide Energy Grids, Unionized Energy Grid, & Material Data
// This is not reflective of a real Monte Carlo simulation workload,
// therefore, do not profile this region!
// =====================================================================
SimulationData SD;
// If read from file mode is selected, skip initialization and load
// all simulation data structures from file instead
if( in.binary_mode == READ )
SD = binary_read(in);
else
SD = grid_init_do_not_profile( in, mype );
// If writing from file mode is selected, write all simulation data
// structures to file
if( in.binary_mode == WRITE && mype == 0 )
binary_write(in, SD);
// =====================================================================
// Cross Section (XS) Parallel Lookup Simulation
// This is the section that should be profiled, as it reflects a
// realistic continuous energy Monte Carlo macroscopic cross section
// lookup kernel.
// =====================================================================
if( mype == 0 )
{
printf("\n");
border_print();
center_print("SIMULATION", 79);
border_print();
}
// Start Simulation Timer
omp_start = get_time();
// Run simulation
if( in.simulation_method == EVENT_BASED )
{
if( in.kernel_id == 0 )
verification = run_event_based_simulation(in, SD, mype);
else if( in.kernel_id == 1 )
verification = run_event_based_simulation_optimization_1(in, SD, mype);
else
{
printf("Error: No kernel ID %d found!\n", in.kernel_id);
exit(1);
}
}
else
verification = run_history_based_simulation(in, SD, mype);
if( mype == 0)
{
printf("\n" );
printf("Simulation complete.\n" );
}
// End Simulation Timer
omp_end = get_time();
// =====================================================================
// Output Results & Finalize
// =====================================================================
// Final Hash Step
verification = verification % 999983;
// Print / Save Results and Exit
int is_invalid_result = print_results( in, mype, omp_end-omp_start, nprocs, verification );
#ifdef MPI
MPI_Finalize();
#endif
#ifdef AML
aml_finalize();
#endif
return is_invalid_result;
}

Binary file not shown.

View File

@@ -0,0 +1,86 @@
#===============================================================================
# User Options
#===============================================================================
# Compiler can be set below, or via environment variable
CC = cc
OPTIMIZE = yes
OPENMP = no
DEBUG = yes
PROFILE = no
MPI = no
AML = no
#===============================================================================
# Program name & source code list
#===============================================================================
program = XSBench
source = \
Main.c \
io.c \
Simulations.c \
GridInit.c \
XSutils.c \
Materials.c
obj = $(source:.c=.o)
#===============================================================================
# Sets Flags
#===============================================================================
# Standard Flags
# Linker Flags
LDFLAGS = -lm
# LLVM Compiler
# ifneq (,$(findstring clang,$(CC)))
# CFLAGS += -flto
# ifeq ($(OPENMP),yes)
# CFLAGS += -fopenmp -DOPENMP
# endif
# endif
# # Intel Compiler
# ifneq (,$(findstring intel,$(CC)))
# CFLAGS += -ipo
# ifeq ($(OPENMP),yes)
# CFLAGS += -fopenmp -DOPENMP
# endif
# endif
# # Debug Flags
# ifeq ($(DEBUG),yes)
# CFLAGS += -g
# LDFLAGS += -g
# endif
# Profiling Flags
# Optimization Flags
# AML
CFLAGS += -g -Wall -mabi=purecap-benchmark -lpthread
#===============================================================================
# Targets to Build
#===============================================================================
$(program): $(obj) XSbench_header.h Makefile
$(CC) $(CFLAGS) $(obj) -o $@ $(LDFLAGS)
%.o: %.c XSbench_header.h Makefile
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -rf $(program) $(obj)
edit:
vim -p $(source) XSbench_header.h
run:
./$(program)

View File

@@ -0,0 +1,117 @@
// Material data is hard coded into the functions in this file.
// Note that there are 12 materials present in H-M (large or small)
#include "XSbench_header.h"
// num_nucs represents the number of nuclides that each material contains
int * load_num_nucs(long n_isotopes)
{
int * num_nucs = (int*)malloc(12*sizeof(int));
// Material 0 is a special case (fuel). The H-M small reactor uses
// 34 nuclides, while H-M larges uses 300.
if( n_isotopes == 68 )
num_nucs[0] = 34; // HM Small is 34, H-M Large is 321
else
num_nucs[0] = 321; // HM Small is 34, H-M Large is 321
num_nucs[1] = 5;
num_nucs[2] = 4;
num_nucs[3] = 4;
num_nucs[4] = 27;
num_nucs[5] = 21;
num_nucs[6] = 21;
num_nucs[7] = 21;
num_nucs[8] = 21;
num_nucs[9] = 21;
num_nucs[10] = 9;
num_nucs[11] = 9;
return num_nucs;
}
// Assigns an array of nuclide ID's to each material
int * load_mats( int * num_nucs, long n_isotopes, int * max_num_nucs )
{
*max_num_nucs = 0;
int num_mats = 12;
for( int m = 0; m < num_mats; m++ )
{
if( num_nucs[m] > *max_num_nucs )
*max_num_nucs = num_nucs[m];
}
int * mats = (int *) malloc( num_mats * (*max_num_nucs) * sizeof(int) );
// Small H-M has 34 fuel nuclides
int mats0_Sml[] = { 58, 59, 60, 61, 40, 42, 43, 44, 45, 46, 1, 2, 3, 7,
8, 9, 10, 29, 57, 47, 48, 0, 62, 15, 33, 34, 52, 53,
54, 55, 56, 18, 23, 41 }; //fuel
// Large H-M has 300 fuel nuclides
int mats0_Lrg[321] = { 58, 59, 60, 61, 40, 42, 43, 44, 45, 46, 1, 2, 3, 7,
8, 9, 10, 29, 57, 47, 48, 0, 62, 15, 33, 34, 52, 53,
54, 55, 56, 18, 23, 41 }; //fuel
for( int i = 0; i < 321-34; i++ )
mats0_Lrg[34+i] = 68 + i; // H-M large adds nuclides to fuel only
// These are the non-fuel materials
int mats1[] = { 63, 64, 65, 66, 67 }; // cladding
int mats2[] = { 24, 41, 4, 5 }; // cold borated water
int mats3[] = { 24, 41, 4, 5 }; // hot borated water
int mats4[] = { 19, 20, 21, 22, 35, 36, 37, 38, 39, 25, 27, 28, 29,
30, 31, 32, 26, 49, 50, 51, 11, 12, 13, 14, 6, 16,
17 }; // RPV
int mats5[] = { 24, 41, 4, 5, 19, 20, 21, 22, 35, 36, 37, 38, 39, 25,
49, 50, 51, 11, 12, 13, 14 }; // lower radial reflector
int mats6[] = { 24, 41, 4, 5, 19, 20, 21, 22, 35, 36, 37, 38, 39, 25,
49, 50, 51, 11, 12, 13, 14 }; // top reflector / plate
int mats7[] = { 24, 41, 4, 5, 19, 20, 21, 22, 35, 36, 37, 38, 39, 25,
49, 50, 51, 11, 12, 13, 14 }; // bottom plate
int mats8[] = { 24, 41, 4, 5, 19, 20, 21, 22, 35, 36, 37, 38, 39, 25,
49, 50, 51, 11, 12, 13, 14 }; // bottom nozzle
int mats9[] = { 24, 41, 4, 5, 19, 20, 21, 22, 35, 36, 37, 38, 39, 25,
49, 50, 51, 11, 12, 13, 14 }; // top nozzle
int mats10[] = { 24, 41, 4, 5, 63, 64, 65, 66, 67 }; // top of FA's
int mats11[] = { 24, 41, 4, 5, 63, 64, 65, 66, 67 }; // bottom FA's
// H-M large v small dependency
if( n_isotopes == 68 )
memcpy( mats, mats0_Sml, num_nucs[0] * sizeof(int) );
else
memcpy( mats, mats0_Lrg, num_nucs[0] * sizeof(int) );
// Copy other materials
memcpy( mats + *max_num_nucs * 1, mats1, num_nucs[1] * sizeof(int) );
memcpy( mats + *max_num_nucs * 2, mats2, num_nucs[2] * sizeof(int) );
memcpy( mats + *max_num_nucs * 3, mats3, num_nucs[3] * sizeof(int) );
memcpy( mats + *max_num_nucs * 4, mats4, num_nucs[4] * sizeof(int) );
memcpy( mats + *max_num_nucs * 5, mats5, num_nucs[5] * sizeof(int) );
memcpy( mats + *max_num_nucs * 6, mats6, num_nucs[6] * sizeof(int) );
memcpy( mats + *max_num_nucs * 7, mats7, num_nucs[7] * sizeof(int) );
memcpy( mats + *max_num_nucs * 8, mats8, num_nucs[8] * sizeof(int) );
memcpy( mats + *max_num_nucs * 9, mats9, num_nucs[9] * sizeof(int) );
memcpy( mats + *max_num_nucs * 10, mats10, num_nucs[10] * sizeof(int) );
memcpy( mats + *max_num_nucs * 11, mats11, num_nucs[11] * sizeof(int) );
return mats;
}
// Randomizes the concentrations of all nuclides in a variety of materials
double * load_concs( int * num_nucs, int max_num_nucs )
{
uint64_t seed = STARTING_SEED * STARTING_SEED;
double * concs = (double *) malloc( 12 * max_num_nucs * sizeof( double ) );
for( int i = 0; i < 12; i++ )
for( int j = 0; j < num_nucs[i]; j++ )
concs[i * max_num_nucs + j] = LCG_random_double(&seed);
// test
/*
for( int i = 0; i < 12; i++ )
for( int j = 0; j < num_nucs[i]; j++ )
printf("concs[%d][%d] = %lf\n", i, j, concs[i][j] );
*/
return concs;
}

Binary file not shown.

View File

@@ -0,0 +1,871 @@
#include "XSbench_header.h"
////////////////////////////////////////////////////////////////////////////////////
// BASELINE FUNCTIONS
////////////////////////////////////////////////////////////////////////////////////
// All "baseline" code is at the top of this file. The baseline code is a simple
// implementation of the algorithm, with only minor CPU optimizations in place.
// Following these functions are a number of optimized variants,
// which each deploy a different combination of optimizations strategies. By
// default, XSBench will only run the baseline implementation. Optimized variants
// must be specifically selected using the "-k <optimized variant ID>" command
// line argument.
////////////////////////////////////////////////////////////////////////////////////
unsigned long long run_event_based_simulation(Inputs in, SimulationData SD, int mype)
{
if( mype == 0)
printf("Beginning event based simulation...\n");
////////////////////////////////////////////////////////////////////////////////
// SUMMARY: Simulation Data Structure Manifest for "SD" Object
// Here we list all heap arrays (and lengths) in SD that would need to be
// offloaded manually if using an accelerator with a seperate memory space
////////////////////////////////////////////////////////////////////////////////
// int * num_nucs; // Length = length_num_nucs;
// double * concs; // Length = length_concs
// int * mats; // Length = length_mats
// double * unionized_energy_array; // Length = length_unionized_energy_array
// int * index_grid; // Length = length_index_grid
// NuclideGridPoint * nuclide_grid; // Length = length_nuclide_grid
//
// Note: "unionized_energy_array" and "index_grid" can be of zero length
// depending on lookup method.
//
// Note: "Lengths" are given as the number of objects in the array, not the
// number of bytes.
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Begin Actual Simulation Loop
////////////////////////////////////////////////////////////////////////////////
unsigned long long verification = 0;
int i = 0;
#pragma omp parallel for schedule(dynamic,100) reduction(+:verification)
for( i = 0; i < in.lookups; i++ )
{
#ifdef AML
int * num_nucs = aml_replicaset_hwloc_local_replica(SD.num_nucs_replica);
double * concs = aml_replicaset_hwloc_local_replica(SD.concs_replica);
double * unionized_energy_array = aml_replicaset_hwloc_local_replica(SD.unionized_energy_array_replica);
int * index_grid = aml_replicaset_hwloc_local_replica(SD.index_grid_replica);
NuclideGridPoint * nuclide_grid = aml_replicaset_hwloc_local_replica(SD.nuclide_grid_replica);
#else
int * num_nucs = SD.num_nucs;
double * concs = SD.concs;
double * unionized_energy_array = SD.unionized_energy_array;
int * index_grid = SD.index_grid;
NuclideGridPoint * nuclide_grid = SD.nuclide_grid;
#endif
// Set the initial seed value
uint64_t seed = STARTING_SEED;
// Forward seed to lookup index (we need 2 samples per lookup)
seed = fast_forward_LCG(seed, 2*i);
// Randomly pick an energy and material for the particle
double p_energy = LCG_random_double(&seed);
int mat = pick_mat(&seed);
double macro_xs_vector[5] = {0};
// Perform macroscopic Cross Section Lookup
calculate_macro_xs(
p_energy, // Sampled neutron energy (in lethargy)
mat, // Sampled material type index neutron is in
in.n_isotopes, // Total number of isotopes in simulation
in.n_gridpoints, // Number of gridpoints per isotope in simulation
num_nucs, // 1-D array with number of nuclides per material
concs, // Flattened 2-D array with concentration of each nuclide in each material
unionized_energy_array, // 1-D Unionized energy array
index_grid, // Flattened 2-D grid holding indices into nuclide grid for each unionized energy level
nuclide_grid, // Flattened 2-D grid holding energy levels and XS_data for all nuclides in simulation
SD.mats, // Flattened 2-D array with nuclide indices defining composition of each type of material
macro_xs_vector, // 1-D array with result of the macroscopic cross section (5 different reaction channels)
in.grid_type, // Lookup type (nuclide, hash, or unionized)
in.hash_bins, // Number of hash bins used (if using hash lookup type)
SD.max_num_nucs // Maximum number of nuclides present in any material
);
// For verification, and to prevent the compiler from optimizing
// all work out, we interrogate the returned macro_xs_vector array
// to find its maximum value index, then increment the verification
// value by that index. In this implementation, we prevent thread
// contention by using an OMP reduction on the verification value.
// For accelerators, a different approach might be required
// (e.g., atomics, reduction of thread-specific values in large
// array via CUDA thrust, etc).
double max = -1.0;
int max_idx = 0;
for(int j = 0; j < 5; j++ )
{
if( macro_xs_vector[j] > max )
{
max = macro_xs_vector[j];
max_idx = j;
}
}
verification += max_idx+1;
}
return verification;
}
unsigned long long run_history_based_simulation(Inputs in, SimulationData SD, int mype)
{
if( mype == 0)
printf("Beginning history based simulation...\n");
////////////////////////////////////////////////////////////////////////////////
// SUMMARY: Simulation Data Structure Manifest for "SD" Object
// Here we list all heap arrays (and lengths) in SD that would need to be
// offloaded manually if using an accelerator with a seperate memory space
////////////////////////////////////////////////////////////////////////////////
// int * num_nucs; // Length = length_num_nucs;
// double * concs; // Length = length_concs
// int * mats; // Length = length_mats
// double * unionized_energy_array; // Length = length_unionized_energy_array
// int * index_grid; // Length = length_index_grid
// NuclideGridPoint * nuclide_grid; // Length = length_nuclide_grid
//
// Note: "unionized_energy_array" and "index_grid" can be of zero length
// depending on lookup method.
//
// Note: "Lengths" are given as the number of objects in the array, not the
// number of bytes.
////////////////////////////////////////////////////////////////////////////////
unsigned long long verification = 0;
// Begin outer lookup loop over particles. This loop is independent.
int p = 0;
#pragma omp parallel for schedule(dynamic, 100) reduction(+:verification)
for( p = 0; p < in.particles; p++ )
{
#ifdef AML
int * num_nucs = aml_replicaset_hwloc_local_replica(SD.num_nucs_replica);
double * concs = aml_replicaset_hwloc_local_replica(SD.concs_replica);
double * unionized_energy_array = aml_replicaset_hwloc_local_replica(SD.unionized_energy_array_replica);
int * index_grid = aml_replicaset_hwloc_local_replica(SD.index_grid_replica);
NuclideGridPoint * nuclide_grid = aml_replicaset_hwloc_local_replica(SD.nuclide_grid_replica);
#else
int * num_nucs = SD.num_nucs;
double * concs = SD.concs;
double * unionized_energy_array = SD.unionized_energy_array;
int * index_grid = SD.index_grid;
NuclideGridPoint * nuclide_grid = SD.nuclide_grid;
#endif
// Set the initial seed value
uint64_t seed = STARTING_SEED;
// Forward seed to lookup index (we need 2 samples per lookup, and
// we may fast forward up to 5 times after each lookup)
seed = fast_forward_LCG(seed, p*in.lookups*2*5);
// Randomly pick an energy and material for the particle
double p_energy = LCG_random_double(&seed);
int mat = pick_mat(&seed);
// Inner XS Lookup Loop
// This loop is dependent!
// i.e., Next iteration uses data computed in previous iter.
for( int i = 0; i < in.lookups; i++ )
{
double macro_xs_vector[5] = {0};
// Perform macroscopic Cross Section Lookup
calculate_macro_xs(
p_energy, // Sampled neutron energy (in lethargy)
mat, // Sampled material type neutron is in
in.n_isotopes, // Total number of isotopes in simulation
in.n_gridpoints, // Number of gridpoints per isotope in simulation
num_nucs, // 1-D array with number of nuclides per material
concs, // Flattened 2-D array with concentration of each nuclide in each material
unionized_energy_array, // 1-D Unionized energy array
index_grid, // Flattened 2-D grid holding indices into nuclide grid for each unionized energy level
nuclide_grid, // Flattened 2-D grid holding energy levels and XS_data for all nuclides in simulation
SD.mats, // Flattened 2-D array with nuclide indices for each type of material
macro_xs_vector, // 1-D array with result of the macroscopic cross section (5 different reaction channels)
in.grid_type, // Lookup type (nuclide, hash, or unionized)
in.hash_bins, // Number of hash bins used (if using hash lookups)
SD.max_num_nucs // Maximum number of nuclides present in any material
);
// For verification, and to prevent the compiler from optimizing
// all work out, we interrogate the returned macro_xs_vector array
// to find its maximum value index, then increment the verification
// value by that index. In this implementation, we prevent thread
// contention by using an OMP reduction on it. For other accelerators,
// a different approach might be required (e.g., atomics, reduction
// of thread-specific values in large array via CUDA thrust, etc)
double max = -1.0;
int max_idx = 0;
for(int j = 0; j < 5; j++ )
{
if( macro_xs_vector[j] > max )
{
max = macro_xs_vector[j];
max_idx = j;
}
}
verification += max_idx+1;
// Randomly pick next energy and material for the particle
// Also incorporates results from macro_xs lookup to
// enforce loop dependency.
// In a real MC app, this dependency is expressed in terms
// of branching physics sampling, whereas here we are just
// artificially enforcing this dependence based on fast
// forwarding the LCG state
uint64_t n_forward = 0;
for( int j = 0; j < 5; j++ )
if( macro_xs_vector[j] > 1.0 )
n_forward++;
if( n_forward > 0 )
seed = fast_forward_LCG(seed, n_forward);
p_energy = LCG_random_double(&seed);
mat = pick_mat(&seed);
}
}
return verification;
}
// Calculates the microscopic cross section for a given nuclide & energy
void calculate_micro_xs( double p_energy, int nuc, long n_isotopes,
long n_gridpoints,
double * restrict egrid, int * restrict index_data,
NuclideGridPoint * restrict nuclide_grids,
long idx, double * restrict xs_vector, int grid_type, int hash_bins ){
// Variables
double f;
NuclideGridPoint * low, * high;
// If using only the nuclide grid, we must perform a binary search
// to find the energy location in this particular nuclide's grid.
if( grid_type == NUCLIDE )
{
// Perform binary search on the Nuclide Grid to find the index
idx = grid_search_nuclide( n_gridpoints, p_energy, &nuclide_grids[nuc*n_gridpoints], 0, n_gridpoints-1);
// pull ptr from nuclide grid and check to ensure that
// we're not reading off the end of the nuclide's grid
if( idx == n_gridpoints - 1 )
low = &nuclide_grids[nuc*n_gridpoints + idx - 1];
else
low = &nuclide_grids[nuc*n_gridpoints + idx];
}
else if( grid_type == UNIONIZED) // Unionized Energy Grid - we already know the index, no binary search needed.
{
// pull ptr from energy grid and check to ensure that
// we're not reading off the end of the nuclide's grid
if( index_data[idx * n_isotopes + nuc] == n_gridpoints - 1 )
low = &nuclide_grids[nuc*n_gridpoints + index_data[idx * n_isotopes + nuc] - 1];
else
low = &nuclide_grids[nuc*n_gridpoints + index_data[idx * n_isotopes + nuc]];
}
else // Hash grid
{
// load lower bounding index
int u_low = index_data[idx * n_isotopes + nuc];
// Determine higher bounding index
int u_high;
if( idx == hash_bins - 1 )
u_high = n_gridpoints - 1;
else
u_high = index_data[(idx+1)*n_isotopes + nuc] + 1;
// Check edge cases to make sure energy is actually between these
// Then, if things look good, search for gridpoint in the nuclide grid
// within the lower and higher limits we've calculated.
double e_low = nuclide_grids[nuc*n_gridpoints + u_low].energy;
double e_high = nuclide_grids[nuc*n_gridpoints + u_high].energy;
int lower;
if( p_energy <= e_low )
lower = 0;
else if( p_energy >= e_high )
lower = n_gridpoints - 1;
else
lower = grid_search_nuclide( n_gridpoints, p_energy, &nuclide_grids[nuc*n_gridpoints], u_low, u_high);
if( lower == n_gridpoints - 1 )
low = &nuclide_grids[nuc*n_gridpoints + lower - 1];
else
low = &nuclide_grids[nuc*n_gridpoints + lower];
}
high = low + 1;
// calculate the re-useable interpolation factor
f = (high->energy - p_energy) / (high->energy - low->energy);
// Total XS
xs_vector[0] = high->total_xs - f * (high->total_xs - low->total_xs);
// Elastic XS
xs_vector[1] = high->elastic_xs - f * (high->elastic_xs - low->elastic_xs);
// Absorbtion XS
xs_vector[2] = high->absorbtion_xs - f * (high->absorbtion_xs - low->absorbtion_xs);
// Fission XS
xs_vector[3] = high->fission_xs - f * (high->fission_xs - low->fission_xs);
// Nu Fission XS
xs_vector[4] = high->nu_fission_xs - f * (high->nu_fission_xs - low->nu_fission_xs);
}
// Calculates macroscopic cross section based on a given material & energy
void calculate_macro_xs( double p_energy, int mat, long n_isotopes,
long n_gridpoints, int * restrict num_nucs,
double * restrict concs,
double * restrict egrid, int * restrict index_data,
NuclideGridPoint * restrict nuclide_grids,
int * restrict mats,
double * restrict macro_xs_vector, int grid_type, int hash_bins, int max_num_nucs ){
int p_nuc; // the nuclide we are looking up
long idx = -1;
double conc; // the concentration of the nuclide in the material
// cleans out macro_xs_vector
for( int k = 0; k < 5; k++ )
macro_xs_vector[k] = 0;
// If we are using the unionized energy grid (UEG), we only
// need to perform 1 binary search per macroscopic lookup.
// If we are using the nuclide grid search, it will have to be
// done inside of the "calculate_micro_xs" function for each different
// nuclide in the material.
if( grid_type == UNIONIZED )
idx = grid_search( n_isotopes * n_gridpoints, p_energy, egrid);
else if( grid_type == HASH )
{
double du = 1.0 / hash_bins;
idx = p_energy / du;
}
// Once we find the pointer array on the UEG, we can pull the data
// from the respective nuclide grids, as well as the nuclide
// concentration data for the material
// Each nuclide from the material needs to have its micro-XS array
// looked up & interpolatied (via calculate_micro_xs). Then, the
// micro XS is multiplied by the concentration of that nuclide
// in the material, and added to the total macro XS array.
// (Independent -- though if parallelizing, must use atomic operations
// or otherwise control access to the xs_vector and macro_xs_vector to
// avoid simulataneous writing to the same data structure)
for( int j = 0; j < num_nucs[mat]; j++ )
{
double xs_vector[5];
p_nuc = mats[mat*max_num_nucs + j];
conc = concs[mat*max_num_nucs + j];
calculate_micro_xs( p_energy, p_nuc, n_isotopes,
n_gridpoints, egrid, index_data,
nuclide_grids, idx, xs_vector, grid_type, hash_bins );
for( int k = 0; k < 5; k++ )
macro_xs_vector[k] += xs_vector[k] * conc;
}
}
// binary search for energy on unionized energy grid
// returns lower index
long grid_search( long n, double quarry, double * restrict A)
{
long lowerLimit = 0;
long upperLimit = n-1;
long examinationPoint;
long length = upperLimit - lowerLimit;
while( length > 1 )
{
examinationPoint = lowerLimit + ( length / 2 );
if( A[examinationPoint] > quarry )
upperLimit = examinationPoint;
else
lowerLimit = examinationPoint;
length = upperLimit - lowerLimit;
}
return lowerLimit;
}
// binary search for energy on nuclide energy grid
long grid_search_nuclide( long n, double quarry, NuclideGridPoint * A, long low, long high)
{
long lowerLimit = low;
long upperLimit = high;
long examinationPoint;
long length = upperLimit - lowerLimit;
while( length > 1 )
{
examinationPoint = lowerLimit + ( length / 2 );
if( A[examinationPoint].energy > quarry )
upperLimit = examinationPoint;
else
lowerLimit = examinationPoint;
length = upperLimit - lowerLimit;
}
return lowerLimit;
}
// picks a material based on a probabilistic distribution
int pick_mat( uint64_t * seed )
{
// I have a nice spreadsheet supporting these numbers. They are
// the fractions (by volume) of material in the core. Not a
// *perfect* approximation of where XS lookups are going to occur,
// but this will do a good job of biasing the system nonetheless.
double dist[12];
dist[0] = 0.140; // fuel
dist[1] = 0.052; // cladding
dist[2] = 0.275; // cold, borated water
dist[3] = 0.134; // hot, borated water
dist[4] = 0.154; // RPV
dist[5] = 0.064; // Lower, radial reflector
dist[6] = 0.066; // Upper reflector / top plate
dist[7] = 0.055; // bottom plate
dist[8] = 0.008; // bottom nozzle
dist[9] = 0.015; // top nozzle
dist[10] = 0.025; // top of fuel assemblies
dist[11] = 0.013; // bottom of fuel assemblies
double roll = LCG_random_double(seed);
// makes a pick based on the distro
for( int i = 0; i < 12; i++ )
{
double running = 0;
for( int j = i; j > 0; j-- )
running += dist[j];
if( roll < running )
return i;
}
return 0;
}
double LCG_random_double(uint64_t * seed)
{
// LCG parameters
const uint64_t m = 9223372036854775808ULL; // 2^63
const uint64_t a = 2806196910506780709ULL;
const uint64_t c = 1ULL;
*seed = (a * (*seed) + c) % m;
return (double) (*seed) / (double) m;
}
uint64_t fast_forward_LCG(uint64_t seed, uint64_t n)
{
// LCG parameters
const uint64_t m = 9223372036854775808ULL; // 2^63
uint64_t a = 2806196910506780709ULL;
uint64_t c = 1ULL;
n = n % m;
uint64_t a_new = 1;
uint64_t c_new = 0;
while(n > 0)
{
if(n & 1)
{
a_new *= a;
c_new = c_new * a + c;
}
c *= (a + 1);
a *= a;
n >>= 1;
}
return (a_new * seed + c_new) % m;
}
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
// OPTIMIZED VARIANT FUNCTIONS
////////////////////////////////////////////////////////////////////////////////////
// This section contains a number of optimized variants of some of the above
// functions, which each deploy a different combination of optimizations strategies.
// By default, XSBench will not run any of these variants. They
// must be specifically selected using the "-k <optimized variant ID>" command
// line argument.
//
// As fast parallel sorting will be required for these optimizations, we will
// first define a set of key-value parallel quicksort routines.
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
// Parallel Quicksort Key-Value Sorting Algorithms
////////////////////////////////////////////////////////////////////////////////////
//
// These algorithms are based on the parallel quicksort implementation by
// Eduard Lopez published at https://github.com/eduardlopez/quicksort-parallel
//
// Eduard's original version was for an integer type quicksort, but I have modified
// it to form two different versions that can sort key-value pairs together without
// having to bundle them into a separate object. Additionally, I have modified the
// optimal chunk sizes and restricted the number of threads for the array sizing
// that XSBench will be using by default.
//
// Eduard's original implementation carries the following license, which applies to
// the following functions only:
//
// void quickSort_parallel_internal_i_d(int* key,double * value, int left, int right, int cutoff)
// void quickSort_parallel_i_d(int* key,double * value, int lenArray, int numThreads)
// void quickSort_parallel_internal_d_i(double* key,int * value, int left, int right, int cutoff)
// void quickSort_parallel_d_i(double* key,int * value, int lenArray, int numThreads)
//
// The MIT License (MIT)
//
// Copyright (c) 2016 Eduard López
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
////////////////////////////////////////////////////////////////////////////////////
void quickSort_parallel_internal_i_d(int* key,double * value, int left, int right, int cutoff)
{
int i = left, j = right;
int tmp;
int pivot = key[(left + right) / 2];
{
while (i <= j) {
while (key[i] < pivot)
i++;
while (key[j] > pivot)
j--;
if (i <= j) {
tmp = key[i];
key[i] = key[j];
key[j] = tmp;
double tmp_v = value[i];
value[i] = value[j];
value[j] = tmp_v;
i++;
j--;
}
}
}
if ( ((right-left)<cutoff) ){
if (left < j){ quickSort_parallel_internal_i_d(key, value, left, j, cutoff); }
if (i < right){ quickSort_parallel_internal_i_d(key, value, i, right, cutoff); }
}else{
#pragma omp task
{ quickSort_parallel_internal_i_d(key, value, left, j, cutoff); }
#pragma omp task
{ quickSort_parallel_internal_i_d(key, value, i, right, cutoff); }
}
}
void quickSort_parallel_i_d(int* key,double * value, int lenArray, int numThreads){
// Set minumum problem size to still spawn threads for
int cutoff = 10000;
// For this problem size, more than 16 threads on CPU is not helpful
if( numThreads > 16 )
numThreads = 16;
#pragma omp parallel num_threads(numThreads)
{
#pragma omp single nowait
{
quickSort_parallel_internal_i_d(key,value, 0, lenArray-1, cutoff);
}
}
}
void quickSort_parallel_internal_d_i(double* key,int * value, int left, int right, int cutoff)
{
int i = left, j = right;
double tmp;
double pivot = key[(left + right) / 2];
{
while (i <= j) {
while (key[i] < pivot)
i++;
while (key[j] > pivot)
j--;
if (i <= j) {
tmp = key[i];
key[i] = key[j];
key[j] = tmp;
int tmp_v = value[i];
value[i] = value[j];
value[j] = tmp_v;
i++;
j--;
}
}
}
if ( ((right-left)<cutoff) ){
if (left < j){ quickSort_parallel_internal_d_i(key, value, left, j, cutoff); }
if (i < right){ quickSort_parallel_internal_d_i(key, value, i, right, cutoff); }
}else{
#pragma omp task
{ quickSort_parallel_internal_d_i(key, value, left, j, cutoff); }
#pragma omp task
{ quickSort_parallel_internal_d_i(key, value, i, right, cutoff); }
}
}
void quickSort_parallel_d_i(double* key,int * value, int lenArray, int numThreads){
// Set minumum problem size to still spawn threads for
int cutoff = 10000;
// For this problem size, more than 16 threads on CPU is not helpful
if( numThreads > 16 )
numThreads = 16;
#pragma omp parallel num_threads(numThreads)
{
#pragma omp single nowait
{
quickSort_parallel_internal_d_i(key,value, 0, lenArray-1, cutoff);
}
}
}
////////////////////////////////////////////////////////////////////////////////////
// Optimization 1 -- Event-based Sample/XS Lookup kernel splitting + Sorting
// lookups by material and energy
////////////////////////////////////////////////////////////////////////////////////
// This kernel separates out the sampling and lookup regions of the event-based
// model, and then sorts the lookups by material type and energy. The goal of this
// optimization is to allow for greatly improved cache locality, and XS indices
// loaded from memory may be re-used for multiple lookups.
//
// As efficienct sorting is key for performance, we also must implement an
// efficient key-value parallel sorting algorithm. We also experimented with using
// the C++ version of thrust for these purposes, but found that our own implemtation
// was slightly faster than the thrust library version, so for speed and
// simplicity we will do not add the thrust dependency.
////////////////////////////////////////////////////////////////////////////////////
unsigned long long run_event_based_simulation_optimization_1(Inputs in, SimulationData SD, int mype)
{
char * optimization_name = "Optimization 1 - Kernel splitting + full material & energy sort";
if( mype == 0) printf("Simulation Kernel:\"%s\"\n", optimization_name);
////////////////////////////////////////////////////////////////////////////////
// Allocate Additional Data Structures Needed by Optimized Kernel
////////////////////////////////////////////////////////////////////////////////
if( mype == 0) printf("Allocating additional data required by optimized kernel...\n");
size_t sz;
size_t total_sz = 0;
double start, stop;
// loop variables
int i = 0;
int m = 0;
sz = in.lookups * sizeof(double);
SD.p_energy_samples = (double *) malloc(sz);
total_sz += sz;
SD.length_p_energy_samples = in.lookups;
sz = in.lookups * sizeof(int);
SD.mat_samples = (int *) malloc(sz);
total_sz += sz;
SD.length_mat_samples = in.lookups;
if( mype == 0) printf("Allocated an additional %.0lf MB of data on GPU.\n", total_sz/1024.0/1024.0);
////////////////////////////////////////////////////////////////////////////////
// Begin Actual Simulation
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Sample Materials and Energies
////////////////////////////////////////////////////////////////////////////////
#pragma omp parallel for schedule(dynamic, 100)
for( i = 0; i < in.lookups; i++ )
{
// Set the initial seed value
uint64_t seed = STARTING_SEED;
// Forward seed to lookup index (we need 2 samples per lookup)
seed = fast_forward_LCG(seed, 2*i);
// Randomly pick an energy and material for the particle
double p_energy = LCG_random_double(&seed);
int mat = pick_mat(&seed);
SD.p_energy_samples[i] = p_energy;
SD.mat_samples[i] = mat;
}
if(mype == 0) printf("finished sampling...\n");
////////////////////////////////////////////////////////////////////////////////
// Sort by Material
////////////////////////////////////////////////////////////////////////////////
start = get_time();
quickSort_parallel_i_d(SD.mat_samples, SD.p_energy_samples, in.lookups, in.nthreads);
stop = get_time();
if(mype == 0) printf("Material sort took %.3lf seconds\n", stop-start);
////////////////////////////////////////////////////////////////////////////////
// Sort by Energy
////////////////////////////////////////////////////////////////////////////////
start = get_time();
// Count up number of each type of sample.
int num_samples_per_mat[12] = {0};
for( int l = 0; l < in.lookups; l++ )
num_samples_per_mat[ SD.mat_samples[l] ]++;
// Determine offsets
int offsets[12] = {0};
for( int m = 1; m < 12; m++ )
offsets[m] = offsets[m-1] + num_samples_per_mat[m-1];
stop = get_time();
if(mype == 0) printf("Counting samples and offsets took %.3lf seconds\n", stop-start);
start = stop;
// Sort each material type by energy level
int offset = 0;
for( int m = 0; m < 12; m++ )
quickSort_parallel_d_i(SD.p_energy_samples + offsets[m],SD.mat_samples + offsets[m], num_samples_per_mat[m], in.nthreads);
stop = get_time();
if(mype == 0) printf("Energy Sorts took %.3lf seconds\n", stop-start);
////////////////////////////////////////////////////////////////////////////////
// Perform lookups for each material separately
////////////////////////////////////////////////////////////////////////////////
start = get_time();
unsigned long long verification = 0;
// Individual Materials
offset = 0;
for( m = 0; m < 12; m++ )
{
#pragma omp parallel for schedule(dynamic,100) reduction(+:verification)
for( i = offset; i < offset + num_samples_per_mat[m]; i++)
{
#ifdef AML
int * num_nucs = aml_replicaset_hwloc_local_replica(SD.num_nucs_replica);
double * concs = aml_replicaset_hwloc_local_replica(SD.concs_replica);
double * unionized_energy_array = aml_replicaset_hwloc_local_replica(SD.unionized_energy_array_replica);
int * index_grid = aml_replicaset_hwloc_local_replica(SD.index_grid_replica);
NuclideGridPoint * nuclide_grid = aml_replicaset_hwloc_local_replica(SD.nuclide_grid_replica);
#else
int * num_nucs = SD.num_nucs;
double * concs = SD.concs;
double * unionized_energy_array = SD.unionized_energy_array;
int * index_grid = SD.index_grid;
NuclideGridPoint * nuclide_grid = SD.nuclide_grid;
#endif
// load pre-sampled energy and material for the particle
double p_energy = SD.p_energy_samples[i];
int mat = SD.mat_samples[i];
double macro_xs_vector[5] = {0};
// Perform macroscopic Cross Section Lookup
calculate_macro_xs(
p_energy, // Sampled neutron energy (in lethargy)
mat, // Sampled material type index neutron is in
in.n_isotopes, // Total number of isotopes in simulation
in.n_gridpoints, // Number of gridpoints per isotope in simulation
num_nucs, // 1-D array with number of nuclides per material
concs, // Flattened 2-D array with concentration of each nuclide in each material
unionized_energy_array, // 1-D Unionized energy array
index_grid, // Flattened 2-D grid holding indices into nuclide grid for each unionized energy level
nuclide_grid, // Flattened 2-D grid holding energy levels and XS_data for all nuclides in simulation
SD.mats, // Flattened 2-D array with nuclide indices defining composition of each type of material
macro_xs_vector, // 1-D array with result of the macroscopic cross section (5 different reaction channels)
in.grid_type, // Lookup type (nuclide, hash, or unionized)
in.hash_bins, // Number of hash bins used (if using hash lookup type)
SD.max_num_nucs // Maximum number of nuclides present in any material
);
// For verification, and to prevent the compiler from optimizing
// all work out, we interrogate the returned macro_xs_vector array
// to find its maximum value index, then increment the verification
// value by that index. In this implementation, we prevent thread
// contention by using an OMP reduction on the verification value.
// For accelerators, a different approach might be required
// (e.g., atomics, reduction of thread-specific values in large
// array via CUDA thrust, etc).
double max = -1.0;
int max_idx = 0;
for(int j = 0; j < 5; j++ )
{
if( macro_xs_vector[j] > max )
{
max = macro_xs_vector[j];
max_idx = j;
}
}
verification += max_idx+1;
}
offset += num_samples_per_mat[m];
}
stop = get_time();
if(mype == 0) printf("XS Lookups took %.3lf seconds\n", stop-start);
return verification;
}

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,151 @@
#ifndef __XSBENCH_HEADER_H__
#define __XSBENCH_HEADER_H__
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<string.h>
#include<math.h>
#include<assert.h>
#include<stdint.h>
#ifdef _MSC_VER
#define strncasecmp _strnicmp
#define strcasecmp _stricmp
#else
#include<unistd.h>
#include<sys/time.h>
#endif
#ifdef OPENMP
#include<omp.h>
#endif
// Papi Header
#ifdef PAPI
#include "papi.h"
#endif
//AML header
#ifdef AML
#include<aml.h>
#include<aml/higher/replicaset.h>
#include<aml/higher/replicaset/hwloc.h>
#endif
// Grid types
#define UNIONIZED 0
#define NUCLIDE 1
#define HASH 2
// Simulation types
#define HISTORY_BASED 1
#define EVENT_BASED 2
// Binary Mode Type
#define NONE 0
#define READ 1
#define WRITE 2
// Starting Seed
#define STARTING_SEED 1070
// Structures
typedef struct{
double energy;
double total_xs;
double elastic_xs;
double absorbtion_xs;
double fission_xs;
double nu_fission_xs;
} NuclideGridPoint;
typedef struct{
int nthreads;
long n_isotopes;
long n_gridpoints;
int lookups;
char * HM;
int grid_type; // 0: Unionized Grid (default) 1: Nuclide Grid
int hash_bins;
int particles;
int simulation_method;
int binary_mode;
int kernel_id;
} Inputs;
typedef struct{
int * num_nucs; // Length = length_num_nucs;
double * concs; // Length = length_concs
int * mats; // Length = length_mats
double * unionized_energy_array; // Length = length_unionized_energy_array
int * index_grid; // Length = length_index_grid
NuclideGridPoint * nuclide_grid; // Length = length_nuclide_grid
#ifdef AML
struct aml_replicaset * num_nucs_replica;
struct aml_replicaset * concs_replica;
struct aml_replicaset * unionized_energy_array_replica;
struct aml_replicaset * index_grid_replica;
struct aml_replicaset * nuclide_grid_replica;
#endif
int length_num_nucs;
int length_concs;
int length_mats;
int length_unionized_energy_array;
long length_index_grid;
int length_nuclide_grid;
int max_num_nucs;
double * p_energy_samples;
int length_p_energy_samples;
int * mat_samples;
int length_mat_samples;
} SimulationData;
// io.c
void logo(int version);
void center_print(const char *s, int width);
void border_print(void);
void fancy_int(long a);
Inputs read_CLI( int argc, char * argv[] );
void print_CLI_error(void);
void print_inputs(Inputs in, int nprocs, int version);
int print_results( Inputs in, int mype, double runtime, int nprocs, unsigned long long vhash );
void binary_write( Inputs in, SimulationData SD );
SimulationData binary_read( Inputs in );
// Simulation.c
unsigned long long run_event_based_simulation(Inputs in, SimulationData SD, int mype);
unsigned long long run_history_based_simulation(Inputs in, SimulationData SD, int mype);
void calculate_micro_xs( double p_energy, int nuc, long n_isotopes,
long n_gridpoints,
double * restrict egrid, int * restrict index_data,
NuclideGridPoint * restrict nuclide_grids,
long idx, double * restrict xs_vector, int grid_type, int hash_bins );
void calculate_macro_xs( double p_energy, int mat, long n_isotopes,
long n_gridpoints, int * restrict num_nucs,
double * restrict concs,
double * restrict egrid, int * restrict index_data,
NuclideGridPoint * restrict nuclide_grids,
int * restrict mats,
double * restrict macro_xs_vector, int grid_type, int hash_bins, int max_num_nucs );
long grid_search( long n, double quarry, double * restrict A);
long grid_search_nuclide( long n, double quarry, NuclideGridPoint * A, long low, long high);
int pick_mat( uint64_t * seed );
double LCG_random_double(uint64_t * seed);
uint64_t fast_forward_LCG(uint64_t seed, uint64_t n);
unsigned long long run_event_based_simulation_optimization_1(Inputs in, SimulationData SD, int mype);
// GridInit.c
SimulationData grid_init_do_not_profile( Inputs in, int mype );
// XSutils.c
int NGP_compare( const void * a, const void * b );
int double_compare(const void * a, const void * b);
size_t estimate_mem_usage( Inputs in );
double get_time(void);
// Materials.c
int * load_num_nucs(long n_isotopes);
int * load_mats( int * num_nucs, long n_isotopes, int * max_num_nucs );
double * load_concs( int * num_nucs, int max_num_nucs );
#endif

View File

@@ -0,0 +1,63 @@
#include "XSbench_header.h"
int double_compare(const void * a, const void * b)
{
double A = *((double *) a);
double B = *((double *) b);
if( A > B )
return 1;
else if( A < B )
return -1;
else
return 0;
}
int NGP_compare(const void * a, const void * b)
{
NuclideGridPoint A = *((NuclideGridPoint *) a);
NuclideGridPoint B = *((NuclideGridPoint *) b);
if( A.energy > B.energy )
return 1;
else if( A.energy < B.energy )
return -1;
else
return 0;
}
size_t estimate_mem_usage( Inputs in )
{
size_t single_nuclide_grid = in.n_gridpoints * sizeof( NuclideGridPoint );
size_t all_nuclide_grids = in.n_isotopes * single_nuclide_grid;
size_t size_UEG = in.n_isotopes*in.n_gridpoints*sizeof(double) + in.n_isotopes*in.n_gridpoints*in.n_isotopes*sizeof(int);
size_t size_hash_grid = in.hash_bins * in.n_isotopes * sizeof(int);
size_t memtotal;
if( in.grid_type == UNIONIZED )
memtotal = all_nuclide_grids + size_UEG;
else if( in.grid_type == NUCLIDE )
memtotal = all_nuclide_grids;
else
memtotal = all_nuclide_grids + size_hash_grid;
memtotal = ceil(memtotal / (1024.0*1024.0));
return memtotal;
}
double get_time(void)
{
#ifdef OPENMP
return omp_get_wtime();
#endif
struct timeval timecheck;
gettimeofday(&timecheck, NULL);
long ms = (long)timecheck.tv_sec * 1000 + (long)timecheck.tv_usec / 1000;
double time = (double) ms / 1000.0;
return time;
}

Binary file not shown.

View File

@@ -0,0 +1,508 @@
#include "XSbench_header.h"
#ifdef MPI
#include<mpi.h>
#endif
// Prints program logo
void logo(int version)
{
border_print();
printf(
" __ __ ___________ _ \n"
" \\ \\ / // ___| ___ \\ | | \n"
" \\ V / \\ `--.| |_/ / ___ _ __ ___| |__ \n"
" / \\ `--. \\ ___ \\/ _ \\ '_ \\ / __| '_ \\ \n"
" / /^\\ \\/\\__/ / |_/ / __/ | | | (__| | | | \n"
" \\/ \\/\\____/\\____/ \\___|_| |_|\\___|_| |_| \n\n"
);
border_print();
center_print("Developed at Argonne National Laboratory", 79);
char v[100];
sprintf(v, "Version: %d", version);
center_print(v, 79);
border_print();
}
// Prints Section titles in center of 80 char terminal
void center_print(const char *s, int width)
{
int length = strlen(s);
int i;
for (i=0; i<=(width-length)/2; i++) {
fputs(" ", stdout);
}
fputs(s, stdout);
fputs("\n", stdout);
}
int print_results( Inputs in, int mype, double runtime, int nprocs,
unsigned long long vhash )
{
// Calculate Lookups per sec
int lookups = 0;
if( in.simulation_method == HISTORY_BASED )
lookups = in.lookups * in.particles;
else if( in.simulation_method == EVENT_BASED )
lookups = in.lookups;
int lookups_per_sec = (int) ((double) lookups / runtime);
// If running in MPI, reduce timing statistics and calculate average
#ifdef MPI
int total_lookups = 0;
MPI_Barrier(MPI_COMM_WORLD);
MPI_Reduce(&lookups_per_sec, &total_lookups, 1, MPI_INT,
MPI_SUM, 0, MPI_COMM_WORLD);
#endif
int is_invalid_result = 1;
// Print output
if( mype == 0 )
{
border_print();
center_print("RESULTS", 79);
border_print();
// Print the results
printf("Threads: %d\n", in.nthreads);
#ifdef MPI
printf("MPI ranks: %d\n", nprocs);
#endif
#ifdef MPI
printf("Total Lookups/s: ");
fancy_int(total_lookups);
printf("Avg Lookups/s per MPI rank: ");
fancy_int(total_lookups / nprocs);
#else
printf("Runtime: %.3lf seconds\n", runtime);
printf("Lookups: "); fancy_int(lookups);
printf("Lookups/s: ");
fancy_int(lookups_per_sec);
#endif
}
unsigned long long large = 0;
unsigned long long small = 0;
if( in.simulation_method == EVENT_BASED )
{
small = 945990;
large = 952131;
}
else if( in.simulation_method == HISTORY_BASED )
{
small = 941535;
large = 954318;
}
if( strcmp(in.HM, "large") == 0 )
{
if( vhash == large )
is_invalid_result = 0;
}
else if( strcmp(in.HM, "small") == 0 )
{
if( vhash == small )
is_invalid_result = 0;
}
if(mype == 0 )
{
if( is_invalid_result )
printf("Verification checksum: %llu (WARNING - INVALID CHECKSUM!)\n", vhash);
else
printf("Verification checksum: %llu (Valid)\n", vhash);
border_print();
}
return is_invalid_result;
}
void print_inputs(Inputs in, int nprocs, int version )
{
// Calculate Estimate of Memory Usage
int mem_tot = estimate_mem_usage( in );
logo(version);
center_print("INPUT SUMMARY", 79);
border_print();
if( in.simulation_method == EVENT_BASED )
printf("Simulation Method: Event Based\n");
else
printf("Simulation Method: History Based\n");
if( in.grid_type == NUCLIDE )
printf("Grid Type: Nuclide Grid\n");
else if( in.grid_type == UNIONIZED )
printf("Grid Type: Unionized Grid\n");
else
printf("Grid Type: Hash\n");
printf("Materials: %d\n", 12);
printf("H-M Benchmark Size: %s\n", in.HM);
printf("Total Nuclides: %ld\n", in.n_isotopes);
printf("Gridpoints (per Nuclide): ");
fancy_int(in.n_gridpoints);
if( in.grid_type == HASH )
{
printf("Hash Bins: ");
fancy_int(in.hash_bins);
}
if( in.grid_type == UNIONIZED )
{
printf("Unionized Energy Gridpoints: ");
fancy_int(in.n_isotopes*in.n_gridpoints);
}
if( in.simulation_method == HISTORY_BASED )
{
printf("Particle Histories: "); fancy_int(in.particles);
printf("XS Lookups per Particle: "); fancy_int(in.lookups);
}
printf("Total XS Lookups: "); fancy_int(in.lookups);
#ifdef MPI
printf("MPI Ranks: %d\n", nprocs);
printf("OMP Threads per MPI Rank: %d\n", in.nthreads);
printf("Mem Usage per MPI Rank (MB): "); fancy_int(mem_tot);
#else
printf("Threads: %d\n", in.nthreads);
printf("Est. Memory Usage (MB): "); fancy_int(mem_tot);
#endif
printf("Binary File Mode: ");
if( in.binary_mode == NONE )
printf("Off\n");
else if( in.binary_mode == READ)
printf("Read\n");
else
printf("Write\n");
border_print();
center_print("INITIALIZATION - DO NOT PROFILE", 79);
border_print();
}
void border_print(void)
{
printf(
"==================================================================="
"=============\n");
}
// Prints comma separated integers - for ease of reading
void fancy_int( long a )
{
if( a < 1000 )
printf("%ld\n",a);
else if( a >= 1000 && a < 1000000 )
printf("%ld,%03ld\n", a / 1000, a % 1000);
else if( a >= 1000000 && a < 1000000000 )
printf("%ld,%03ld,%03ld\n",a / 1000000,(a % 1000000) / 1000,a % 1000 );
else if( a >= 1000000000 )
printf("%ld,%03ld,%03ld,%03ld\n",
a / 1000000000,
(a % 1000000000) / 1000000,
(a % 1000000) / 1000,
a % 1000 );
else
printf("%ld\n",a);
}
void print_CLI_error(void)
{
printf("Usage: ./XSBench <options>\n");
printf("Options include:\n");
printf(" -m <simulation method> Simulation method (history, event)\n");
printf(" -t <threads> Number of OpenMP threads to run\n");
printf(" -s <size> Size of H-M Benchmark to run (small, large, XL, XXL)\n");
printf(" -g <gridpoints> Number of gridpoints per nuclide (overrides -s defaults)\n");
printf(" -G <grid type> Grid search type (unionized, nuclide, hash). Defaults to unionized.\n");
printf(" -p <particles> Number of particle histories\n");
printf(" -l <lookups> History Based: Number of Cross-section (XS) lookups per particle. Event Based: Total number of XS lookups.\n");
printf(" -h <hash bins> Number of hash bins (only relevant when used with \"-G hash\")\n");
printf(" -b <binary mode> Read or write all data structures to file. If reading, this will skip initialization phase. (read, write)\n");
printf(" -k <kernel ID> Specifies which kernel to run. 0 is baseline, 1, 2, etc are optimized variants. (0 is default.)\n");
printf("Default is equivalent to: -m history -s large -l 34 -p 500000 -G unionized\n");
printf("See readme for full description of default run values\n");
exit(4);
}
Inputs read_CLI( int argc, char * argv[] )
{
Inputs input;
// defaults to the history based simulation method
input.simulation_method = HISTORY_BASED;
// defaults to max threads on the system
#ifdef OPENMP
input.nthreads = omp_get_num_procs();
#else
input.nthreads = 1;
#endif
// defaults to 355 (corresponding to H-M Large benchmark)
input.n_isotopes = 355;
// defaults to 11303 (corresponding to H-M Large benchmark)
input.n_gridpoints = 11303;
// defaults to 500,000
input.particles = 500000;
// defaults to 34
input.lookups = 34;
// default to unionized grid
input.grid_type = UNIONIZED;
// default to unionized grid
input.hash_bins = 10000;
// default to no binary read/write
input.binary_mode = NONE;
// defaults to baseline kernel
input.kernel_id = 0;
// defaults to H-M Large benchmark
input.HM = (char *) malloc( 6 * sizeof(char) );
input.HM[0] = 'l' ;
input.HM[1] = 'a' ;
input.HM[2] = 'r' ;
input.HM[3] = 'g' ;
input.HM[4] = 'e' ;
input.HM[5] = '\0';
// Check if user sets these
int user_g = 0;
int default_lookups = 1;
int default_particles = 1;
// Collect Raw Input
for( int i = 1; i < argc; i++ )
{
char * arg = argv[i];
// nthreads (-t)
if( strcmp(arg, "-t") == 0 )
{
if( ++i < argc )
input.nthreads = atoi(argv[i]);
else
print_CLI_error();
}
// n_gridpoints (-g)
else if( strcmp(arg, "-g") == 0 )
{
if( ++i < argc )
{
user_g = 1;
input.n_gridpoints = atol(argv[i]);
}
else
print_CLI_error();
}
// Simulation Method (-m)
else if( strcmp(arg, "-m") == 0 )
{
char * sim_type;
if( ++i < argc )
sim_type = argv[i];
else
print_CLI_error();
if( strcmp(sim_type, "history") == 0 )
input.simulation_method = HISTORY_BASED;
else if( strcmp(sim_type, "event") == 0 )
{
input.simulation_method = EVENT_BASED;
// Also resets default # of lookups
if( default_lookups && default_particles )
{
input.lookups = input.lookups * input.particles;
input.particles = 0;
}
}
else
print_CLI_error();
}
// lookups (-l)
else if( strcmp(arg, "-l") == 0 )
{
if( ++i < argc )
{
input.lookups = atoi(argv[i]);
default_lookups = 0;
}
else
print_CLI_error();
}
// hash bins (-h)
else if( strcmp(arg, "-h") == 0 )
{
if( ++i < argc )
input.hash_bins = atoi(argv[i]);
else
print_CLI_error();
}
// particles (-p)
else if( strcmp(arg, "-p") == 0 )
{
if( ++i < argc )
{
input.particles = atoi(argv[i]);
default_particles = 0;
}
else
print_CLI_error();
}
// HM (-s)
else if( strcmp(arg, "-s") == 0 )
{
if( ++i < argc )
input.HM = argv[i];
else
print_CLI_error();
}
// grid type (-G)
else if( strcmp(arg, "-G") == 0 )
{
char * grid_type;
if( ++i < argc )
grid_type = argv[i];
else
print_CLI_error();
if( strcmp(grid_type, "unionized") == 0 )
input.grid_type = UNIONIZED;
else if( strcmp(grid_type, "nuclide") == 0 )
input.grid_type = NUCLIDE;
else if( strcmp(grid_type, "hash") == 0 )
input.grid_type = HASH;
else
print_CLI_error();
}
// binary mode (-b)
else if( strcmp(arg, "-b") == 0 )
{
char * binary_mode;
if( ++i < argc )
binary_mode = argv[i];
else
print_CLI_error();
if( strcmp(binary_mode, "read") == 0 )
input.binary_mode = READ;
else if( strcmp(binary_mode, "write") == 0 )
input.binary_mode = WRITE;
else
print_CLI_error();
}
// kernel optimization selection (-k)
else if( strcmp(arg, "-k") == 0 )
{
if( ++i < argc )
{
input.kernel_id = atoi(argv[i]);
}
else
print_CLI_error();
}
else
print_CLI_error();
}
// Validate Input
// Validate nthreads
if( input.nthreads < 1 )
print_CLI_error();
// Validate n_isotopes
if( input.n_isotopes < 1 )
print_CLI_error();
// Validate n_gridpoints
if( input.n_gridpoints < 1 )
print_CLI_error();
// Validate lookups
if( input.lookups < 1 )
print_CLI_error();
// Validate Hash Bins
if( input.hash_bins < 1 )
print_CLI_error();
// Validate HM size
if( strcasecmp(input.HM, "small") != 0 &&
strcasecmp(input.HM, "large") != 0 &&
strcasecmp(input.HM, "XL") != 0 &&
strcasecmp(input.HM, "XXL") != 0 )
print_CLI_error();
// Set HM size specific parameters
// (defaults to large)
if( strcasecmp(input.HM, "small") == 0 )
input.n_isotopes = 68;
else if( strcasecmp(input.HM, "XL") == 0 && user_g == 0 )
input.n_gridpoints = 238847; // sized to make 120 GB XS data
else if( strcasecmp(input.HM, "XXL") == 0 && user_g == 0 )
input.n_gridpoints = 238847 * 2.1; // 252 GB XS data
// Return input struct
return input;
}
void binary_write( Inputs in, SimulationData SD )
{
char * fname = "XS_data.dat";
printf("Writing all data structures to binary file %s...\n", fname);
FILE * fp = fopen(fname, "w");
// Write SimulationData Object. Include pointers, even though we won't be using them.
fwrite(&SD, sizeof(SimulationData), 1, fp);
// Write heap arrays in SimulationData Object
fwrite(SD.num_nucs, sizeof(int), SD.length_num_nucs, fp);
fwrite(SD.concs, sizeof(double), SD.length_concs, fp);
fwrite(SD.mats, sizeof(int), SD.length_mats, fp);
fwrite(SD.nuclide_grid, sizeof(NuclideGridPoint), SD.length_nuclide_grid, fp);
fwrite(SD.index_grid, sizeof(int), SD.length_index_grid, fp);
fwrite(SD.unionized_energy_array, sizeof(double), SD.length_unionized_energy_array, fp);
fclose(fp);
}
SimulationData binary_read( Inputs in )
{
SimulationData SD;
char * fname = "XS_data.dat";
printf("Reading all data structures from binary file %s...\n", fname);
FILE * fp = fopen(fname, "r");
assert(fp != NULL);
// Read SimulationData Object. Include pointers, even though we won't be using them.
fread(&SD, sizeof(SimulationData), 1, fp);
// Allocate space for arrays on heap
SD.num_nucs = (int *) malloc(SD.length_num_nucs * sizeof(int));
SD.concs = (double *) malloc(SD.length_concs * sizeof(double));
SD.mats = (int *) malloc(SD.length_mats * sizeof(int));
SD.nuclide_grid = (NuclideGridPoint *) malloc(SD.length_nuclide_grid * sizeof(NuclideGridPoint));
SD.index_grid = (int *) malloc( SD.length_index_grid * sizeof(int));
SD.unionized_energy_array = (double *) malloc( SD.length_unionized_energy_array * sizeof(double));
// Read heap arrays into SimulationData Object
fread(SD.num_nucs, sizeof(int), SD.length_num_nucs, fp);
fread(SD.concs, sizeof(double), SD.length_concs, fp);
fread(SD.mats, sizeof(int), SD.length_mats, fp);
fread(SD.nuclide_grid, sizeof(NuclideGridPoint), SD.length_nuclide_grid, fp);
fread(SD.index_grid, sizeof(int), SD.length_index_grid, fp);
fread(SD.unionized_energy_array, sizeof(double), SD.length_unionized_energy_array, fp);
fclose(fp);
return SD;
}

Binary file not shown.

View File

@@ -0,0 +1,116 @@
cmake_minimum_required(VERSION 3.0)
project(mimalloc-bench CXX C)
set(CMAKE_CXX_STANDARD 17)
if (NOT CMAKE_BUILD_TYPE)
message(STATUS "No build type selected, default to *** Release ***")
set(CMAKE_BUILD_TYPE "Release")
endif()
FUNCTION(PREPEND var prefix)
SET(listVar "")
FOREACH(f ${ARGN})
LIST(APPEND listVar "${prefix}/${f}")
ENDFOREACH(f)
SET(${var} "${listVar}" PARENT_SCOPE)
ENDFUNCTION(PREPEND)
set(cfrac_sources
cfrac.c
pops.c pconst.c pio.c
pabs.c pneg.c pcmp.c podd.c phalf.c
padd.c psub.c pmul.c pdivmod.c psqrt.c ppowmod.c
atop.c ptoa.c itop.c utop.c ptou.c errorp.c
pfloat.c pidiv.c pimod.c picmp.c
primes.c pcfrac.c pgcd.c)
PREPEND(cfrac_sources . ${cfrac_sources})
# set(espresso_sources
# cofactor.c cols.c compl.c contain.c cubestr.c cvrin.c cvrm.c cvrmisc.c cvrout.c
# dominate.c equiv.c espresso.c essen.c exact.c expand.c gasp.c getopt.c gimpel.c
# globals.c hack.c indep.c irred.c main.c map.c matrix.c mincov.c opo.c pair.c part.c
# primes.c reduce.c rows.c set.c setc.c sharp.c sminterf.c solution.c sparse.c unate.c
# utility.c verify.c)
# PREPEND(espresso_sources . ${espresso_sources})
# set(barnes_sources
# code.c code_io.c load.c grav.c getparam.c util.c)
# PREPEND(barnes_sources barnes/ ${barnes_sources})
# turn off warnings..
message(STATUS "${CMAKE_C_COMPILER_ID}")
if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|Clang|GNU")
set(FLAGS " -w -Wno-implicit-function-declaration -Wno-implicit-int -Wno-int-conversion -Wno-return-mismatch -Wno-incompatible-pointer-types -mabi=purecap-benchmark")
string(APPEND CMAKE_C_FLAGS ${FLAGS})
string(APPEND CMAKE_CXX_FLAGS ${FLAGS})
endif()
add_executable(cfrac ${cfrac_sources})
target_compile_options(cfrac PRIVATE $<$<C_COMPILER_ID:GNU>:-std=gnu89>)
target_compile_definitions(cfrac PRIVATE NOMEMOPT=1)
target_link_libraries(cfrac m)
# add_executable(espresso ${espresso_sources})
# target_compile_options(espresso PRIVATE $<$<C_COMPILER_ID:GNU>:-std=gnu89>)
# target_link_libraries(espresso m)
# add_executable(barnes ${barnes_sources})
# target_link_libraries(barnes m)
# add_executable(larson larson/larson.cpp)
# target_compile_options(larson PRIVATE -Wno-unused-result)
# target_compile_definitions(larson PRIVATE CPP=1)
# target_link_libraries(larson pthread)
# add_executable(larson-sized larson/larson.cpp)
# target_compile_options(larson-sized PRIVATE -Wno-unused-result -fsized-deallocation)
# target_compile_definitions(larson-sized PRIVATE CPP=1 SIZED=1)
# target_link_libraries(larson-sized pthread)
# add_executable(alloc-test alloc-test/test_common.cpp alloc-test/allocator_tester.cpp)
# target_compile_definitions(alloc-test PRIVATE BENCH=4)
# target_link_libraries(alloc-test pthread)
# if(NOT APPLE)
# add_executable(sh6bench shbench/sh6bench-new.c)
# target_compile_definitions(sh6bench PRIVATE BENCH=1 SYS_MULTI_THREAD=1)
# target_link_libraries(sh6bench pthread)
# add_executable(sh8bench shbench/sh8bench-new.c)
# target_compile_definitions(sh8bench PRIVATE BENCH=1 SYS_MULTI_THREAD=1)
# target_link_libraries(sh8bench pthread)
# endif()
# add_executable(cache-scratch cache-scratch/cache-scratch.cpp)
# target_link_libraries(cache-scratch pthread)
# add_executable(cache-thrash cache-thrash/cache-thrash.cpp)
# target_link_libraries(cache-thrash pthread)
# add_executable(xmalloc-test xmalloc-test/xmalloc-test.c)
# target_link_libraries(xmalloc-test pthread)
# add_executable(malloc-large-old malloc-large/malloc-large-old.cpp)
# target_link_libraries(malloc-large-old pthread)
# add_executable(malloc-large malloc-large/malloc-large.cpp)
# target_link_libraries(malloc-large pthread)
# add_executable(mstress mstress/mstress.c)
# target_link_libraries(mstress pthread)
# add_executable(mleak mleak/mleak.c)
# target_link_libraries(mleak pthread)
# add_executable(rptest rptest/rptest.c rptest/thread.c rptest/timer.c)
# target_compile_options(rptest PRIVATE -fpermissive)
# target_include_directories(rptest PRIVATE rptest)
# target_link_libraries(rptest pthread m)
# add_executable(glibc-simple glibc-bench/bench-malloc-simple.c)
# target_link_libraries(glibc-simple pthread)
# add_executable(glibc-thread glibc-bench/bench-malloc-thread.c)
# target_link_libraries(glibc-thread pthread)
#

View File

@@ -0,0 +1,116 @@
cmake_minimum_required(VERSION 3.0)
project(mimalloc-bench CXX C)
set(CMAKE_CXX_STANDARD 17)
if (NOT CMAKE_BUILD_TYPE)
message(STATUS "No build type selected, default to *** Release ***")
set(CMAKE_BUILD_TYPE "Release")
endif()
FUNCTION(PREPEND var prefix)
SET(listVar "")
FOREACH(f ${ARGN})
LIST(APPEND listVar "${prefix}/${f}")
ENDFOREACH(f)
SET(${var} "${listVar}" PARENT_SCOPE)
ENDFUNCTION(PREPEND)
# set(cfrac_sources
# cfrac.c
# pops.c pconst.c pio.c
# pabs.c pneg.c pcmp.c podd.c phalf.c
# padd.c psub.c pmul.c pdivmod.c psqrt.c ppowmod.c
# atop.c ptoa.c itop.c utop.c ptou.c errorp.c
# pfloat.c pidiv.c pimod.c picmp.c
# primes.c pcfrac.c pgcd.c)
# PREPEND(cfrac_sources cfrac/ ${cfrac_sources})
set(espresso_sources
cofactor.c cols.c compl.c contain.c cubestr.c cvrin.c cvrm.c cvrmisc.c cvrout.c
dominate.c equiv.c espresso.c essen.c exact.c expand.c gasp.c getopt.c gimpel.c
globals.c hack.c indep.c irred.c main.c map.c matrix.c mincov.c opo.c pair.c part.c
primes.c reduce.c rows.c set.c setc.c sharp.c sminterf.c solution.c sparse.c unate.c
utility.c verify.c)
PREPEND(espresso_sources . ${espresso_sources})
# set(barnes_sources
# code.c code_io.c load.c grav.c getparam.c util.c)
# PREPEND(barnes_sources barnes/ ${barnes_sources})
# turn off warnings..
message(STATUS "${CMAKE_C_COMPILER_ID}")
if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|Clang|GNU")
set(FLAGS " -w -Wno-implicit-function-declaration -Wno-implicit-int -Wno-int-conversion -Wno-return-mismatch -Wno-incompatible-pointer-types -mabi=purecap-benchmark")
string(APPEND CMAKE_C_FLAGS ${FLAGS})
string(APPEND CMAKE_CXX_FLAGS ${FLAGS})
endif()
# add_executable(cfrac ${cfrac_sources})
# target_compile_options(cfrac PRIVATE $<$<C_COMPILER_ID:GNU>:-std=gnu89>)
# target_compile_definitions(cfrac PRIVATE NOMEMOPT=1)
# target_link_libraries(cfrac m)
add_executable(espresso ${espresso_sources})
target_compile_options(espresso PRIVATE $<$<C_COMPILER_ID:GNU>:-std=gnu89>)
target_link_libraries(espresso m)
# add_executable(barnes ${barnes_sources})
# target_link_libraries(barnes m)
# add_executable(larson larson/larson.cpp)
# target_compile_options(larson PRIVATE -Wno-unused-result)
# target_compile_definitions(larson PRIVATE CPP=1)
# target_link_libraries(larson pthread)
# add_executable(larson-sized larson/larson.cpp)
# target_compile_options(larson-sized PRIVATE -Wno-unused-result -fsized-deallocation)
# target_compile_definitions(larson-sized PRIVATE CPP=1 SIZED=1)
# target_link_libraries(larson-sized pthread)
# add_executable(alloc-test alloc-test/test_common.cpp alloc-test/allocator_tester.cpp)
# target_compile_definitions(alloc-test PRIVATE BENCH=4)
# target_link_libraries(alloc-test pthread)
# if(NOT APPLE)
# add_executable(sh6bench shbench/sh6bench-new.c)
# target_compile_definitions(sh6bench PRIVATE BENCH=1 SYS_MULTI_THREAD=1)
# target_link_libraries(sh6bench pthread)
# add_executable(sh8bench shbench/sh8bench-new.c)
# target_compile_definitions(sh8bench PRIVATE BENCH=1 SYS_MULTI_THREAD=1)
# target_link_libraries(sh8bench pthread)
# endif()
# add_executable(cache-scratch cache-scratch/cache-scratch.cpp)
# target_link_libraries(cache-scratch pthread)
# add_executable(cache-thrash cache-thrash/cache-thrash.cpp)
# target_link_libraries(cache-thrash pthread)
# add_executable(xmalloc-test xmalloc-test/xmalloc-test.c)
# target_link_libraries(xmalloc-test pthread)
# add_executable(malloc-large-old malloc-large/malloc-large-old.cpp)
# target_link_libraries(malloc-large-old pthread)
# add_executable(malloc-large malloc-large/malloc-large.cpp)
# target_link_libraries(malloc-large pthread)
# add_executable(mstress mstress/mstress.c)
# target_link_libraries(mstress pthread)
# add_executable(mleak mleak/mleak.c)
# target_link_libraries(mleak pthread)
# add_executable(rptest rptest/rptest.c rptest/thread.c rptest/timer.c)
# target_compile_options(rptest PRIVATE -fpermissive)
# target_include_directories(rptest PRIVATE rptest)
# target_link_libraries(rptest pthread m)
# add_executable(glibc-simple glibc-bench/bench-malloc-simple.c)
# target_link_libraries(glibc-simple pthread)
# add_executable(glibc-thread glibc-bench/bench-malloc-thread.c)
# target_link_libraries(glibc-thread pthread)
#

View File

@@ -39,8 +39,8 @@
#include "coz.h"
#define malloc MALLOC
#define free FREE
// #define malloc MALLOCCHERI
// #define free FREECHERI
#define DEF_NUM_POINTS 150000
#define DEF_NUM_MEANS 100
@@ -270,8 +270,8 @@ int main(int argc, char **argv)
// Extra code snippet added
// printf("Initial alloc called\n");
//INITAlloc();
//INITREGULARALLOC();
init_malloc();
// INITREGULARALLOC();
// init_malloc();
int num_procs, curr_point;
int i;

View File

@@ -1,7 +1,10 @@
sh build.sh
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
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
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
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
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
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
# 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

@@ -1,177 +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>
// /* 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/
// // 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
// // #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.
// 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
// 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;
// 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);
}
// // 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
// 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;
}
// #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))
// #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;
// // 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;
}
// // 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;
// // 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;
// // 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;
}
// // 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;
// // 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;
// // 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);
}
}
// // 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;
}
// // Do not call this unless you are absolutely sure about the cause of poisoning
// void clear_posion() {
// poison = 0;
// }

View File

@@ -54,29 +54,29 @@
#define MAXPAGESIZES 2
init_malloc(void) {
// Init malloc implementation
//INITREGULARALLOC();
// init_malloc(void) {
// // Init malloc implementation
// INITREGULARALLOC();
// init_alloc(100,100000);
// brm_init();
alloc_init(104857632);
}
// // init_alloc(100,100000);
// // brm_init();
// //alloc_init(104857632);
// }
void* MALLOC(size_t sz) {
// malloc implementation
//return MALLOCCHERI(sz);
// return malloc_buddy(sz);
return alloc(sz);
// return (void *)alloc_chunk();
}
// void* MALLOC(size_t sz) {
// // malloc implementation
// return MALLOCCHERI(sz);
// // return malloc_buddy(sz);
// // return alloc(sz);
// // return (void *)alloc_chunk();
// }
void FREE(void *ptr) {
// free implementation
//FREECHERI(ptr);
// free_chunk(ptr);
free_mem(ptr);
}
// void FREE(void *ptr) {
// // free implementation
// FREECHERI(ptr);
// // free_chunk(ptr);
// //free_mem(ptr);
// }
//#define TIMING

View File

@@ -441,7 +441,8 @@ int main(int argc, char* argv[])
unsigned long start = microseconds();
result += inner_loop(inner_iterations);
unsigned long elapsed = microseconds() - start;
printf("Richards: iterations=1 runtime: %lu%s\n", elapsed, "us");
// printf("Richards: iterations=1 runtime: %lu%s\n", elapsed, "us");
print(elapsed);
iterations--;
}
}

View File

@@ -0,0 +1 @@
cc -g -Wall -o main.out -mabi=purecap-benchmark -lpthread xmalloc-test.c