#include // using omp_get_max_threads #include // using std::fabsf, HUGE_VALF #include // using std::chrono::* #include // using std::default_random_engine, std::seedq, std::uniform_*_distribution #include // using std::cout #include // using boost::mutex #include // using boost::shared_mutex #include // using boost::lock_guard #include // using boost::shared_lock_guard namespace { #ifdef USE_RWLOCK using mutex_type = boost::shared_mutex; using read_lock_type = boost::shared_lock_guard; #else using mutex_type = boost::mutex; using read_lock_type = boost::lock_guard; #endif using write_lock_type = boost::lock_guard; enum class Direction { up, down, left, right }; struct Vector2f { float x, y; constexpr Vector2f(float x=0.f, float y=0.f) noexcept : x(x), y(y) {} }; struct UnitState { Vector2f position, velocity; Direction facing; constexpr UnitState() noexcept : position(), velocity(), facing(Direction::up) {} }; class GameUnit { mutable mutex_type mutex; UnitState state; float maxSpeed; public: GameUnit() noexcept : mutex(), state(), maxSpeed(HUGE_VALF) {} UnitState getFullState() const { read_lock_type lock(mutex); return state; } void updatePosition(float x, float y, Direction facing) { write_lock_type lock(mutex); if(std::fabs(state.position.x - x) > maxSpeed || std::fabs(state.position.y - y) > maxSpeed) return; state.position.x = x; state.position.y = y; state.facing = facing; } }; } int main() { using clock_t = std::chrono::steady_clock; constexpr int maxReadsToWrites = 1000; constexpr std::chrono::seconds seconds(10); const clock_t::duration clock_seconds = std::chrono::duration_cast(seconds); const int maxThreads = omp_get_max_threads(); std::seed_seq seed; std::uniform_real_distribution floatDistr; for(int readsToWrites = 1; readsToWrites <= maxReadsToWrites; readsToWrites *= 10) { std::uniform_int_distribution intDistr(0, readsToWrites); for(int threads = 1; threads <= maxThreads; threads *= 2) { GameUnit unit; double opsPerSec = 0.; const clock_t::time_point startTime = clock_t::now(); # pragma omp parallel num_threads(threads) { std::default_random_engine rnd; # pragma omp critical rnd = std::default_random_engine(seed); long long localOps = 0; clock_t::duration dt; Vector2f pos; for(localOps = 0; (dt = clock_t::now() - startTime) < clock_seconds; ++localOps) { if(intDistr(rnd)) pos = unit.getFullState().position; else { pos.x += floatDistr(rnd); pos.y += floatDistr(rnd); unit.updatePosition(pos.x, pos.y, Direction::up); } } const double localOpsPerSec = localOps / (std::chrono::duration_cast(dt).count() * 1e-9); # pragma omp critical opsPerSec += localOpsPerSec; } std::cout << readsToWrites << " reads per write " << threads << " threads " << opsPerSec << " operations per second\n"; } } }