From 5ed9ecedf6c1a614513c1157d0a41396f91bb7c0 Mon Sep 17 00:00:00 2001
From: Matthieu Schaller <schaller@strw.leidenuniv.nl>
Date: Thu, 2 Apr 2020 14:04:39 +0200
Subject: [PATCH] Added a simple unit test checking the correctness of the
 atomic implementation

---
 .gitignore         |   1 +
 tests/Makefile.am  |   8 ++-
 tests/testAtomic.c | 146 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 153 insertions(+), 2 deletions(-)
 create mode 100644 tests/testAtomic.c

diff --git a/.gitignore b/.gitignore
index f843002a1c..99aa018c9d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -122,6 +122,7 @@ tests/testInteractions.sh
 tests/testSymmetry
 tests/testHydroMPIrules
 tests/testMaths
+tests/testAtomic
 tests/testRandom
 tests/testRandomSpacing
 tests/testThreadpool
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 11426ac899..e24a2a69b0 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -29,7 +29,8 @@ TESTS = testGreetings testMaths testReading.sh testKernel \
 	testPeriodicBC.sh testPeriodicBCPerturbed.sh testPotentialSelf \
 	testPotentialPair testEOS testUtilities testSelectOutput.sh \
 	testCbrt testCosmology testOutputList testFormat.sh \
-	test27cellsStars.sh test27cellsStarsPerturbed.sh testHydroMPIrules
+	test27cellsStars.sh test27cellsStarsPerturbed.sh testHydroMPIrules \
+        testAtomic
 
 # List of test programs to compile
 check_PROGRAMS = testGreetings testReading testTimeIntegration \
@@ -41,7 +42,8 @@ check_PROGRAMS = testGreetings testReading testTimeIntegration \
 		 testVoronoi1D testVoronoi2D testVoronoi3D testPeriodicBC \
 		 testGravityDerivatives testPotentialSelf testPotentialPair testEOS testUtilities \
 		 testSelectOutput testCbrt testCosmology testOutputList test27cellsStars \
-		 test27cellsStars_subset testCooling testComovingCooling testFeedback testHashmap testHydroMPIrules
+		 test27cellsStars_subset testCooling testComovingCooling testFeedback testHashmap \
+                 testAtomic testHydroMPIrules
 
 # Rebuild tests when SWIFT is updated.
 $(check_PROGRAMS): ../src/.libs/libswiftsim.a
@@ -51,6 +53,8 @@ testGreetings_SOURCES = testGreetings.c
 
 testMaths_SOURCES = testMaths.c
 
+testAtomic_SOURCES = testAtomic.c
+
 testRandom_SOURCES = testRandom.c
 
 testRandomSpacing_SOURCES = testRandomSpacing.c
diff --git a/tests/testAtomic.c b/tests/testAtomic.c
new file mode 100644
index 0000000000..47eca7b736
--- /dev/null
+++ b/tests/testAtomic.c
@@ -0,0 +1,146 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (C) 2020 Matthieu Schaller (schaller@strw.leidenuniv.nl)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+
+/* Config parameters. */
+#include "../config.h"
+
+/* Standard includes. */
+#include <fenv.h>
+
+/* Local includes */
+#include "swift.h"
+
+const int array_size = 2048 * 2048;
+const int num_threads = 64;
+const int chunk_size = 64;
+
+void map_function_sum_f(void *data, int num_elements, void *extra_data) {
+
+  float *array = (float *)data;
+  float *sum = (float *)extra_data;
+
+  for (int i = 0; i < num_elements; ++i) atomic_add_f(sum, array[i]);
+}
+
+void map_function_sum_ll(void *data, int num_elements, void *extra_data) {
+
+  long long *array = (long long *)data;
+  long long *sum = (long long *)extra_data;
+
+  for (int i = 0; i < num_elements; ++i) atomic_add(sum, array[i]);
+}
+
+void map_function_inc_ll(void *data, int num_elements, void *extra_data) {
+
+  long long *sum = (long long *)extra_data;
+
+  for (int i = 0; i < num_elements; ++i) atomic_inc(sum);
+}
+
+int main(int argc, char *argv[]) {
+
+  /* Initialize CPU frequency, this also starts time. */
+  unsigned long long cpufreq = 0;
+  clocks_set_cpufreq(cpufreq);
+
+/* Choke on FPEs */
+#ifdef HAVE_FE_ENABLE_EXCEPT
+  feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW);
+#endif
+
+  /* Get some randomness going */
+  const int seed = time(NULL);
+  message("Seed = %d", seed);
+  srand(seed);
+
+  /* Start a bunch of threads */
+  printf("# Creating threadpool with %d threads\n", num_threads);
+  struct threadpool tp;
+  threadpool_init(&tp, num_threads);
+
+  /* Create some random data */
+  float *array_f = malloc(array_size * sizeof(float));
+  long long *array_ll = malloc(array_size * sizeof(long long));
+
+  for (int i = 0; i < array_size; ++i) {
+    array_f[i] = rand() / ((float)RAND_MAX);
+    array_ll[i] = rand();
+  }
+
+  /*** Test the addition atomic ops *******************************/
+
+  /* float case */
+
+  /* Compute the real answer */
+  float real_sum_f = 0.f;
+  for (int i = 0; i < array_size; ++i) {
+    real_sum_f += array_f[i];
+  }
+
+  /* Compute the answer via threads and atomic */
+  float atomic_sum_f = 0.f;
+  threadpool_map(&tp, map_function_sum_f, array_f, array_size, sizeof(float),
+                 chunk_size, &atomic_sum_f);
+
+  const double diff_sum_f = (double)real_sum_f - (double)atomic_sum_f;
+  const double sum_sum_f = (double)real_sum_f + (double)atomic_sum_f;
+  const double rel_sum_f = 0.5 * fabs(diff_sum_f) / sum_sum_f;
+  message("Real sum = %.7e -- atomic sum = %.7e rel=%e", real_sum_f,
+          atomic_sum_f, rel_sum_f);
+
+  /* long long case */
+
+  /* Compute the real answer */
+  long long real_sum_ll = 0.f;
+  for (int i = 0; i < array_size; ++i) {
+    real_sum_ll += array_ll[i];
+  }
+
+  /* Compute the answer via threads and atomic */
+  long long atomic_sum_ll = 0LL;
+  threadpool_map(&tp, map_function_sum_ll, array_ll, array_size,
+                 sizeof(long long), chunk_size, &atomic_sum_ll);
+
+  const double diff_sum_ll = (double)real_sum_ll - (double)atomic_sum_ll;
+  const double sum_sum_ll = (double)real_sum_ll + (double)atomic_sum_ll;
+  const double rel_sum_ll = 0.5 * fabs(diff_sum_ll) / sum_sum_ll;
+  message("Real sum = %lld -- atomic sum = %lld rel=%e", real_sum_ll,
+          atomic_sum_ll, rel_sum_ll);
+
+  /*** Test the inc atomic ops *******************************/
+
+  long long real_inc_ll = array_size;
+
+  /* Compute the answer via threads and atomic */
+  long long atomic_inc_ll = 0LL;
+  threadpool_map(&tp, map_function_inc_ll, array_ll, array_size,
+                 sizeof(long long), chunk_size, &atomic_inc_ll);
+
+  const double diff_inc_ll = (double)real_inc_ll - (double)atomic_inc_ll;
+  const double sum_inc_ll = (double)real_inc_ll + (double)atomic_inc_ll;
+  const double rel_inc_ll = 0.5 * fabs(diff_inc_ll) / sum_inc_ll;
+  message("Real inc = %lld -- atomic inc = %lld rel=%e", real_inc_ll,
+          atomic_inc_ll, rel_inc_ll);
+
+  /* Be clean */
+  threadpool_clean(&tp);
+  free(array_f);
+  free(array_ll);
+  return 0;
+}
-- 
GitLab