diff --git a/app/invoke_parallel/main.cpp b/app/invoke_parallel/main.cpp index a856b51..4ae48ef 100644 --- a/app/invoke_parallel/main.cpp +++ b/app/invoke_parallel/main.cpp @@ -34,7 +34,7 @@ long fib(long n) { int main() { PROFILE_ENABLE - pls::scheduler scheduler{&my_scheduler_memory, 2}; + pls::scheduler scheduler{&my_scheduler_memory, 8}; long result; scheduler.perform_work([&] { diff --git a/lib/pls/CMakeLists.txt b/lib/pls/CMakeLists.txt index 4f8652a..66ed5b1 100644 --- a/lib/pls/CMakeLists.txt +++ b/lib/pls/CMakeLists.txt @@ -5,7 +5,9 @@ add_library(pls STATIC include/pls/algorithms/invoke_parallel.h include/pls/algorithms/invoke_parallel_impl.h - include/pls/internal/base/spin_lock.h src/internal/base/spin_lock.cpp + include/pls/internal/base/spin_lock.h + include/pls/internal/base/tas_spin_lock.h src/internal/base/tas_spin_lock.cpp + include/pls/internal/base/ttas_spin_lock.h src/internal/base/ttas_spin_lock.cpp include/pls/internal/base/thread.h src/internal/base/thread.cpp include/pls/internal/base/thread_impl.h include/pls/internal/base/barrier.h src/internal/base/barrier.cpp @@ -20,6 +22,7 @@ add_library(pls STATIC include/pls/internal/helpers/prohibit_new.h include/pls/internal/helpers/profiler.h include/pls/internal/helpers/mini_benchmark.h + include/pls/internal/helpers/unique_id.h include/pls/internal/scheduling/root_task.h src/internal/scheduling/root_task.cpp include/pls/internal/scheduling/thread_state.h src/internal/scheduling/thread_state.cpp @@ -29,7 +32,7 @@ add_library(pls STATIC include/pls/internal/scheduling/run_on_n_threads_task.h src/internal/scheduling/run_on_n_threads_task.cpp include/pls/internal/scheduling/fork_join_task.h src/internal/scheduling/fork_join_task.cpp include/pls/internal/scheduling/scheduler_memory.h src/internal/scheduling/scheduler_memory.cpp - include/pls/internal/helpers/unique_id.h) +) # Add everything in `./include` to be in the include path of this project target_include_directories(pls diff --git a/lib/pls/include/pls/internal/base/spin_lock.h b/lib/pls/include/pls/internal/base/spin_lock.h index cb87811..36f4bc3 100644 --- a/lib/pls/include/pls/internal/base/spin_lock.h +++ b/lib/pls/include/pls/internal/base/spin_lock.h @@ -2,32 +2,14 @@ #ifndef PLS_SPINLOCK_H #define PLS_SPINLOCK_H -#include -#include - -#include "pls/internal/base/thread.h" +#include "tas_spin_lock.h" +#include "ttas_spin_lock.h" namespace pls { namespace internal { namespace base { - /** - * A simple set and test_and_set based spin lock implementation. - * - * PORTABILITY: - * Current implementation is based on C++ 11 atomic_flag. - */ - class spin_lock { - std::atomic_flag flag_; - unsigned int yield_at_tries_; - - - public: - spin_lock(): flag_{ATOMIC_FLAG_INIT}, yield_at_tries_{1024} {}; - spin_lock(const spin_lock& other): flag_{ATOMIC_FLAG_INIT}, yield_at_tries_{other.yield_at_tries_} {} - - void lock(); - void unlock(); - }; + // Default Spin-Lock implementation for this project. + using spin_lock = tas_spin_lock; } } } diff --git a/lib/pls/include/pls/internal/base/tas_spin_lock.h b/lib/pls/include/pls/internal/base/tas_spin_lock.h new file mode 100644 index 0000000..173ee76 --- /dev/null +++ b/lib/pls/include/pls/internal/base/tas_spin_lock.h @@ -0,0 +1,39 @@ + +#ifndef PLS_TAS_SPIN_LOCK_H +#define PLS_TAS_SPIN_LOCK_H + +#include "tas_spin_lock.h" + +#include +#include + +#include "pls/internal/base/thread.h" + +namespace pls { + namespace internal { + namespace base { + /** + * A simple set and test_and_set based spin lock implementation. + * + * PORTABILITY: + * Current implementation is based on C++ 11 atomic_flag. + */ + class tas_spin_lock { + std::atomic_flag flag_; + unsigned int yield_at_tries_; + + + public: + tas_spin_lock(): flag_{ATOMIC_FLAG_INIT}, yield_at_tries_{1024} {}; + tas_spin_lock(const tas_spin_lock& other): flag_{ATOMIC_FLAG_INIT}, yield_at_tries_{other.yield_at_tries_} {} + + void lock(); + bool try_lock(); + void unlock(); + }; + } + } +} + + +#endif //PLS_TAS_SPIN_LOCK_H diff --git a/lib/pls/include/pls/internal/base/ttas_spin_lock.h b/lib/pls/include/pls/internal/base/ttas_spin_lock.h new file mode 100644 index 0000000..821865e --- /dev/null +++ b/lib/pls/include/pls/internal/base/ttas_spin_lock.h @@ -0,0 +1,39 @@ + +#ifndef PLS_TTAS_SPIN_LOCK_H +#define PLS_TTAS_SPIN_LOCK_H + +#include "tas_spin_lock.h" + +#include +#include + +#include "pls/internal/base/thread.h" + +namespace pls { + namespace internal { + namespace base { + /** + * A simple set and test_and_set based spin lock implementation. + * + * PORTABILITY: + * Current implementation is based on C++ 11 atomic_flag. + */ + class ttas_spin_lock { + std::atomic flag_; + unsigned int yield_at_tries_; + + + public: + ttas_spin_lock(): flag_{0}, yield_at_tries_{1024} {}; + ttas_spin_lock(const ttas_spin_lock& other): flag_{0}, yield_at_tries_{other.yield_at_tries_} {} + + void lock(); + bool try_lock(); + void unlock(); + }; + } + } +} + + +#endif //PLS_TTAS_SPIN_LOCK_H diff --git a/lib/pls/src/internal/base/spin_lock.cpp b/lib/pls/src/internal/base/tas_spin_lock.cpp similarity index 66% rename from lib/pls/src/internal/base/spin_lock.cpp rename to lib/pls/src/internal/base/tas_spin_lock.cpp index aff28f4..3ad4e42 100644 --- a/lib/pls/src/internal/base/spin_lock.cpp +++ b/lib/pls/src/internal/base/tas_spin_lock.cpp @@ -1,16 +1,13 @@ #include "pls/internal/helpers/profiler.h" -#include "pls/internal/base/spin_lock.h" +#include "pls/internal/base/tas_spin_lock.h" namespace pls { namespace internal { namespace base { - // TODO: Research/Measure how the memory fences/orders influence this. - // For now we simply try to be safe by forcing this lock to - // also act as a strict memory fence. - void spin_lock::lock() { + void tas_spin_lock::lock() { PROFILE_LOCK("Acquire Lock") int tries = 0; - while (flag_.test_and_set(std::memory_order_seq_cst)) { + while (flag_.test_and_set(std::memory_order_acquire)) { tries++; if (tries % yield_at_tries_ == 0) { this_thread::yield(); @@ -18,8 +15,12 @@ namespace pls { } } - void spin_lock::unlock() { - flag_.clear(std::memory_order_seq_cst); + bool tas_spin_lock::try_lock() { + return flag_.test_and_set(std::memory_order_acquire) == 0; + } + + void tas_spin_lock::unlock() { + flag_.clear(std::memory_order_release); } } } diff --git a/lib/pls/src/internal/base/ttas_spin_lock.cpp b/lib/pls/src/internal/base/ttas_spin_lock.cpp new file mode 100644 index 0000000..b60934b --- /dev/null +++ b/lib/pls/src/internal/base/ttas_spin_lock.cpp @@ -0,0 +1,34 @@ +#include "pls/internal/helpers/profiler.h" +#include "pls/internal/base/ttas_spin_lock.h" + +namespace pls { + namespace internal { + namespace base { + void ttas_spin_lock::lock() { + PROFILE_LOCK("Acquire Lock") + int tries = 0; + int expected = 0; + + do { + while (flag_.load(std::memory_order_relaxed) == 1) { + tries++; + if (tries % yield_at_tries_ == 0) { + this_thread::yield(); + } + } + + expected = 0; + } while (!flag_.compare_exchange_weak(expected, 1, std::memory_order_acquire)); + } + + bool ttas_spin_lock::try_lock() { + int expected = 0; + return flag_.load(std::memory_order_relaxed) == 0 && flag_.compare_exchange_weak(expected, 1, std::memory_order_acquire); + } + + void ttas_spin_lock::unlock() { + flag_.store(0, std::memory_order_release); + } + } + } +}