9d10bd035a
[SVN r84329]
238 lines
6.7 KiB
C++
238 lines
6.7 KiB
C++
// (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 <boost/thread/condition_variable.hpp>
|
|
#include <boost/thread/mutex.hpp>
|
|
#include <boost/chrono/stopwatches/simple_stopwatch.hpp>
|
|
|
|
#include <condition_variable>
|
|
#include <future>
|
|
#include <limits>
|
|
#include <cstdio>
|
|
#include <thread>
|
|
#include <mutex>
|
|
#include <vector>
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
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<std::mutex> scoped_lock;
|
|
};
|
|
|
|
template <class Types>
|
|
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 <class S>
|
|
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 <class S>
|
|
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 <class Types>
|
|
Stopwatch::rep benchmark_ping_pong(unsigned consumer_count)
|
|
{
|
|
typedef SharedData<Types> S;
|
|
|
|
auto best_producer_time = std::numeric_limits<Stopwatch::rep>::max BOOST_PREVENT_MACRO_SUBSTITUTION ();
|
|
|
|
std::vector<std::thread> 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<S> , &shared_data };
|
|
// Start the producer and wait till it finishes.
|
|
std::thread
|
|
{ producer_thread<S> , &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<StdTypes> (i);
|
|
std::printf("BOOST: %d\n", i);
|
|
b.boost = benchmark_ping_pong<BoostTypes> (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;
|
|
}
|