Benchmark for std::mutex
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <math.h> | |
#include <stdint.h> | |
#include <sys/time.h> | |
#include <atomic> | |
#include <functional> | |
#include <map> | |
#include <mutex> | |
#include <string> | |
#include <thread> | |
#include <vector> | |
using namespace std; | |
typedef int64_t int64; | |
const double kBenchmarkDuration = 0.7; | |
const int kShortWait = 1000; | |
class BenchmarkState { | |
public: | |
BenchmarkState(const string& name) : name_(name) {} | |
bool KeepRunning(); | |
private: | |
void PrintStats(); | |
static double GetTime(); | |
string name_; | |
double start_time_; | |
int64 iteration_ = 0; | |
int64 iteration_to_check_ = 1; | |
vector<pair<int64, double>> results_; | |
}; | |
bool BenchmarkState::KeepRunning() { | |
iteration_++; | |
if (iteration_to_check_ == iteration_) { | |
if (iteration_to_check_ == 1) { | |
iteration_to_check_ = 2; | |
start_time_ = GetTime(); | |
results_.reserve(100); | |
results_.emplace_back(iteration_, start_time_); | |
return true; | |
} | |
double current_time = GetTime(); | |
if (current_time - start_time_ < 0.1 && | |
current_time - start_time_ < kBenchmarkDuration) { | |
if (current_time - start_time_ < 0.01) { | |
iteration_to_check_ = iteration_ * 8; | |
} else { | |
iteration_to_check_ = iteration_ * 2; | |
} | |
} else { | |
results_.emplace_back(iteration_, current_time); | |
iteration_to_check_ = | |
iteration_ + round(iteration_ / (current_time - start_time_) / 10); | |
if (iteration_to_check_ <= iteration_) { | |
iteration_to_check_ = iteration_ + 1; | |
} | |
if (current_time - start_time_ > kBenchmarkDuration) { | |
PrintStats(); | |
return false; | |
} | |
} | |
} | |
return true; | |
} | |
void BenchmarkState::PrintStats() { | |
double best_qps = 0; | |
for (int i = 1; i < results_.size(); i++) { | |
best_qps = max(best_qps, (results_[i].first - results_[i - 1].first) / | |
(results_[i].second - results_[i - 1].second)); | |
} | |
printf("%32s: % 16.2f ns\n", name_.c_str(), 1e9 / best_qps); | |
} | |
double BenchmarkState::GetTime() { | |
struct timeval tv; | |
gettimeofday(&tv, nullptr); | |
return tv.tv_sec + tv.tv_usec * 1e-6; | |
} | |
void Benchmark(const string& name, std::function<void()> target) { | |
BenchmarkState state(name); | |
while (state.KeepRunning()) { | |
target(); | |
} | |
} | |
void ShortSleep() { | |
for (volatile int x = 0; x < kShortWait; x++); | |
} | |
int main() { | |
std::atomic<bool> b; | |
Benchmark("Atomic", [&]() { | |
b = true; | |
b = false; | |
}); | |
std::mutex m; | |
Benchmark("LockAndUnlock", [&]() { | |
m.lock(); | |
m.unlock(); | |
}); | |
Benchmark("ShortSleep", [&]() { | |
ShortSleep(); | |
}); | |
m.lock(); | |
std::atomic<bool> finished(false); | |
std::atomic<bool> waiting(false); | |
std::thread t([&]() { | |
while (!finished) { | |
while (waiting == false); | |
ShortSleep(); | |
m.unlock(); | |
m.lock(); | |
} | |
}); | |
Benchmark("LockAndUnlockWithContention", [&]() { | |
waiting = true; | |
m.lock(); | |
waiting = false; | |
ShortSleep(); | |
m.unlock(); | |
}); | |
waiting = true; | |
finished = true; | |
t.join(); | |
m.unlock(); | |
Benchmark("LockAndUnlockWithPenalty", [&]() { | |
waiting = true; | |
m.lock(); | |
ShortSleep(); | |
waiting = false; | |
ShortSleep(); | |
m.unlock(); | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment