// (C) Copyright 2012 Vicente J. Botet Escriba // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // This performance test is based on the performance test provided by maxim.yegorushkin // at https://svn.boost.org/trac/boost/ticket/7422 #define BOOST_THREAD_DONT_PROVIDE_INTERRUPTIONS #include #include #include #include #include #include #include #include #include #include //////////////////////////////////////////////////////////////////////////////////////////////// namespace { //////////////////////////////////////////////////////////////////////////////////////////////// // class Stopwatch // { // public: // typedef long long rep; // // static rep now() // { // timespec ts; // if (clock_gettime(CLOCK_MONOTONIC, &ts)) abort(); // return ts.tv_sec * rep(1000000000) + ts.tv_nsec; // } // // Stopwatch() : // start_(now()) // { // } // // rep elapsed() const // { // return now() - start_; // } // // private: // rep start_; // }; typedef boost::chrono::simple_stopwatch<> Stopwatch; //////////////////////////////////////////////////////////////////////////////////////////////// struct BoostTypes { typedef boost::condition_variable condition_variable; typedef boost::mutex mutex; typedef boost::mutex::scoped_lock scoped_lock; }; struct StdTypes { typedef std::condition_variable condition_variable; typedef std::mutex mutex; typedef std::unique_lock scoped_lock; }; template struct SharedData: Types { unsigned const iterations; unsigned counter; unsigned semaphore; typename Types::condition_variable cnd; typename Types::mutex mtx; Stopwatch::rep producer_time; SharedData(unsigned iterations, unsigned consumers) : iterations(iterations), counter(), semaphore(consumers) // Initialize to the number of consumers. (*) , producer_time() { } }; //////////////////////////////////////////////////////////////////////////////////////////////// template void producer_thread(S* shared_data) { Stopwatch sw; unsigned const consumers = shared_data->semaphore; // (*) for (unsigned i = shared_data->iterations; i--;) { { typename S::scoped_lock lock(shared_data->mtx); // Wait till all consumers signal. while (consumers != shared_data->semaphore) { shared_data->cnd.wait(lock); } shared_data->semaphore = 0; // Signal consumers. ++shared_data->counter; } shared_data->cnd.notify_all(); } shared_data->producer_time = sw.elapsed().count(); } template void consumer_thread(S* shared_data) { unsigned counter = 0; while (counter != shared_data->iterations) { { typename S::scoped_lock lock(shared_data->mtx); // Wait till the producer signals. while (counter == shared_data->counter) { shared_data->cnd.wait(lock); } counter = shared_data->counter; // Signal the producer. ++shared_data->semaphore; } shared_data->cnd.notify_all(); } } //////////////////////////////////////////////////////////////////////////////////////////////// template Stopwatch::rep benchmark_ping_pong(unsigned consumer_count) { typedef SharedData S; auto best_producer_time = std::numeric_limits::max BOOST_PREVENT_MACRO_SUBSTITUTION (); std::vector consumers { consumer_count }; // Run the benchmark 10 times and report the best time. for (int times = 10; times--;) { S shared_data { 100000, consumer_count }; // Start the consumers. for (unsigned i = 0; i < consumer_count; ++i) consumers[i] = std::thread { consumer_thread , &shared_data }; // Start the producer and wait till it finishes. std::thread { producer_thread , &shared_data }.join(); // Wait till consumers finish. for (unsigned i = 0; i < consumer_count; ++i) consumers[i].join(); best_producer_time = std::min BOOST_PREVENT_MACRO_SUBSTITUTION (best_producer_time, shared_data.producer_time); } return best_producer_time; } //////////////////////////////////////////////////////////////////////////////////////////////// } // namespace //////////////////////////////////////////////////////////////////////////////////////////////// // sudo chrt -f 99 /usr/bin/time -f "\n***\ntime: %E\ncontext switches: %c\nwaits: %w" /home/max/otsquant/build/Linux-x86_64-64.g++-release/test/test /* Producer-consumer ping-pong tests. It aims to benchmark condition variables with and without thread cancellation support by comparing the time it took to complete the benchmark. Condition variable with thread cancellation support is boost::condition_variable from boost-1.51. Without - std::condition_variable that comes with gcc-4.7.2. One producer, one to CONSUMER_MAX consumers. The benchmark calls condition_variable::notify_all() without holding a mutex to maximize contention within this function. Each benchmark for a number of consumers is run three times and the best time is picked to get rid of outliers. The results are reported for each benchmark for a number of consumers. The most important number is (std - boost) / std * 100. Positive numbers are when boost::condition_variable is faster, negative it is slower. */ int main() { std::printf("MAIN\n"); enum { CONSUMER_MAX = 2 }; struct { Stopwatch::rep boost, std; } best_times[CONSUMER_MAX] = {}; for (unsigned i = 1; i <= CONSUMER_MAX; ++i) { auto& b = best_times[i - 1]; std::printf("STD: %d\n", i); b.std = benchmark_ping_pong (i); std::printf("BOOST: %d\n", i); b.boost = benchmark_ping_pong (i); std::printf("consumers: %4d\n", i); std::printf("best std producer time: %15.9fsec\n", b.std * 1e-9); std::printf("best boost producer time: %15.9fsec\n", b.boost * 1e-9); std::printf("(std - boost) / std: %7.2f%%\n", (b.std - b.boost) * 100. / b.std); } printf("\ncsv:\n\n"); printf("consumers,(std-boost)/std,std,boost\n"); for (unsigned i = 1; i <= CONSUMER_MAX; ++i) { auto& b = best_times[i - 1]; printf("%d,%f,%lld,%lld\n", i, (b.std - b.boost) * 100. / b.std, b.std, b.boost); } return 1; }