Skip to content

Commit

Permalink
Make kExpected file-specific, rewrite float tests to avoid epsilon.
Browse files Browse the repository at this point in the history
  • Loading branch information
Logikable committed Feb 7, 2024
1 parent 46300e0 commit dd84b71
Show file tree
Hide file tree
Showing 11 changed files with 122 additions and 61 deletions.
12 changes: 8 additions & 4 deletions SingleSource/UnitTests/Atomic/big_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@

#include "util.h"

// V >> 56 = 66, so to prevent 32-bit overflow, kExpected must be less than
// 2^31 / 66 = 32 x 10^6.
#ifdef SMALL_PROBLEM_SIZE
static constexpr int kIterations = 1'000'000;
#else
static constexpr int kIterations = 3'000'000;
#endif
static constexpr int kExpected = kThreads * kIterations;
static constexpr int kBigSize = 10;
struct big_t {
int v[kBigSize];
Expand All @@ -44,7 +52,6 @@ void looper_big_cmpxchg(big_t *abig, int success_model, int fail_model) {

void test_big_cmpxchg() {
std::vector<std::thread> pool;

for (int success_model : atomic_compare_exchange_models) {
for (int fail_model : atomic_compare_exchange_models) {
big_t abig = {};
Expand All @@ -66,9 +73,6 @@ void test_big() {
}

int main() {
std::cout << kThreads << " threads; "
<< kIterations << " iterations each; total of "
<< kExpected << "\n";
test_big();
std::cout << "PASSED\n";
}
1 change: 0 additions & 1 deletion SingleSource/UnitTests/Atomic/big_test.reference_output
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
10 threads; 1000000 iterations each; total of 10000000
Testing big
PASSED
exit 0
58 changes: 38 additions & 20 deletions SingleSource/UnitTests/Atomic/float_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
// This file tests atomic operations on floating point types with aligned
// memory addresses.
//
// The types tested are: float, double.
// The types tested are: float, double, float128.
// The ops tested are: xchg, cmpxchg.
//
// Please read the README before contributing.
Expand All @@ -22,47 +22,68 @@
#include <thread>
#include <vector>

#include "numeric.h"
#include "util.h"

// See numeric.h for an explanation of numeric xchg tests.
// There are 23-bits in the mantissa of a single-precision float.
// Therefore, kExpected cannot exceed 2^24.
static constexpr int kIterations = 1'500'000;
static constexpr int kExpected = kThreads * kIterations;

// See int_aligned_test.cc for an explanation of xchg tests.
template <typename T>
void looper_float_scalar_xchg(T *afloat, int model) {
__int128_t error = 0;
T next = *afloat + 1;
T result;
for (int n = 0; n < kIterations; ++n) {
__atomic_exchange(afloat, &next, &result, model);
error +=
static_cast<__int128_t>(next) - static_cast<__int128_t>(result + 1);
next = result + 1;
}
__atomic_fetch_sub(afloat, static_cast<T>(error), model);
}

template <typename T>
void test_float_scalar_xchg() {
static constexpr T val = V >> right_shift<T>();
static constexpr T expected = val * kExpected;
std::vector<std::thread> pool;

for (int model : atomic_exchange_models) {
T afloat = 0;
for (int n = 0; n < kThreads; ++n)
pool.emplace_back(looper_numeric_xchg_atomic<T>, &afloat, model);
pool.emplace_back(looper_float_scalar_xchg<T>, &afloat, model);
for (int n = 0; n < kThreads; ++n)
pool[n].join();
pool.clear();
if (afloat < expected * (1 - kEpsilon) ||
afloat > expected * (1 + kEpsilon))
if (afloat != kExpected)
fail();
}
}

// See numeric.h for an explanation of numeric cmpxchg tests.
// See int_aligned_test.cc for an explanation of cmpxchg tests.
template <typename T>
void looper_float_scalar_cmpxchg(T *afloat, int success_model, int fail_model) {
for (int n = 0; n < kIterations; ++n) {
T desired, expected = 0;
do {
desired = expected + 1;
} while (!__atomic_compare_exchange(afloat, &expected, &desired, true,
success_model, fail_model));
}
}

template <typename T>
void test_float_scalar_cmpxchg() {
static constexpr T val = V >> right_shift<T>();
static constexpr T expected = val * kExpected;
std::vector<std::thread> pool;

for (int success_model : atomic_compare_exchange_models) {
for (int fail_model : atomic_compare_exchange_models) {
T afloat = 0;
for (int n = 0; n < kThreads; ++n)
pool.emplace_back(looper_numeric_cmpxchg<T>, &afloat, success_model,
fail_model);
pool.emplace_back(looper_float_scalar_cmpxchg<T>, &afloat,
success_model, fail_model);
for (int n = 0; n < kThreads; ++n)
pool[n].join();
pool.clear();
if (afloat < expected * (1 - kEpsilon) ||
afloat > expected * (1 + kEpsilon))
if (afloat != kExpected)
fail();
}
}
Expand All @@ -83,9 +104,6 @@ void test_floating_point() {
}

int main() {
std::cout << kThreads << " threads; "
<< kIterations << " iterations each; total of "
<< kExpected << "\n";
test_floating_point();
std::cout << "PASSED\n";
}
1 change: 0 additions & 1 deletion SingleSource/UnitTests/Atomic/float_test.reference_output
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
10 threads; 1000000 iterations each; total of 10000000
Testing float
Testing double
Testing float128
Expand Down
58 changes: 51 additions & 7 deletions SingleSource/UnitTests/Atomic/int_aligned_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,17 @@
#include <thread>
#include <vector>

#include "numeric.h"
#include "util.h"

// V >> 56 = 66, so to prevent 32-bit overflow, kExpected must be less than
// 2^31 / 66 = 32 x 10^6.
#ifdef SMALL_PROBLEM_SIZE
static constexpr int kIterations = 1'000'000;
#else
static constexpr int kIterations = 3'000'000;
#endif
static constexpr int kExpected = kThreads * kIterations;

template <typename T>
void looper_int_fetch_add(T *aint, int model) {
static constexpr T val = V >> right_shift<T>();
Expand Down Expand Up @@ -167,14 +175,41 @@ void test_int_fetch_xor() {
}
}

// The xchg tests work as follows:
//
// Each thread increments a local copy of the shared variable, and exchanges it
// with the shared value. Most of the time, the value moved from shared -> local
// is one less than the value moved from local -> shared. Other times, the
// difference is much bigger (or smaller). When this occurs, the thread
// accumulates the difference in a local error variable. Upon completion, the
// thread subtracts the error from the shared value, all at once.
//
// Like many tests, this test increments by more than 1 -- specifically, a
// number that scales with the width of the type is picked.
//
template <typename T>
void looper_int_xchg(T *aint, int model) {
static constexpr T val = V >> right_shift<T>();
__int128_t error = 0;
T next = *aint + val;
T result;
for (int n = 0; n < kIterations; ++n) {
__atomic_exchange(aint, &next, &result, model);
error +=
static_cast<__int128_t>(next) - static_cast<__int128_t>(result + val);
next = result + val;
}
__atomic_fetch_sub(aint, static_cast<T>(error), model);
}

template <typename T>
void test_int_xchg() {
static constexpr T val = V >> right_shift<T>();
std::vector<std::thread> pool;
for (int model : atomic_exchange_models) {
T aint = 0;
for (int n = 0; n < kThreads; ++n)
pool.emplace_back(looper_numeric_xchg_atomic<T>, &aint, model);
pool.emplace_back(looper_int_xchg<T>, &aint, model);
for (int n = 0; n < kThreads; ++n)
pool[n].join();
pool.clear();
Expand All @@ -183,7 +218,6 @@ void test_int_xchg() {
}
}

// See numeric.h for an explanation of numeric xchg tests.
template <typename T>
void looper_int_xchg_n(T *aint, int model) {
static constexpr T val = V >> right_shift<T>();
Expand Down Expand Up @@ -213,6 +247,19 @@ void test_int_xchg_n() {
}
}

// The cmpxchg tests act similar to fetch_add tests.
template <typename T>
void looper_int_cmpxchg(T *aint, int success_model, int fail_model) {
static constexpr T val = V >> right_shift<T>();
for (int n = 0; n < kIterations; ++n) {
T desired, expected = 0;
do {
desired = expected + val;
} while (!__atomic_compare_exchange(aint, &expected, &desired, true,
success_model, fail_model));
}
}

template <typename T>
void test_int_cmpxchg() {
static constexpr T val = V >> right_shift<T>();
Expand All @@ -221,7 +268,7 @@ void test_int_cmpxchg() {
for (int fail_model : atomic_compare_exchange_models) {
T aint = 0;
for (int n = 0; n < kThreads; ++n)
pool.emplace_back(looper_numeric_cmpxchg<T>, &aint, success_model,
pool.emplace_back(looper_int_cmpxchg<T>, &aint, success_model,
fail_model);
for (int n = 0; n < kThreads; ++n) pool[n].join();
pool.clear();
Expand All @@ -231,7 +278,6 @@ void test_int_cmpxchg() {
}
}

// See numeric.h for an explanation of numeric cmpxchg tests.
template <typename T>
void looper_int_cmpxchg_n(T *aint, int success_model, int fail_model) {
static constexpr T val = V >> right_shift<T>();
Expand Down Expand Up @@ -298,8 +344,6 @@ void test_aligned_int() {
}

int main() {
std::cout << kThreads << " threads; " << kIterations
<< " iterations each; total of " << kExpected << "\n";
test_aligned_int();
std::cout << "PASSED\n";
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
10 threads; 1000000 iterations each; total of 10000000
Testing aligned uint32_t
Testing aligned uint64_t
Testing aligned int32_t
Expand Down
21 changes: 13 additions & 8 deletions SingleSource/UnitTests/Atomic/int_misaligned_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@

#include "util.h"

#ifdef SMALL_PROBLEM_SIZE
static constexpr int kIterations = 50'000;
#else
static constexpr int kIterations = 500'000;
#endif
static constexpr int kExpected = kThreads * kIterations;

template <typename T>
struct __attribute__((packed)) misaligned {
char byte;
Expand Down Expand Up @@ -179,9 +186,9 @@ void test_int_misaligned_fetch_xor() {
}
}

// See numeric.h for an explanation of numeric xchg tests.
// See int_aligned_test.cc for an explanation of xchg tests.
template <typename T>
void looper_int_misaligned_xchg_atomic(misaligned<T> &astruct, int model) {
void looper_int_misaligned_xchg(misaligned<T> &astruct, int model) {
static constexpr T val = V >> right_shift<T>();
__int128_t error = 0;
T next = astruct.data + val;
Expand All @@ -203,7 +210,7 @@ void test_int_misaligned_xchg() {
for (int model : atomic_exchange_models) {
astruct.data = 0;
for (int n = 0; n < kThreads; ++n)
pool.emplace_back(looper_int_misaligned_xchg_atomic<T>, std::ref(astruct),
pool.emplace_back(looper_int_misaligned_xchg<T>, std::ref(astruct),
model);
for (int n = 0; n < kThreads; ++n)
pool[n].join();
Expand All @@ -213,7 +220,7 @@ void test_int_misaligned_xchg() {
}
}

// See numeric.h for an explanation of numeric xchg tests.
// See int_aligned_test.cc for an explanation of xchg tests.
template <typename T>
void looper_int_misaligned_xchg_n(misaligned<T> &astruct, int model) {
static constexpr T val = V >> right_shift<T>();
Expand Down Expand Up @@ -246,7 +253,7 @@ void test_int_misaligned_xchg_n() {
}
}

// See numeric.h for an explanation of numeric cmpxchg tests.
// See int_aligned_test.cc for an explanation of cmpxchg tests.
template <typename T>
void looper_int_misaligned_cmpxchg(misaligned<T> &astruct, int success_model,
int fail_model) {
Expand Down Expand Up @@ -280,7 +287,7 @@ void test_int_misaligned_cmpxchg() {
}
}

// See numeric.h for an explanation of numeric cmpxchg tests.
// See int_aligned_test.cc for an explanation of cmpxchg tests.
template <typename T>
void looper_int_misaligned_cmpxchg_n(misaligned<T> &astruct, int success_model,
int fail_model) {
Expand Down Expand Up @@ -350,8 +357,6 @@ void test_misaligned_int() {
}

int main() {
std::cout << kThreads << " threads; " << kIterations
<< " iterations each; total of " << kExpected << "\n";
test_misaligned_int();
std::cout << "PASSED\n";
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
10 threads; 1000000 iterations each; total of 10000000
Testing misaligned uint32_t
Testing misaligned uint64_t
Testing misaligned int32_t
Expand Down
Loading

0 comments on commit dd84b71

Please sign in to comment.