From 60de3503723219caebbda2c2a5c79f2925b435d6 Mon Sep 17 00:00:00 2001 From: Akilan Date: Thu, 12 Dec 2024 17:18:16 +0000 Subject: [PATCH] added XSBench --- benchmarks/benchmarks/CMakeLists.txt | 130 ++- .../benchmarks/StressTestMalloc/glibc-bench.c | 13 +- .../StressTestMalloc/glibc-bench.sh | 2 +- .../benchmarks/StressTestMalloc/loadmem.c | 24 +- .../StressTestMalloc/memaccesstest.c | 6 +- .../StressTestMalloc/memaccesstest.sh | 2 +- .../StressTestMalloc/run-loadmem.sh | 2 +- benchmarks/benchmarks/XSbench/GridInit.c | 230 +++++ benchmarks/benchmarks/XSbench/GridInit.o | Bin 0 -> 15632 bytes benchmarks/benchmarks/XSbench/Main.c | 123 +++ benchmarks/benchmarks/XSbench/Main.o | Bin 0 -> 10752 bytes benchmarks/benchmarks/XSbench/Makefile | 86 ++ benchmarks/benchmarks/XSbench/Materials.c | 117 +++ benchmarks/benchmarks/XSbench/Materials.o | Bin 0 -> 13664 bytes benchmarks/benchmarks/XSbench/Simulations.c | 871 ++++++++++++++++++ benchmarks/benchmarks/XSbench/Simulations.o | Bin 0 -> 56400 bytes benchmarks/benchmarks/XSbench/XSBench | Bin 0 -> 95832 bytes .../benchmarks/XSbench/XSbench_header.h | 151 +++ benchmarks/benchmarks/XSbench/XSutils.c | 63 ++ benchmarks/benchmarks/XSbench/XSutils.o | Bin 0 -> 7288 bytes benchmarks/benchmarks/XSbench/io.c | 508 ++++++++++ benchmarks/benchmarks/XSbench/io.o | Bin 0 -> 45280 bytes benchmarks/benchmarks/cfrac/CMakeLists.txt | 116 +++ benchmarks/benchmarks/espresso/CMakeLists.txt | 116 +++ benchmarks/benchmarks/kmeans/kmeans-pthread.c | 8 +- .../benchmarks/kmeans/run-cluster-sizes.sh | 7 +- benchmarks/benchmarks/kmeans/simple.h | 318 +++---- benchmarks/benchmarks/kmeans/stddefines.h | 40 +- .../benchmarks/richards-benchmark/benchmark.c | 3 +- benchmarks/benchmarks/xmalloc-test/build.sh | 1 + 30 files changed, 2713 insertions(+), 224 deletions(-) create mode 100644 benchmarks/benchmarks/XSbench/GridInit.c create mode 100644 benchmarks/benchmarks/XSbench/GridInit.o create mode 100644 benchmarks/benchmarks/XSbench/Main.c create mode 100644 benchmarks/benchmarks/XSbench/Main.o create mode 100644 benchmarks/benchmarks/XSbench/Makefile create mode 100644 benchmarks/benchmarks/XSbench/Materials.c create mode 100644 benchmarks/benchmarks/XSbench/Materials.o create mode 100644 benchmarks/benchmarks/XSbench/Simulations.c create mode 100644 benchmarks/benchmarks/XSbench/Simulations.o create mode 100755 benchmarks/benchmarks/XSbench/XSBench create mode 100644 benchmarks/benchmarks/XSbench/XSbench_header.h create mode 100644 benchmarks/benchmarks/XSbench/XSutils.c create mode 100644 benchmarks/benchmarks/XSbench/XSutils.o create mode 100644 benchmarks/benchmarks/XSbench/io.c create mode 100644 benchmarks/benchmarks/XSbench/io.o create mode 100644 benchmarks/benchmarks/cfrac/CMakeLists.txt create mode 100644 benchmarks/benchmarks/espresso/CMakeLists.txt create mode 100644 benchmarks/benchmarks/xmalloc-test/build.sh diff --git a/benchmarks/benchmarks/CMakeLists.txt b/benchmarks/benchmarks/CMakeLists.txt index 32b64e7..092da36 100644 --- a/benchmarks/benchmarks/CMakeLists.txt +++ b/benchmarks/benchmarks/CMakeLists.txt @@ -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 $<$:-std=gnu89>) +target_compile_definitions(cfrac PRIVATE NOMEMOPT=1) +target_link_libraries(cfrac m) + +add_executable(espresso ${espresso_sources}) +target_compile_options(espresso PRIVATE $<$:-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) + +# \ No newline at end of file diff --git a/benchmarks/benchmarks/StressTestMalloc/glibc-bench.c b/benchmarks/benchmarks/StressTestMalloc/glibc-bench.c index 5c603f0..6e1c5dc 100644 --- a/benchmarks/benchmarks/StressTestMalloc/glibc-bench.c +++ b/benchmarks/benchmarks/StressTestMalloc/glibc-bench.c @@ -24,8 +24,8 @@ #include #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; } \ No newline at end of file diff --git a/benchmarks/benchmarks/StressTestMalloc/glibc-bench.sh b/benchmarks/benchmarks/StressTestMalloc/glibc-bench.sh index 5135664..e281b2f 100644 --- a/benchmarks/benchmarks/StressTestMalloc/glibc-bench.sh +++ b/benchmarks/benchmarks/StressTestMalloc/glibc-bench.sh @@ -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 \ No newline at end of file +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 \ No newline at end of file diff --git a/benchmarks/benchmarks/StressTestMalloc/loadmem.c b/benchmarks/benchmarks/StressTestMalloc/loadmem.c index 54326e7..ba769bf 100644 --- a/benchmarks/benchmarks/StressTestMalloc/loadmem.c +++ b/benchmarks/benchmarks/StressTestMalloc/loadmem.c @@ -2,8 +2,8 @@ #include #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; diff --git a/benchmarks/benchmarks/StressTestMalloc/memaccesstest.c b/benchmarks/benchmarks/StressTestMalloc/memaccesstest.c index 481191b..abf2ef1 100644 --- a/benchmarks/benchmarks/StressTestMalloc/memaccesstest.c +++ b/benchmarks/benchmarks/StressTestMalloc/memaccesstest.c @@ -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 diff --git a/benchmarks/benchmarks/StressTestMalloc/memaccesstest.sh b/benchmarks/benchmarks/StressTestMalloc/memaccesstest.sh index 8b40253..b5002d8 100644 --- a/benchmarks/benchmarks/StressTestMalloc/memaccesstest.sh +++ b/benchmarks/benchmarks/StressTestMalloc/memaccesstest.sh @@ -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 \ No newline at end of file +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 \ No newline at end of file diff --git a/benchmarks/benchmarks/StressTestMalloc/run-loadmem.sh b/benchmarks/benchmarks/StressTestMalloc/run-loadmem.sh index ad5432e..39fcc22 100644 --- a/benchmarks/benchmarks/StressTestMalloc/run-loadmem.sh +++ b/benchmarks/benchmarks/StressTestMalloc/run-loadmem.sh @@ -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 \ No newline at end of file +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 \ No newline at end of file diff --git a/benchmarks/benchmarks/XSbench/GridInit.c b/benchmarks/benchmarks/XSbench/GridInit.c new file mode 100644 index 0000000..08a0f27 --- /dev/null +++ b/benchmarks/benchmarks/XSbench/GridInit.c @@ -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; +} \ No newline at end of file diff --git a/benchmarks/benchmarks/XSbench/GridInit.o b/benchmarks/benchmarks/XSbench/GridInit.o new file mode 100644 index 0000000000000000000000000000000000000000..ff701fdc57822574479e7a956fb1d56e06220955 GIT binary patch literal 15632 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 +#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; +} \ No newline at end of file diff --git a/benchmarks/benchmarks/XSbench/Main.o b/benchmarks/benchmarks/XSbench/Main.o new file mode 100644 index 0000000000000000000000000000000000000000..4f3180e8c5d743e2f5177a4c25a98c933ae08216 GIT binary patch literal 10752 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 literal 0 HcmV?d00001 diff --git a/benchmarks/benchmarks/XSbench/Makefile b/benchmarks/benchmarks/XSbench/Makefile new file mode 100644 index 0000000..4fcfab8 --- /dev/null +++ b/benchmarks/benchmarks/XSbench/Makefile @@ -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) diff --git a/benchmarks/benchmarks/XSbench/Materials.c b/benchmarks/benchmarks/XSbench/Materials.c new file mode 100644 index 0000000..5606aeb --- /dev/null +++ b/benchmarks/benchmarks/XSbench/Materials.c @@ -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; +} diff --git a/benchmarks/benchmarks/XSbench/Materials.o b/benchmarks/benchmarks/XSbench/Materials.o new file mode 100644 index 0000000000000000000000000000000000000000..a70ea7dbe87c076bb62f3f726e60961e4f850099 GIT binary patch literal 13664 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 literal 0 HcmV?d00001 diff --git a/benchmarks/benchmarks/XSbench/Simulations.c b/benchmarks/benchmarks/XSbench/Simulations.c new file mode 100644 index 0000000..0676e48 --- /dev/null +++ b/benchmarks/benchmarks/XSbench/Simulations.c @@ -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 " 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 " 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) 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) 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; +} \ No newline at end of file diff --git a/benchmarks/benchmarks/XSbench/Simulations.o b/benchmarks/benchmarks/XSbench/Simulations.o new file mode 100644 index 0000000000000000000000000000000000000000..7a554f6a73f552e1b211cd88893b05337407f28b GIT binary patch literal 56400 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 literal 0 HcmV?d00001 diff --git a/benchmarks/benchmarks/XSbench/XSBench b/benchmarks/benchmarks/XSbench/XSBench new file mode 100755 index 0000000000000000000000000000000000000000..e88a93d7334ee3080a2950bc16907082389f0983 GIT binary patch literal 95832 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 +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#define strncasecmp _strnicmp +#define strcasecmp _stricmp +#else +#include +#include +#endif + +#ifdef OPENMP +#include +#endif + +// Papi Header +#ifdef PAPI +#include "papi.h" +#endif + +//AML header +#ifdef AML +#include +#include +#include +#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 \ No newline at end of file diff --git a/benchmarks/benchmarks/XSbench/XSutils.c b/benchmarks/benchmarks/XSbench/XSutils.c new file mode 100644 index 0000000..72984c1 --- /dev/null +++ b/benchmarks/benchmarks/XSbench/XSutils.c @@ -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; +} \ No newline at end of file diff --git a/benchmarks/benchmarks/XSbench/XSutils.o b/benchmarks/benchmarks/XSbench/XSutils.o new file mode 100644 index 0000000000000000000000000000000000000000..691a16d421f58a6000223cdb43681fc93795b5ee GIT binary patch literal 7288 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 pk?Gqm7O|SbU!} +#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 \n"); + printf("Options include:\n"); + printf(" -m Simulation method (history, event)\n"); + printf(" -t Number of OpenMP threads to run\n"); + printf(" -s Size of H-M Benchmark to run (small, large, XL, XXL)\n"); + printf(" -g Number of gridpoints per nuclide (overrides -s defaults)\n"); + printf(" -G Grid search type (unionized, nuclide, hash). Defaults to unionized.\n"); + printf(" -p Number of particle histories\n"); + printf(" -l History Based: Number of Cross-section (XS) lookups per particle. Event Based: Total number of XS lookups.\n"); + printf(" -h Number of hash bins (only relevant when used with \"-G hash\")\n"); + printf(" -b Read or write all data structures to file. If reading, this will skip initialization phase. (read, write)\n"); + printf(" -k 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; +} \ No newline at end of file diff --git a/benchmarks/benchmarks/XSbench/io.o b/benchmarks/benchmarks/XSbench/io.o new file mode 100644 index 0000000000000000000000000000000000000000..8ab5795c09166acafec298ca805b2b3a1e328bc6 GIT binary patch literal 45280 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 nsRRC;g~v|@SRNO>{=)$D*rDBmJMa_6u{3*):-std=gnu89>) +target_compile_definitions(cfrac PRIVATE NOMEMOPT=1) +target_link_libraries(cfrac m) + +# add_executable(espresso ${espresso_sources}) +# target_compile_options(espresso PRIVATE $<$:-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) + +# \ No newline at end of file diff --git a/benchmarks/benchmarks/espresso/CMakeLists.txt b/benchmarks/benchmarks/espresso/CMakeLists.txt new file mode 100644 index 0000000..697255d --- /dev/null +++ b/benchmarks/benchmarks/espresso/CMakeLists.txt @@ -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 $<$:-std=gnu89>) +# target_compile_definitions(cfrac PRIVATE NOMEMOPT=1) +# target_link_libraries(cfrac m) + +add_executable(espresso ${espresso_sources}) +target_compile_options(espresso PRIVATE $<$:-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) + +# \ No newline at end of file diff --git a/benchmarks/benchmarks/kmeans/kmeans-pthread.c b/benchmarks/benchmarks/kmeans/kmeans-pthread.c index 7d08df8..6ea03aa 100644 --- a/benchmarks/benchmarks/kmeans/kmeans-pthread.c +++ b/benchmarks/benchmarks/kmeans/kmeans-pthread.c @@ -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; diff --git a/benchmarks/benchmarks/kmeans/run-cluster-sizes.sh b/benchmarks/benchmarks/kmeans/run-cluster-sizes.sh index d9f98fb..8cc411f 100644 --- a/benchmarks/benchmarks/kmeans/run-cluster-sizes.sh +++ b/benchmarks/benchmarks/kmeans/run-cluster-sizes.sh @@ -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 \ No newline at end of file +# 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 \ No newline at end of file diff --git a/benchmarks/benchmarks/kmeans/simple.h b/benchmarks/benchmarks/kmeans/simple.h index 9de2a96..35c3a98 100644 --- a/benchmarks/benchmarks/kmeans/simple.h +++ b/benchmarks/benchmarks/kmeans/simple.h @@ -1,177 +1,177 @@ -/* Copyright (C) 2023. Shivashish Das. Licensed under the MIT License.*/ -#include -#include -#include -#include -#include +// /* Copyright (C) 2023. Shivashish Das. Licensed under the MIT License.*/ +// #include +// #include +// #include +// #include +// #include -// 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 -#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 +// #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; -} \ No newline at end of file +// // Do not call this unless you are absolutely sure about the cause of poisoning +// void clear_posion() { +// poison = 0; +// } \ No newline at end of file diff --git a/benchmarks/benchmarks/kmeans/stddefines.h b/benchmarks/benchmarks/kmeans/stddefines.h index 966bf69..0f99e04 100644 --- a/benchmarks/benchmarks/kmeans/stddefines.h +++ b/benchmarks/benchmarks/kmeans/stddefines.h @@ -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 diff --git a/benchmarks/benchmarks/richards-benchmark/benchmark.c b/benchmarks/benchmarks/richards-benchmark/benchmark.c index 51ef20e..d6ff568 100644 --- a/benchmarks/benchmarks/richards-benchmark/benchmark.c +++ b/benchmarks/benchmarks/richards-benchmark/benchmark.c @@ -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--; } } \ No newline at end of file diff --git a/benchmarks/benchmarks/xmalloc-test/build.sh b/benchmarks/benchmarks/xmalloc-test/build.sh new file mode 100644 index 0000000..e681000 --- /dev/null +++ b/benchmarks/benchmarks/xmalloc-test/build.sh @@ -0,0 +1 @@ +cc -g -Wall -o main.out -mabi=purecap-benchmark -lpthread xmalloc-test.c \ No newline at end of file