spin_test.cc (4176B)
1 // Copyright 2025 Google LLC 2 // SPDX-License-Identifier: Apache-2.0 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 #include "hwy/contrib/thread_pool/spin.h" 17 18 #include <stddef.h> 19 #include <stdint.h> 20 #include <stdio.h> 21 22 #include <atomic> 23 24 #include "hwy/aligned_allocator.h" // HWY_ALIGNMENT 25 #include "hwy/contrib/thread_pool/futex.h" // NanoSleep 26 #include "hwy/contrib/thread_pool/thread_pool.h" 27 #include "hwy/contrib/thread_pool/topology.h" 28 #include "hwy/tests/hwy_gtest.h" 29 #include "hwy/tests/test_util-inl.h" 30 #include "hwy/timer.h" 31 32 namespace hwy { 33 namespace { 34 35 struct TestPingPongT { 36 template <class Spin> 37 void operator()(const Spin& spin) const { 38 constexpr size_t kU32PerLine = HWY_ALIGNMENT / 4; 39 constexpr size_t kF64PerLine = HWY_ALIGNMENT / 8; 40 alignas(HWY_ALIGNMENT) std::atomic<uint32_t> thread_active[kU32PerLine]; 41 alignas(HWY_ALIGNMENT) std::atomic<uint32_t> thread_done[kU32PerLine]; 42 43 thread_active[0].store(0, std::memory_order_release); 44 thread_done[0].store(0, std::memory_order_release); 45 hwy::ThreadPool pool(1); 46 HWY_ASSERT(pool.NumWorkers() == 2); 47 48 const double t0 = hwy::platform::Now(); 49 std::atomic_flag error = ATOMIC_FLAG_INIT; 50 51 alignas(HWY_ALIGNMENT) std::atomic<size_t> reps1; 52 alignas(HWY_ALIGNMENT) std::atomic<size_t> reps2; 53 54 alignas(HWY_ALIGNMENT) std::atomic<double> before_thread_done[kF64PerLine]; 55 alignas(HWY_ALIGNMENT) std::atomic<double> before_thread_go[kF64PerLine]; 56 alignas(HWY_ALIGNMENT) std::atomic<double> ack_thread_done[kF64PerLine]; 57 alignas(HWY_ALIGNMENT) std::atomic<double> ack_thread_release[kF64PerLine]; 58 59 const auto kAcq = std::memory_order_acquire; 60 const auto kRel = std::memory_order_release; 61 pool.Run(0, 2, [&](uint64_t task, size_t thread) { 62 HWY_ASSERT(task == thread); 63 if (task == 0) { // new thread 64 SpinResult result = spin.UntilDifferent(0, thread_active[0]); 65 ack_thread_release[0].store(hwy::platform::Now(), kRel); 66 reps1.store(result.reps); 67 if (!NanoSleep(20 * 1000 * 1000)) { 68 error.test_and_set(); 69 } 70 before_thread_done[0].store(hwy::platform::Now(), kRel); 71 thread_done[0].store(1, kRel); 72 } else { // main thread 73 if (!NanoSleep(30 * 1000 * 1000)) { 74 error.test_and_set(); 75 } 76 // Release the thread. 77 before_thread_go[0].store(hwy::platform::Now(), kRel); 78 thread_active[0].store(1, kRel); 79 // Wait for it to finish. 80 const size_t reps = spin.UntilEqual(1, thread_done[0]); 81 ack_thread_done[0].store(hwy::platform::Now(), kRel); 82 reps2.store(reps); 83 } 84 }); 85 86 const double t1 = hwy::platform::Now(); 87 const double elapsed = t1 - t0; 88 const double latency1 = 89 ack_thread_release[0].load(kAcq) - before_thread_go[0].load(kAcq); 90 const double latency2 = 91 ack_thread_done[0].load(kAcq) - before_thread_done[0].load(kAcq); 92 fprintf(stderr, 93 "Elapsed time: %f us; reps1=%zu, reps2=%zu, latency=%f %f us\n", 94 elapsed * 1E6, reps1.load(), reps2.load(), latency1 * 1E6, 95 latency2 * 1E6); 96 // Unless NanoSleep failed to sleep, this should take 50ms+epsilon. 97 HWY_ASSERT(error.test_and_set() || elapsed > 25E-3); 98 } 99 }; // namespace hwy 100 101 // Simple mutex. 102 TEST(SpinTest, TestPingPong) { 103 if (!HaveThreadingSupport()) { 104 HWY_WARN("Threads not supported, skipping test\n"); 105 return; 106 } 107 108 const SpinType spin_type = DetectSpin(); 109 fprintf(stderr, "Spin method : %s\n", ToString(spin_type)); 110 CallWithSpin(spin_type, TestPingPongT()); 111 } 112 113 } // namespace 114 } // namespace hwy 115 116 HWY_TEST_MAIN();