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 0000000..ff701fd Binary files /dev/null and b/benchmarks/benchmarks/XSbench/GridInit.o differ diff --git a/benchmarks/benchmarks/XSbench/Main.c b/benchmarks/benchmarks/XSbench/Main.c new file mode 100644 index 0000000..6498bec --- /dev/null +++ b/benchmarks/benchmarks/XSbench/Main.c @@ -0,0 +1,123 @@ +#include "XSbench_header.h" + +#ifdef MPI +#include +#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 0000000..4f3180e Binary files /dev/null and b/benchmarks/benchmarks/XSbench/Main.o differ 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 0000000..a70ea7d Binary files /dev/null and b/benchmarks/benchmarks/XSbench/Materials.o differ 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 0000000..7a554f6 Binary files /dev/null and b/benchmarks/benchmarks/XSbench/Simulations.o differ diff --git a/benchmarks/benchmarks/XSbench/XSBench b/benchmarks/benchmarks/XSbench/XSBench new file mode 100755 index 0000000..e88a93d Binary files /dev/null and b/benchmarks/benchmarks/XSbench/XSBench differ diff --git a/benchmarks/benchmarks/XSbench/XSbench_header.h b/benchmarks/benchmarks/XSbench/XSbench_header.h new file mode 100644 index 0000000..a0229ed --- /dev/null +++ b/benchmarks/benchmarks/XSbench/XSbench_header.h @@ -0,0 +1,151 @@ +#ifndef __XSBENCH_HEADER_H__ +#define __XSBENCH_HEADER_H__ + +#include +#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 0000000..691a16d Binary files /dev/null and b/benchmarks/benchmarks/XSbench/XSutils.o differ diff --git a/benchmarks/benchmarks/XSbench/io.c b/benchmarks/benchmarks/XSbench/io.c new file mode 100644 index 0000000..f8b85c3 --- /dev/null +++ b/benchmarks/benchmarks/XSbench/io.c @@ -0,0 +1,508 @@ +#include "XSbench_header.h" + +#ifdef MPI +#include +#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 0000000..8ab5795 Binary files /dev/null and b/benchmarks/benchmarks/XSbench/io.o differ diff --git a/benchmarks/benchmarks/cfrac/CMakeLists.txt b/benchmarks/benchmarks/cfrac/CMakeLists.txt new file mode 100644 index 0000000..c594ab1 --- /dev/null +++ b/benchmarks/benchmarks/cfrac/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_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/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