From ead59bc4531deb988095dfe57b3b421deea81f4a Mon Sep 17 00:00:00 2001 From: TB Schardl Date: Wed, 17 Feb 2021 17:06:38 +0000 Subject: [PATCH] [test/cilksan] Add some simple tests for Cilksan functionality. --- test/cilksan/alloctest.c | 29 +++ test/cilksan/alloctypes.cpp | 154 +++++++++++++ test/cilksan/alpha-beta.c | 64 ++++++ test/cilksan/call-once.cpp | 27 +++ test/cilksan/increment.cpp | 46 ++++ test/cilksan/libraces.cpp | 86 ++++++++ test/cilksan/noalias.c | 51 +++++ test/cilksan/reducer-lock-test.cpp | 24 ++ test/cilksan/sum-ints.cpp | 342 +++++++++++++++++++++++++++++ test/cilksan/sum-vector-int.c | 275 +++++++++++++++++++++++ test/cilksan/sum-vector-int.cpp | 335 ++++++++++++++++++++++++++++ test/cilksan/syncregs.c | 30 +++ test/cilksan/vla.c | 60 +++++ 13 files changed, 1523 insertions(+) create mode 100644 test/cilksan/alloctest.c create mode 100644 test/cilksan/alloctypes.cpp create mode 100644 test/cilksan/alpha-beta.c create mode 100644 test/cilksan/call-once.cpp create mode 100644 test/cilksan/increment.cpp create mode 100644 test/cilksan/libraces.cpp create mode 100644 test/cilksan/noalias.c create mode 100644 test/cilksan/reducer-lock-test.cpp create mode 100644 test/cilksan/sum-ints.cpp create mode 100644 test/cilksan/sum-vector-int.c create mode 100644 test/cilksan/sum-vector-int.cpp create mode 100644 test/cilksan/syncregs.c create mode 100644 test/cilksan/vla.c diff --git a/test/cilksan/alloctest.c b/test/cilksan/alloctest.c new file mode 100644 index 0000000..f8ed9b9 --- /dev/null +++ b/test/cilksan/alloctest.c @@ -0,0 +1,29 @@ +#include +#include +#include + +#include + +int main(int argc, char *argv[]) { + long n = 0; + if (argc > 1) + n = atol(argv[1]); + + // Check that instrumentation on this allocation is handled + // correctly, even when n == 0. + long x[n]; + + // Ensure that we have racing accesses on x, so that x is + // instrumented. + cilk_spawn { + cilk_for (long i = 0; i < n; ++i) + x[i] = sin(i); + } + cilk_for (long i = 0; i < n; ++i) + x[i] = cos(i); + + for (long i = 0; i < n; ++i) + printf("sin(%ld) = %ld\n", i, x[i]); + + return 0; +} diff --git a/test/cilksan/alloctypes.cpp b/test/cilksan/alloctypes.cpp new file mode 100644 index 0000000..c502d8d --- /dev/null +++ b/test/cilksan/alloctypes.cpp @@ -0,0 +1,154 @@ +#include +#include + +struct Foo_st { + int a = 0; + double b = 0.0; +}; + +class Foo { + int val = 0; +public: + Foo() {} + ~Foo() {} + int &getVal() { return val; } + void incVal() { val++; } +}; + +class Bar { + int val[4] = {0,0,0,0}; +public: + Bar() {} + ~Bar() {} + int &getVal(int i) { return val[i]; } + void incVal(int i) { val[i]++; } +}; + +int global = 0; + +__attribute__((noinline)) +static void helper(int &x) { + x++; +} + +static void arr_helper(int *x, int n) { + for (int i = 0; i < n; i++) + x[i]++; +} + +void global_test() { + std::cout << "global_test\n"; + cilk_for (int i = 0; i < 1000; i++) + helper(global); + + cilk_for (int i = 0; i < 1000; i++) + global--; + std::cout << global << '\n'; +} + +void local_test() { + std::cout << "local_test\n"; + int local = 1; + cilk_for (int i = 0; i < 1000; i++) + helper(local); + std::cout << local << '\n'; +} + +void param_test(int ¶m) { + std::cout << "param_test\n"; + cilk_for (int i = 0; i < 1000; i++) + helper(param); + std::cout << param << '\n'; +} + +int *malloc_test(int size) { + std::cout << "malloc_test\n"; + int *x = (int*)malloc(size * sizeof(int)); + x[0] = 0; + cilk_for (int i = 0; i < 1000; i++) + arr_helper(x, size); + std::cout << x[0] << '\n'; + return x; +} + +void calloc_test(int size) { + std::cout << "calloc_test\n"; + int *y = (int*)calloc(size, sizeof(int)); + cilk_for (int i = 0; i < 1000; i++) + arr_helper(y, size); + std::cout << y[0] << '\n'; + free(y); +} + +int *realloc_test(int *x, int size) { + std::cout << "realloc_test\n"; + x = (int*)realloc(x, size * sizeof(int)); + cilk_for (int i = 0; i < 1000; i++) + arr_helper(x, size); + std::cout << x[0] << '\n'; + return x; +} + +void new_test() { + std::cout << "new_test\n"; + Foo *x = new Foo(); + cilk_for (int i = 0; i < 1000; i++) + x->getVal()--; + + cilk_for (int i = 0; i < 1000; i++) + x->incVal(); + std::cout << "x->getVal() = " << x->getVal() << '\n'; + delete x; + + Bar *y = new Bar(); + cilk_for (int i = 0; i < 1000; i++) + y->getVal(i % 4)--; + + cilk_for (int i = 0; i < 1000; i++) + y->incVal(i % 4); + std::cout << "y->getVal(0) = " << y->getVal(0) << '\n'; + delete y; + + Foo_st *z = new Foo_st(); + cilk_for (int i = 0; i < 1000; i++) { + z->a++; + z->b += z->a * i; + } + std::cout << "z->b = " << z->b << '\n'; + delete z; +} + +int main(int argc, char** argv) { + int arrsize = 1; + if (argc == 2) + arrsize = atoi(argv[1]); + + // Race 1 + global_test(); + + // Race 2 + local_test(); + + int parent_local = 2; + // Race 3 + param_test(parent_local); + + // Race 4 + int *x = malloc_test(arrsize); + + // Race 5 + calloc_test(arrsize); + + // Race 6 + x = realloc_test(x, 5 * arrsize); + free(x); + + // Redundant with race 4 + x = malloc_test(arrsize); + free(x); + + // Races 7-12 + new_test(); + + return 0; +} diff --git a/test/cilksan/alpha-beta.c b/test/cilksan/alpha-beta.c new file mode 100644 index 0000000..e277921 --- /dev/null +++ b/test/cilksan/alpha-beta.c @@ -0,0 +1,64 @@ + +#include +#include + +#include +//#include "../cilktool/cilktool.h" + +#define DEPTH_THRESHOLD 7 + +typedef struct node_s { + const struct node_s* parent; + uint32_t value; + uint32_t depth; +} node_t; + + +// Function declarations +void evaluateMove(const node_t *move); +void scout_search(node_t *move, uint32_t depth); + + +void initialize_move(node_t *move, uint32_t depth) { + move->depth = depth; +} + +void evaluateMove(const node_t *move) { + node_t next_move; + next_move.parent = move; + next_move.value = 2 * move->value; + +/* #if EXPOSE_BUG == 1 */ + // NO RACE + uint32_t new_depth = move->depth + 1; +/* #else */ +/* volatile uint32_t new_depth = move->depth + 1; */ +/* #endif */ + + // Search further using `scout_search` + scout_search(&next_move, new_depth); +} + +void scout_search(node_t *move, uint32_t depth) { + // Stop the recursion once our depth surpassed a threshold + if (depth > DEPTH_THRESHOLD) { + return; + } + + initialize_move(move, depth); + + cilk_for(uint32_t i = 0; i < move->value; i++) { + evaluateMove(move); + } +} + +int main() { + node_t root_move; + root_move.parent = NULL; + root_move.value = 1; + root_move.depth = 1; + + evaluateMove(&root_move); + + return 0; +} diff --git a/test/cilksan/call-once.cpp b/test/cilksan/call-once.cpp new file mode 100644 index 0000000..3df325e --- /dev/null +++ b/test/cilksan/call-once.cpp @@ -0,0 +1,27 @@ +#include +#include +#include + +int x = 0; + +int bar() +{ + static std::once_flag initialized; + + std::call_once(initialized, []() { + std::cout << "initializing x = 1 in bar once" << std::endl; + x = 1; + } + ); + return x; +} + +int main(int argc, char **argv) +{ + int a = cilk_spawn bar(); + int b = cilk_spawn bar(); + cilk_sync; + std::cout << "a + b = " << a + b << std::endl; + + return 0; +} diff --git a/test/cilksan/increment.cpp b/test/cilksan/increment.cpp new file mode 100644 index 0000000..d34e0f3 --- /dev/null +++ b/test/cilksan/increment.cpp @@ -0,0 +1,46 @@ +#include +#include +//#include + +int global = 0; + +void increment(int *x, int n) { + for (int i = 0; i < n; i++) + x[i]++; +} + +__attribute__((noinline)) +void helper(int *x) { + (*x)++; +} + +int main(int argc, char** argv) { + int n = 1; + if (argc == 2) n = atoi(argv[1]); + + cilk_for (int i = 0; i < 1000; i++) + helper(&global); + std::cout << global << '\n'; + + int local = 1; + cilk_for (int i = 0; i < 1000; i++) + helper(&local); + std::cout << local << '\n'; + + int *x = (int*)malloc(n * sizeof(int)); + cilk_for (int i = 0; i < 1000; i++) + increment(x, n); + std::cout << x[0] << '\n'; + + int *y = (int*)calloc(n, sizeof(int)); + cilk_for (int i = 0; i < 1000; i++) + increment(y, n); + std::cout << y[0] << '\n'; + + int *z = (int*)realloc(x, 4 * n * sizeof(int)); + cilk_for (int i = 0; i < 1000; i++) + increment(z, 2 * n); + std::cout << z[0] << '\n'; + + return 0; +} diff --git a/test/cilksan/libraces.cpp b/test/cilksan/libraces.cpp new file mode 100644 index 0000000..2b71bf3 --- /dev/null +++ b/test/cilksan/libraces.cpp @@ -0,0 +1,86 @@ +#include +#include +#include +#include + +double global = 0.0; + +void global_printf_test(int n) { + // Write-read race + cilk_spawn { global += n; } + printf("global = %f\n", global); +} + +void global_cout_test(int n) { + // Write-read race + cilk_spawn { global *= n; } + std::cout << "global = " << global << "\n"; +} + +static void arr_helper(int *x, int n) { + for (int i = 0; i < n; i++) + x[i]++; +} + +void malloc_free_test(int size) { + int *x = (int*)malloc(size * sizeof(int)); + // Write-free race + cilk_spawn arr_helper(x, size); + free(x); +} + +void malloc_printf_test(int size) { + int *x = (int*)malloc(size * sizeof(int)); + // Write-read race + cilk_spawn arr_helper(x, size); + printf("x[0] = %d\n", x[0]); + cilk_sync; + free(x); +} + +void malloc_cout_test(int size) { + int *x = (int*)malloc(size * sizeof(int)); + // Write-read race + cilk_spawn arr_helper(x, size); + std::cout << "x[0] = " << x[0] << "\n"; + cilk_sync; + free(x); +} + +void str_printf_test() { + const char *str = "Hello, world!"; + char *cpy = (char *)malloc(sizeof(*str)); + cilk_spawn strcpy(cpy, str); + // No race + printf("str len = %ld\n", strlen(str)); + // Race with spawned strcpy + cilk_spawn printf("cpy len = %ld\n", strlen(cpy)); + // Race with spawned strcpy + char *str2 = cilk_spawn strdup(cpy); + // Race with spawned strdup + printf("str2: %s\n", str2); + // Race with spawned strcpy + char *str3 = cilk_spawn strndup(cpy, 5); + // Race with spawned strndup + printf("str3: %s\n", str3); + free(str2); + free(str3); +} + +int main(int argc, char** argv) { + int size = 1; + if (argc == 2) + size = atoi(argv[1]); + + global_printf_test(size); + global_cout_test(size); + + malloc_free_test(size); + + malloc_printf_test(size); + malloc_cout_test(size); + + str_printf_test(); + + return 0; +} diff --git a/test/cilksan/noalias.c b/test/cilksan/noalias.c new file mode 100644 index 0000000..3719283 --- /dev/null +++ b/test/cilksan/noalias.c @@ -0,0 +1,51 @@ +#include +#include +#include + +void inc(int *x) { + cilk_spawn x[0]++; + x[1]++; +} + +void inc_loop(int *x, int *y) { + #pragma cilk grainsize 1 + cilk_for (int i = 0; i < 2; ++i) { + x[i]++; + y[i]++; + } +} + +__attribute__((noinline)) +int *frob(int *x) { + return x+1; +} + +int main(int argc, char *argv[]) { + int n = 10; + if (argc > 1) + n = atoi(argv[1]); + + int *x = (int*)malloc(n * sizeof(int)); + cilk_for (int i = 0; i < n; ++i) + x[i] = 0; + + // Race + inc_loop(x, x+1); + // Duplicate race + inc_loop(x+2, frob(x+2)); + + // Race + cilk_spawn inc(x); + inc(x+1); + cilk_sync; + + // Duplicate race + cilk_spawn inc(x+3); + inc(frob(x+3)); + cilk_sync; + + for (int i = 0; i < n; ++i) + printf("x[%d] = %d\n", i, x[i]); + + return 0; +} diff --git a/test/cilksan/reducer-lock-test.cpp b/test/cilksan/reducer-lock-test.cpp new file mode 100644 index 0000000..579768a --- /dev/null +++ b/test/cilksan/reducer-lock-test.cpp @@ -0,0 +1,24 @@ +#include +#include +#include +#include +#include +#include + +int main() { + cilk::reducer_opadd sum; + int rsum = 0; + int lsum = 0; + pthread_mutex_t mtex; + pthread_mutex_init(&mtex, NULL); + cilk_for (int i = 0; i <= 10000; i++) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + sum += i; + rsum += i; + pthread_mutex_lock(&mtex); + lsum += i; + pthread_mutex_unlock(&mtex); + // &nbs p; + } + printf("%d\n%d\n%d\n",sum.get_value(),rsum,lsum); +} diff --git a/test/cilksan/sum-ints.cpp b/test/cilksan/sum-ints.cpp new file mode 100644 index 0000000..70d1995 --- /dev/null +++ b/test/cilksan/sum-ints.cpp @@ -0,0 +1,342 @@ +/** -*- C++ -*- + * + * \file sum-ints.cpp + * + * @brief Sum-accumulation of integer values. + * + * @author Tao B. Schardl (neboat@mit.edu) and Alexandros-Stavros Iliopoulos (ailiop@mit.edu) + * + */ + +/* ================================================== + * INCLUDE DEPENDENCIES + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +/* ================================================== + * ALIASES + */ + + +using num_t = long int; +using sum_t = long long int; + +// using num_vec_t = std::vector; + +using chrono_clock = std::chrono::high_resolution_clock; + +using accum_func_t = sum_t (*) (num_t); + + + +/* ================================================== + * CONSTANTS + */ + + +constexpr num_t N = 10 * 1000 * 1000; + +constexpr num_t MAX_VAL = 100; + + + +/* ================================================== + * FORWARD DECLARATIONS + */ + + +sum_t accum_true (num_t); +sum_t accum_wrong (num_t); +sum_t accum_lock (num_t); +sum_t accum_spawn (num_t); +sum_t accum_reducer (num_t); +sum_t accum_wls (num_t); + +template +sum_t time_accum_func (num_t, + sum_t const = -1, + std::string const = "accumulation function (no description)"); + + + +/* ================================================== + * ANONYMOUS NAMESPACE (PRIVATE FUNCTIONS) + */ + + +namespace { + + sum_t accum_spawn_helper (num_t begin, + num_t end) { + + auto const n = end - begin; + + switch (n) { + + case 1: + return static_cast(begin); + + case 2: + return static_cast(begin + (end - 1)); + + default: + auto const nhalf = n/2 + 1; + auto const s1 = cilk_spawn accum_spawn_helper( begin, begin + nhalf ); + auto const s2 = cilk_spawn accum_spawn_helper( begin + nhalf, end ); + cilk_sync; + return (s1 + s2); + + } // end switch (n) + + } // end function "accum_spawn_helper" + +} // end anonymous namespace + + + +/* ================================================== + * FUNCTIONS + */ + + + +/* ****************************** + * main + */ +int main (int argc, char* argv[]) { + + /* variables */ + + num_t n; + + /* syntax check and input parsing */ + + switch (argc) { + + case 1: { // default N + n = N; + break; } + + case 2: { + std::istringstream iss( argv[1] ); + if (!(iss >> n)) { + std::cerr << "Could not read input (" << argv[1] << ")" + << " as type '" << typeid(n).name() << "'" << std::endl; + return 2; + } + break; } + + default: { // wrong syntax + std::cerr << "Usage: " << argv[0] << " [N]" << std::endl + << " (N: sum upper limit)" << std::endl; + return 1; } + + } // end switch (# input arguments) + + /* create vector of N random integers */ + + // num_vec_t vals (n); + + // std::random_device rd; + // std::default_random_engine rng; + // std::uniform_int_distribution< num_t > rand( 1, MAX_VAL ); + // std::generate( vals.begin(), vals.end(), [&] () { return rand(rng); } ); + + std::cout << "Calculate sum of " << n << " integers" << std::endl; + + /* run various accumulation functions */ + + sum_t const sum_true = + time_accum_func< accum_true >( n, -1, "true solution (std::accumulate)" ); + + time_accum_func< accum_wrong >( n, sum_true, "racy cilk_for (*WRONG*)" ); + + time_accum_func< accum_lock >( n, sum_true, "cilk_for w/ POSIX lock" ); + + time_accum_func< accum_spawn >( n, sum_true, "cilk_spawn reduction" ); + + time_accum_func< accum_reducer >( n, sum_true, "cilk reducer" ); + + time_accum_func< accum_wls >( n, sum_true, "cilk WLS" ); + + /* exit */ + + return 0; + +} // end function "main" + + + +/* ****************************** + * time_accum_func */ +/** + * @brief Run and time accumulation function, and output resulting time to + * stdout. + */ +template +sum_t time_accum_func (num_t n, + sum_t const sum_true, + std::string const desc) { + + std::cout << "..." << desc << "..." << std::endl; + + auto const tstart = chrono_clock::now(); + sum_t const sum = f( n ); + auto const tend = chrono_clock::now(); + + // std::cout << " - sum = " << sum << std::endl; + + if (sum_true != -1) + std::cout << " - " << (sum == sum_true ? "PASS" : "FAIL") << std::endl; + + auto const telapsed = + std::chrono::duration_cast< std::chrono::microseconds >( tend - tstart ); + + std::cout << " - elapsed time: " << telapsed.count() / 1000.0 << " ms" + << std::endl; + + return sum; + +} + + + +/* ****************************** + * accum_true */ +/** + * @brief Calculate sum of vector elements using `std::accumulate`. + */ +sum_t accum_true (num_t n) { + + return static_cast(n * (n-1) / 2); // std::accumulate( vals.cbegin(), vals.cend(), static_cast( 0 ) ); + +} + + + +/* ****************************** + * accum_wrong */ +/** + * @brief Parallel accumulation of vector values using cilk_for, without + * special handling of critical section. + */ +sum_t accum_wrong (num_t n) { + + sum_t sum = 0; + cilk_for (num_t i = 0; i < n; i++) + sum += i; + + return sum; + +} + + + +/* ****************************** + * accum_lock */ +/** + * @brief Parallel accumulation of vector values using cilk_for, with a + * POSIX mutex lock to avoid races. + */ +sum_t accum_lock (num_t n) { + + pthread_mutex_t mutex; + pthread_mutex_init( &mutex, NULL ); + sum_t sum = 0; + cilk_for (num_t i = 0; i < n; i++) { + pthread_mutex_lock( &mutex ); + sum += i; + pthread_mutex_unlock( &mutex ); + } + + return sum; + +} + + + +/* ****************************** + * accum_spawn */ +/** + * @brief Parallel accumulation of vector values using an explicit + * divide-and-conquer reduction via `cilk_spawn`. + */ +sum_t accum_spawn (num_t n) { + + return accum_spawn_helper( 0, n ); + +} + + + +/* ****************************** + * accum_reducer */ +/** + * @brief Parallel accumulation of vector values using a reducer + * hyperobject. + */ +sum_t accum_reducer (num_t n) { + + cilk::reducer_opadd sum(0); + cilk_for (num_t i = 0; i < n; i++) + *sum += i; + + return sum.get_value(); + +} + +/* ****************************** + * accum_wls */ +/** + * @brief Parallel accumulation of vector values using + * worker-local storage. + */ +#ifdef TLS_READ +struct __cilkrts_worker; +extern __thread struct __cilkrts_worker *tls_worker; +#endif +sum_t accum_wls (num_t n) { + sum_t wls_sum[__cilkrts_get_nworkers()]; + sum_t sum = 0; + + for (int i = 0; i < __cilkrts_get_nworkers(); ++i) + wls_sum[i] = 0; + + Cilksan_fake_mutex fake_lock; + // __cilksan_register_lock(&fake_lock); + cilk_for (num_t i = 0; i < n; i++) { +#ifdef TLS_READ + // This approach to getting the Cilk worker ID is not safe in general. + int worker_id = (int) (*(((uint64_t*) tls_worker) + 4)); +#else + int worker_id = __cilkrts_get_worker_number(); +#endif + Cilksan_fake_lock_guard guard(&fake_lock); + // __cilksan_begin_atomic(); + wls_sum[worker_id] += i; + // __cilksan_end_atomic(); + } + // __cilksan_unregister_lock(&fake_lock); + + for (int i = 0; i < __cilkrts_get_nworkers(); ++i) + sum += wls_sum[i]; + + return sum; +} diff --git a/test/cilksan/sum-vector-int.c b/test/cilksan/sum-vector-int.c new file mode 100644 index 0000000..2d4a0f1 --- /dev/null +++ b/test/cilksan/sum-vector-int.c @@ -0,0 +1,275 @@ +/** -*- C -*- + * + * \file sum-vector-int.c + * + * @brief Sum-accumulation of integer vector values. + * + * @author Alexandros-Stavros Iliopoulos (ailiop@mit.edu) + * + */ + + + +/* ================================================== + * INCLUDE DEPENDENCIES + */ + + +#include +#include +#include +#include + +#include + +#include +#include + +/* ================================================== + * ALIASES + */ + + +typedef long int num_t; +typedef long long int sum_t; + + + +/* ================================================== + * CONSTANTS + */ + + +#define N (10 * 1000 * 1000) + +#define MAX_VAL 100 + + + +/* ================================================== + * FORWARD DECLARATIONS + */ + + +sum_t accum_true (num_t const *, num_t const); +sum_t accum_wrong (num_t const *, num_t const); +sum_t accum_lock (num_t const *, num_t const); +sum_t accum_spawn (num_t const *, num_t const); +sum_t accum_wls (num_t const *, num_t const); + +void run_accum (sum_t (*) (num_t const *, num_t const), + num_t const *, num_t const, sum_t const, char const *); + + + +/* ================================================== + * FUNCTIONS + */ + + + +/* ****************************** + * main + */ +int main (int argc, char* argv[]) { + + /* variables */ + + num_t n; + num_t * vals; + + /* syntax check and input parsing */ + + switch (argc) { + + case 1: // default N + n = N; + break; + + case 2: // user-specified N + n = atoi( argv[1] ); + break; + + default: + fprintf( stderr, "Usage: %s [N]\n", argv[0] ); + return 1; + + } // end switch (# input arguments) + + /* create vector of N integers */ + + vals = (num_t*) malloc( n * sizeof(num_t) ); + if (!vals) { + fprintf( stderr, "Could not allocate memory for 'vals'" ); + return 2; + } + + for (num_t i = 0; i < n; i++) + vals[i] = (rand() % MAX_VAL) + 1; + + printf( "Calculate sum of %ld integers\n", n ); + + /* run accumulation functions */ + + wsp_t start = wsp_getworkspan(); + sum_t const sum_true = accum_true( vals, n ); + wsp_t last = wsp_getworkspan(); + wsp_dump(wsp_sub(last, start), "sum_true"); + + run_accum( accum_wrong, vals, n, sum_true, "wrong" ); + + run_accum( accum_lock , vals, n, sum_true, "lock" ); + + run_accum( accum_spawn, vals, n, sum_true, "spawn" ); + + run_accum( accum_wls, vals, n, sum_true, "wls" ); + + /* exit */ + + free( vals ); + return 0; + +} + + + +/* ****************************** + * run_accum */ +/** + * @brief Run and time accumulation function, and output resulting time to + * stdout. + */ +void run_accum (sum_t (*f) (num_t const *, num_t const), + num_t const * vals, num_t const n, + sum_t const sum_true, char const * desc) { + + printf( "...%s...\n", desc ); + + wsp_t start = wsp_getworkspan(); + sum_t const sum = (*f)( vals, n ); + wsp_t end = wsp_getworkspan(); + wsp_dump(wsp_sub(end, start), desc); + + printf( " - %s\n", (sum == sum_true ? "PASS" : "FAIL") ); + + return; + +} + + + +/* ****************************** + * accum_true */ +/** + * @brief Calculate sum of vector elements using `std::accumulate`. + */ +sum_t accum_true (num_t const * vals, num_t const n) { + + sum_t sum = 0; + for (num_t i = 0; i < n; i++) + sum += vals[i]; + + return sum; + +} + + + +/* ****************************** + * accum_wrong */ +/** + * @brief Parallel accumulation of vector values using cilk_for, without + * special handling of critical section. + */ +sum_t accum_wrong (num_t const * vals, num_t const n) { + + sum_t sum = 0; + cilk_for (num_t i = 0; i < n; i++) + sum += vals[i]; + + return sum; + +} + + + +/* ****************************** + * accum_lock */ +/** + * @brief Parallel accumulation of vector values using cilk_for, with a + * POSIX mutex lock to avoid races. + */ +sum_t accum_lock (num_t const * vals, num_t const n) { + + pthread_mutex_t mutex; + pthread_mutex_init( &mutex, NULL ); + + sum_t sum = 0; + cilk_for (num_t i = 0; i < n; i++) { + pthread_mutex_lock( &mutex ); + sum += vals[i]; + pthread_mutex_unlock( &mutex ); + } + + return sum; + +} + + + +/* ****************************** + * accum_spawn */ +/** + * @brief Parallel accumulation of vector values using an explicit + * divide-and-conquer reduction via `cilk_spawn`. + */ +sum_t accum_spawn (num_t const * vals, num_t const n) { + + switch (n) { + + case 1: + return *vals; + + case 2: + return (*vals + *(vals+1)); + + default: { + num_t const nhalf = n/2 + 1; + sum_t const s1 = cilk_spawn accum_spawn( vals , nhalf ); + sum_t const s2 = cilk_spawn accum_spawn( vals + nhalf, n - nhalf ); + cilk_sync; + return (s1 + s2); } + + } // end switch (n) + +} + + + +/* ****************************** + * accum_wls */ +/** + * @brief Parallel accumulation of vector values using + * worker-local storage. + */ +sum_t accum_wls (num_t const * vals, num_t const n) { + sum_t wls_sum[__cilkrts_get_nworkers()]; + sum_t sum = 0; + + for (int i = 0; i < __cilkrts_get_nworkers(); ++i) + wls_sum[i] = 0; + + Cilksan_fake_mutex fake_lock; + __cilksan_register_lock_explicit(&fake_lock); + cilk_for (num_t i = 0; i < n; i++) { + __cilksan_acquire_lock(&fake_lock); + wls_sum[__cilkrts_get_worker_number()] += vals[i]; + __cilksan_release_lock(&fake_lock); + } + __cilksan_unregister_lock_explicit(&fake_lock); + + for (int i = 0; i < __cilkrts_get_nworkers(); ++i) + sum += wls_sum[i]; + + return sum; +} diff --git a/test/cilksan/sum-vector-int.cpp b/test/cilksan/sum-vector-int.cpp new file mode 100644 index 0000000..86c72e9 --- /dev/null +++ b/test/cilksan/sum-vector-int.cpp @@ -0,0 +1,335 @@ +/** -*- C++ -*- + * + * \file sum-vector-int.cpp + * + * @brief Sum-accumulation of integer vector values. + * + * @author Alexandros-Stavros Iliopoulos (ailiop@mit.edu) + * + */ + + + +/* ================================================== + * INCLUDE DEPENDENCIES + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef WLS +#include +#endif + + +/* ================================================== + * ALIASES + */ + + +using num_t = long int; +using sum_t = long long int; + +using num_vec_t = std::vector; + +using chrono_clock = std::chrono::high_resolution_clock; + +using accum_func_t = sum_t (*) (num_vec_t const &); + + + +/* ================================================== + * CONSTANTS + */ + + +constexpr num_t N = 10 * 1000 * 1000; + +constexpr num_t MAX_VAL = 100; + + + +/* ================================================== + * FORWARD DECLARATIONS + */ + + +sum_t accum_true (num_vec_t const &); +sum_t accum_wrong (num_vec_t const &); +sum_t accum_lock (num_vec_t const &); +sum_t accum_spawn (num_vec_t const &); +sum_t accum_reducer (num_vec_t const &); +#ifdef WLS +sum_t accum_wls (num_vec_t const &); +#endif + +template +sum_t time_accum_func (num_vec_t const &, + sum_t const = -1, + std::string const = "accumulation function (no description)"); + + + +/* ================================================== + * ANONYMOUS NAMESPACE (PRIVATE FUNCTIONS) + */ + + +namespace { + + sum_t accum_spawn_helper (num_vec_t::const_iterator itBegin, + num_vec_t::const_iterator itEnd) { + + auto const n = std::distance( itBegin, itEnd ); + + switch (n) { + + case 1: + return *itBegin; + + case 2: + return (*itBegin + *(itBegin+1)); + + default: + auto const nhalf = n/2 + 1; + auto const s1 = cilk_spawn accum_spawn_helper( itBegin, itBegin + nhalf ); + auto const s2 = cilk_spawn accum_spawn_helper( itBegin + nhalf, itEnd ); + cilk_sync; + return (s1 + s2); + + } // end switch (n) + + } // end function "accum_spawn_helper" + +} // end anonymous namespace + + + +/* ================================================== + * FUNCTIONS + */ + + + +/* ****************************** + * main + */ +int main (int argc, char* argv[]) { + + /* variables */ + + num_t n; + + /* syntax check and input parsing */ + + switch (argc) { + + case 1: { // default N + n = N; + break; } + + case 2: { // user-specified N + std::istringstream iss( argv[1] ); + if (!(iss >> n)) { + std::cerr << "Could not read input (" << argv[1] << ")" + << " as type '" << typeid(n).name() << "'" << std::endl; + return 2; + } + break; } + + default: { // wrong syntax + std::cerr << "Usage: sum-vector-int [N]" << std::endl + << " (N: sum upper limit)" << std::endl; + return 1; } + + } // end switch (# input arguments) + + /* create vector of N random integers */ + + num_vec_t vals (n); + + // std::random_device rd; + std::default_random_engine rng; + std::uniform_int_distribution< num_t > rand( 1, MAX_VAL ); + std::generate( vals.begin(), vals.end(), [&] () { return rand(rng); } ); + + std::cout << "Calculate sum of " << n << " integers" << std::endl; + + /* run various accumulation functions */ + + sum_t const sum_true = + time_accum_func< accum_true >( vals, -1, "true solution (std::accumulate)" ); + + time_accum_func< accum_wrong >( vals, sum_true, "racy cilk_for (*WRONG*)" ); + + time_accum_func< accum_lock >( vals, sum_true, "cilk_for w/ POSIX lock" ); + + time_accum_func< accum_spawn >( vals, sum_true, "cilk_spawn reduction" ); + + time_accum_func< accum_reducer >( vals, sum_true, "cilk reducer" ); + +#ifdef WLS + time_accum_func< accum_wls >( vals, sum_true, "cilk WLS" ); +#endif + /* exit */ + + return 0; + +} // end function "main" + + + +/* ****************************** + * time_accum_func */ +/** + * @brief Run and time accumulation function, and output resulting time to + * stdout. + */ +template +sum_t time_accum_func (num_vec_t const & vals, + sum_t const sum_true, + std::string const desc) { + + std::cout << "..." << desc << "..." << std::endl; + + auto const tstart = chrono_clock::now(); + sum_t const sum = f( vals ); + auto const tend = chrono_clock::now(); + + // std::cout << " - sum = " << sum << std::endl; + + if (sum_true != -1) + std::cout << " - " << (sum == sum_true ? "PASS" : "FAIL") << std::endl; + + auto const telapsed = + std::chrono::duration_cast< std::chrono::microseconds >( tend - tstart ); + + std::cout << " - elapsed time: " << telapsed.count() / 1000.0 << " ms" + << std::endl; + + return sum; + +} + + + +/* ****************************** + * accum_true */ +/** + * @brief Calculate sum of vector elements using `std::accumulate`. + */ +sum_t accum_true (num_vec_t const & vals) { + + return std::accumulate( vals.cbegin(), vals.cend(), static_cast( 0 ) ); + +} + + + +/* ****************************** + * accum_wrong */ +/** + * @brief Parallel accumulation of vector values using cilk_for, without + * special handling of critical section. + */ +sum_t accum_wrong (num_vec_t const & vals) { + + sum_t sum = 0; + cilk_for (auto i = 0; i < vals.size(); i++) + sum += vals[i]; + + return sum; + +} + + + +/* ****************************** + * accum_lock */ +/** + * @brief Parallel accumulation of vector values using cilk_for, with a + * POSIX mutex lock to avoid races. + */ +sum_t accum_lock (num_vec_t const & vals) { + + pthread_mutex_t mutex; + pthread_mutex_init( &mutex, NULL ); + sum_t sum = 0; + cilk_for (auto i = 0; i < vals.size(); i++) { + pthread_mutex_lock( &mutex ); + sum += vals[i]; + pthread_mutex_unlock( &mutex ); + } + + return sum; + +} + + + +/* ****************************** + * accum_spawn */ +/** + * @brief Parallel accumulation of vector values using an explicit + * divide-and-conquer reduction via `cilk_spawn`. + */ +sum_t accum_spawn (num_vec_t const & vals) { + + return accum_spawn_helper( vals.cbegin(), vals.cend() ); + +} + + + +/* ****************************** + * accum_reducer */ +/** + * @brief Parallel accumulation of vector values using a reducer + * hyperobject. + */ +sum_t accum_reducer (num_vec_t const & vals) { + + cilk::reducer_opadd sum(0); + cilk_for (auto i = 0; i < vals.size(); i++) + *sum += vals[i]; + + return sum.get_value(); + +} + +#ifdef WLS +/* ****************************** + * accum_wls */ +/** + * @brief Parallel accumulation of vector values using + * worker-local storage. + */ +sum_t accum_wls (num_vec_t const & vals) { + sum_t wls_sum[__cilkrts_get_nworkers()]; + sum_t sum = 0; + + for (int i = 0; i < __cilkrts_get_nworkers(); ++i) + wls_sum[i] = 0; + + cilk_for (auto i = 0; i < vals.size(); i++) + wls_sum[__cilkrts_get_worker_number()] += vals[i]; + + for (int i = 0; i < __cilkrts_get_nworkers(); ++i) + sum += wls_sum[i]; + + return sum; +} +#endif diff --git a/test/cilksan/syncregs.c b/test/cilksan/syncregs.c new file mode 100644 index 0000000..d1a432c --- /dev/null +++ b/test/cilksan/syncregs.c @@ -0,0 +1,30 @@ +#include +#include +#include + +int globl = 0; +void bar() { + // Location of write-write race + globl++; +} + +void foo(int n) { + int sum = 0; + cilk_spawn bar(); + // Write-write race + cilk_for(int i = 0; i < n; ++i) + sum += i; + bar(); + cilk_sync; +} + +int main(int argc, char *argv[]) { + int n = 4096; + if (argc > 1) + n = atoi(argv[1]); + /* cilk_spawn printf("hi\n");; */ + foo(n); + /* cilk_sync; */ + printf("globl = %d\n", globl); + return 0; +} diff --git a/test/cilksan/vla.c b/test/cilksan/vla.c new file mode 100644 index 0000000..9f5f204 --- /dev/null +++ b/test/cilksan/vla.c @@ -0,0 +1,60 @@ +#include +#include +#include +#include +#include + +void fill(unsigned char *data, unsigned length, unsigned char seed) +{ + for (unsigned i = 0; i < length; ++i) + data[i] = seed + i; +} + +unsigned check(unsigned char *data, unsigned length, unsigned char seed) +{ + fprintf(stdout, "check %p seed %d\n", data, seed); + for (unsigned i = 0; i < length; ++i) + if (data[i] != (unsigned char)(seed + i)) + { + fprintf(stdout, "check %p seed %d [%u] %d != %d\n", data, seed, i, + data[i], (unsigned char)(seed + i)); + return 1; + } + return 0; +} + +unsigned work(unsigned char *data, unsigned length, unsigned char seed) +{ + fprintf(stdout, "work %p seed %d\n", data, seed); + fflush(stdout); + fill(data, length, seed); + usleep(100); + return check(data, length, seed); +} + +unsigned loop(unsigned length) +{ + unsigned errors[length]; + memset(errors, 0, sizeof errors); + for (unsigned int i = 0; i < 100; ++i) + { + unsigned char vla[length]; + cilk_spawn errors[i] = work(vla, length, i); + } + cilk_sync; + unsigned sum = 0; + for (unsigned int i = 0; i < 100; ++i) + sum += errors[i]; + return sum; +} + +int main(int argc, char *argv[]) +{ + unsigned length = 0; + if (argc > 1) + length = strtoul(argv[1], 0, 0); + if (length == 0) + length = 399; + unsigned errors = loop(length); + return errors != 0; +}