Commit bcb9817f authored by neverlord's avatar neverlord

performance tests

parent 920472e6
include ../Makefile.rules CXX = /usr/bin/g++-4.6
CXXFLAGS = -std=c++0x -pedantic -Wall -Wextra -O2
#CXX = /opt/local/bin/g++-mp-4.5 #CXX = /opt/local/bin/g++-mp-4.5
#CXX = /opt/local/bin/g++-mp-4.6 #CXX = /opt/local/bin/g++-mp-4.6
#CXXFLAGS = -std=c++0x -pedantic -Wall -Wextra -g -O0 -I/opt/local/include/ #CXXFLAGS = -std=c++0x -pedantic -Wall -Wextra -g -O0 -I/opt/local/include/
#CXXFLAGS = -std=c++0x -pedantic -Wall -Wextra -O2 -I/opt/local/include/ #CXXFLAGS = -std=c++0x -pedantic -Wall -Wextra -O2 -I/opt/local/include/
#LIBS = -L/opt/local/lib -lboost_thread-mt -L../ -lcppa #LIBS = -L/opt/local/lib -lboost_thread-mt -L../ -lcppa
INCLUDES = -I./ -I../ INCLUDES = -I./
LIBS = -pthread
FLAGS = -DCACHE_LINE_SIZE=64 FLAGS = -DCACHE_LINE_SIZE=64
EXECUTABLE = ../queue_test EXECUTABLE = queue_test
HEADERS = sutter_list.hpp HEADERS = blocking_cached_stack2.hpp blocking_cached_stack.hpp blocking_sutter_list.hpp cached_stack.hpp defines.hpp intrusive_sutter_list.hpp lockfree_list.hpp sutter_list.hpp
SOURCES = main.cpp SOURCES = main.cpp
OBJECTS = $(SOURCES:.cpp=.o) OBJECTS = $(SOURCES:.cpp=.o)
%.o : %.cpp $(HEADERS) $(HEADERS) %.o : %.cpp $(HEADERS)
$(CXX) $(CXXFLAGS) $(INCLUDES) $(FLAGS) -c $< -o $@ $(CXX) $(CXXFLAGS) $(INCLUDES) $(FLAGS) -c $< -o $@
$(EXECUTABLE) : $(OBJECTS) $(HEADERS) $(EXECUTABLE) : $(OBJECTS) $(HEADERS)
$(CXX) $(LIBS) -L../ -lcppa $(OBJECTS) -o $(EXECUTABLE) $(CXX) $(LIBS) $(OBJECTS) -o $(EXECUTABLE)
all : $(EXECUTABLE) all : $(EXECUTABLE)
......
#ifndef BLOCKING_CACHED_STACK_HPP #ifndef BLOCKING_CACHED_STACK_HPP
#define BLOCKING_CACHED_STACK_HPP #define BLOCKING_CACHED_STACK_HPP
#include <thread>
#include <atomic> #include <atomic>
#include <boost/thread.hpp>
#include "defines.hpp" #include "defines.hpp"
...@@ -11,115 +11,115 @@ template<typename T> ...@@ -11,115 +11,115 @@ template<typename T>
class blocking_cached_stack class blocking_cached_stack
{ {
// singly linked list, serves as cache // singly linked list, serves as cache
T* m_head; T* m_head;
char m_pad1[CACHE_LINE_SIZE - sizeof(T*)]; char m_pad1[CACHE_LINE_SIZE - sizeof(T*)];
// modified by consumers // modified by consumers
std::atomic<T*> m_stack; std::atomic<T*> m_stack;
char m_pad2[CACHE_LINE_SIZE - sizeof(std::atomic<T*>)]; char m_pad2[CACHE_LINE_SIZE - sizeof(std::atomic<T*>)];
// locked on enqueue/dequeue operations to/from an empty list // locked on enqueue/dequeue operations to/from an empty list
boost::mutex m_mtx; std::mutex m_mtx;
boost::condition_variable m_cv; std::condition_variable m_cv;
typedef boost::unique_lock<boost::mutex> lock_type; typedef std::unique_lock<std::mutex> lock_type;
// read all elements of m_stack, convert them to FIFO order and store // read all elements of m_stack, convert them to FIFO order and store
// them in m_head // them in m_head
// precondition: m_head == nullptr // precondition: m_head == nullptr
bool consume_stack() bool consume_stack()
{ {
T* e = m_stack.load(); T* e = m_stack.load();
while (e) while (e)
{ {
if (m_stack.compare_exchange_weak(e, 0)) if (m_stack.compare_exchange_weak(e, 0))
{ {
// m_stack is now empty (m_stack == nullptr) // m_stack is now empty (m_stack == nullptr)
while (e) while (e)
{ {
T* next = e->next; T* next = e->next;
// enqueue to m_head // enqueue to m_head
e->next = m_head; e->next = m_head;
m_head = e; m_head = e;
// next iteration // next iteration
e = next; e = next;
} }
return true; return true;
} }
} }
// nothing to consume // nothing to consume
return false; return false;
} }
void wait_for_data() void wait_for_data()
{ {
if (!m_head && !(m_stack.load())) if (!m_head && !(m_stack.load()))
{ {
lock_type lock(m_mtx); lock_type lock(m_mtx);
while (!(m_stack.load())) m_cv.wait(lock); while (!(m_stack.load())) m_cv.wait(lock);
} }
} }
public: public:
blocking_cached_stack() : m_head(0) blocking_cached_stack() : m_head(0)
{ {
m_stack = 0; m_stack = 0;
} }
~blocking_cached_stack() ~blocking_cached_stack()
{ {
do do
{ {
while (m_head) while (m_head)
{ {
T* next = m_head->next; T* next = m_head->next;
delete m_head; delete m_head;
m_head = next; m_head = next;
} }
} }
// repeat if m_stack is not empty // repeat if m_stack is not empty
while (consume_stack()); while (consume_stack());
} }
void push(T* what) void push(T* what)
{ {
T* e = m_stack.load(); T* e = m_stack.load();
for (;;) for (;;)
{ {
what->next = e; what->next = e;
if (!e) if (!e)
{ {
lock_type lock(m_mtx); lock_type lock(m_mtx);
if (m_stack.compare_exchange_weak(e, what)) if (m_stack.compare_exchange_weak(e, what))
{ {
m_cv.notify_one(); m_cv.notify_one();
return; return;
} }
} }
// compare_exchange_weak stores the // compare_exchange_weak stores the
// new value to e if the operation fails // new value to e if the operation fails
else if (m_stack.compare_exchange_weak(e, what)) return; else if (m_stack.compare_exchange_weak(e, what)) return;
} }
} }
T* try_pop() T* try_pop()
{ {
if (m_head || consume_stack()) if (m_head || consume_stack())
{ {
T* result = m_head; T* result = m_head;
m_head = m_head->next; m_head = m_head->next;
return result; return result;
} }
return 0; return 0;
} }
T* pop() T* pop()
{ {
wait_for_data(); wait_for_data();
return try_pop(); return try_pop();
} }
}; };
......
...@@ -11,129 +11,129 @@ template<typename T> ...@@ -11,129 +11,129 @@ template<typename T>
class blocking_cached_stack2 class blocking_cached_stack2
{ {
// singly linked list, serves as cache // singly linked list, serves as cache
T* m_head; T* m_head;
char m_pad1[CACHE_LINE_SIZE - sizeof(T*)]; char m_pad1[CACHE_LINE_SIZE - sizeof(T*)];
// modified by consumers // modified by consumers
std::atomic<T*> m_stack; std::atomic<T*> m_stack;
char m_pad2[CACHE_LINE_SIZE - sizeof(std::atomic<T*>)]; char m_pad2[CACHE_LINE_SIZE - sizeof(std::atomic<T*>)];
T* m_dummy; T* m_dummy;
char m_pad3[CACHE_LINE_SIZE - sizeof(T)]; char m_pad3[CACHE_LINE_SIZE - sizeof(T)];
// locked on enqueue/dequeue operations to/from an empty list // locked on enqueue/dequeue operations to/from an empty list
boost::mutex m_mtx; std::mutex m_mtx;
boost::condition_variable m_cv; std::condition_variable m_cv;
typedef boost::unique_lock<boost::mutex> lock_type; typedef std::unique_lock<std::mutex> lock_type;
// read all elements of m_stack, convert them to FIFO order and store // read all elements of m_stack, convert them to FIFO order and store
// them in m_head // them in m_head
// precondition: m_head == nullptr // precondition: m_head == nullptr
void consume_stack() void consume_stack()
{ {
T* e = m_stack.load(); T* e = m_stack.load();
while (e) while (e)
{ {
// enqueue dummy instead of nullptr to reduce // enqueue dummy instead of nullptr to reduce
// lock operations // lock operations
if (m_stack.compare_exchange_weak(e, m_dummy)) if (m_stack.compare_exchange_weak(e, m_dummy))
{ {
// m_stack is now empty (m_stack == m_dummy) // m_stack is now empty (m_stack == m_dummy)
// m_dummy marks always the end of the stack // m_dummy marks always the end of the stack
while (e && e != m_dummy) while (e && e != m_dummy)
{ {
T* next = e->next; T* next = e->next;
// enqueue to m_head // enqueue to m_head
e->next = m_head; e->next = m_head;
m_head = e; m_head = e;
// next iteration // next iteration
e = next; e = next;
} }
return; return;
} }
} }
// nothing to consume // nothing to consume
} }
void wait_for_data() void wait_for_data()
{ {
if (!m_head) if (!m_head)
{ {
T* e = m_stack.load(); T* e = m_stack.load();
while (e == m_dummy) while (e == m_dummy)
{ {
if (m_stack.compare_exchange_weak(e, 0)) e = 0; if (m_stack.compare_exchange_weak(e, 0)) e = 0;
} }
if (!e) if (!e)
{ {
lock_type lock(m_mtx); lock_type lock(m_mtx);
while (!(m_stack.load())) m_cv.wait(lock); while (!(m_stack.load())) m_cv.wait(lock);
} }
consume_stack(); consume_stack();
} }
} }
void delete_head() void delete_head()
{ {
while (m_head) while (m_head)
{ {
T* next = m_head->next; T* next = m_head->next;
delete m_head; delete m_head;
m_head = next; m_head = next;
} }
} }
public: public:
blocking_cached_stack2() : m_head(0) blocking_cached_stack2() : m_head(0)
{ {
m_stack = 0; m_stack = 0;
m_dummy = new T; m_dummy = new T;
} }
~blocking_cached_stack2() ~blocking_cached_stack2()
{ {
delete_head(); delete_head();
T* e = m_stack.load(); T* e = m_stack.load();
if (e && e != m_dummy) if (e && e != m_dummy)
{ {
consume_stack(); consume_stack();
delete_head(); delete_head();
} }
delete m_dummy; delete m_dummy;
} }
void push(T* what) void push(T* what)
{ {
T* e = m_stack.load(); T* e = m_stack.load();
for (;;) for (;;)
{ {
what->next = e; what->next = e;
if (!e) if (!e)
{ {
lock_type lock(m_mtx); lock_type lock(m_mtx);
if (m_stack.compare_exchange_weak(e, what)) if (m_stack.compare_exchange_weak(e, what))
{ {
m_cv.notify_one(); m_cv.notify_one();
return; return;
} }
} }
// compare_exchange_weak stores the // compare_exchange_weak stores the
// new value to e if the operation fails // new value to e if the operation fails
else if (m_stack.compare_exchange_weak(e, what)) return; else if (m_stack.compare_exchange_weak(e, what)) return;
} }
} }
T* pop() T* pop()
{ {
wait_for_data(); wait_for_data();
T* result = m_head; T* result = m_head;
m_head = m_head->next; m_head = m_head->next;
return result; return result;
} }
}; };
......
...@@ -15,99 +15,99 @@ template<typename T> ...@@ -15,99 +15,99 @@ template<typename T>
class blocking_sutter_list class blocking_sutter_list
{ {
struct node struct node
{ {
node(T* val = 0) : value(val), next(0) { } node(T* val = 0) : value(val), next(0) { }
T* value; T* value;
std::atomic<node*> next; std::atomic<node*> next;
char pad[CACHE_LINE_SIZE - sizeof(T*)- sizeof(std::atomic<node*>)]; char pad[CACHE_LINE_SIZE - sizeof(T*)- sizeof(std::atomic<node*>)];
}; };
// one consumer at a time // one consumer at a time
node* m_first; node* m_first;
char m_pad1[CACHE_LINE_SIZE - sizeof(node*)]; char m_pad1[CACHE_LINE_SIZE - sizeof(node*)];
// for one producers at a time // for one producers at a time
node* m_last; node* m_last;
char m_pad2[CACHE_LINE_SIZE - sizeof(node*)]; char m_pad2[CACHE_LINE_SIZE - sizeof(node*)];
// shared among producers // shared among producers
std::atomic<bool> m_producer_lock; std::atomic<bool> m_producer_lock;
char m_pad3[CACHE_LINE_SIZE - sizeof(std::atomic<bool>)]; char m_pad3[CACHE_LINE_SIZE - sizeof(std::atomic<bool>)];
// locked on enqueue/dequeue operations to/from an empty list // locked on enqueue/dequeue operations to/from an empty list
boost::mutex m_mtx; std::mutex m_mtx;
boost::condition_variable m_cv; std::condition_variable m_cv;
typedef boost::unique_lock<boost::mutex> lock_type; typedef std::unique_lock<std::mutex> lock_type;
public: public:
blocking_sutter_list() blocking_sutter_list()
{ {
m_first = m_last = new node; m_first = m_last = new node;
m_producer_lock = false; m_producer_lock = false;
} }
~blocking_sutter_list() ~blocking_sutter_list()
{ {
while (m_first) while (m_first)
{ {
node* tmp = m_first; node* tmp = m_first;
m_first = tmp->next; m_first = tmp->next;
delete tmp; delete tmp;
} }
} }
// takes ownership of what // takes ownership of what
void push(T* what) void push(T* what)
{ {
bool consumer_might_sleep = 0; bool consumer_might_sleep = 0;
node* tmp = new node(what); node* tmp = new node(what);
// acquire exclusivity // acquire exclusivity
while (m_producer_lock.exchange(true)) while (m_producer_lock.exchange(true))
{ {
boost::this_thread::yield(); std::this_thread::yield();
} }
// do we have to wakeup a sleeping consumer? // do we have to wakeup a sleeping consumer?
// this is a sufficient condition because m_last->value is 0 // this is a sufficient condition because m_last->value is 0
// if and only if m_head == m_tail // if and only if m_head == m_tail
consumer_might_sleep = (m_last->value == 0); consumer_might_sleep = (m_last->value == 0);
// publish & swing last forward // publish & swing last forward
m_last->next = tmp; m_last->next = tmp;
m_last = tmp; m_last = tmp;
// release exclusivity // release exclusivity
m_producer_lock = false; m_producer_lock = false;
// wakeup consumer if needed // wakeup consumer if needed
if (consumer_might_sleep) if (consumer_might_sleep)
{ {
lock_type lock(m_mtx); lock_type lock(m_mtx);
m_cv.notify_one(); m_cv.notify_one();
} }
} }
// polls the queue until an element was dequeued // polls the queue until an element was dequeued
T* pop() T* pop()
{ {
node* first = m_first; node* first = m_first;
node* next = m_first->next; node* next = m_first->next;
if (!next) if (!next)
{ {
lock_type lock(m_mtx); lock_type lock(m_mtx);
while (!(next = m_first->next)) while (!(next = m_first->next))
{ {
m_cv.wait(lock); m_cv.wait(lock);
} }
} }
T* result = next->value; // take it out T* result = next->value; // take it out
next->value = 0; // of the node next->value = 0; // of the node
// swing first forward // swing first forward
m_first = next; m_first = next;
// delete old dummy // delete old dummy
delete first; delete first;
// done // done
return result; return result;
} }
}; };
......
...@@ -95,7 +95,7 @@ class cached_stack ...@@ -95,7 +95,7 @@ class cached_stack
T* result = try_pop(); T* result = try_pop();
while (!result) while (!result)
{ {
boost::this_thread::yield(); std::this_thread::yield();
result = try_pop(); result = try_pop();
} }
return result; return result;
......
...@@ -62,7 +62,7 @@ class intrusive_sutter_list ...@@ -62,7 +62,7 @@ class intrusive_sutter_list
// acquire exclusivity // acquire exclusivity
while (m_producer_lock.exchange(true)) while (m_producer_lock.exchange(true))
{ {
boost::this_thread::yield(); std::this_thread::yield();
} }
// publish & swing last forward // publish & swing last forward
m_last->next = tmp; m_last->next = tmp;
...@@ -97,7 +97,7 @@ class intrusive_sutter_list ...@@ -97,7 +97,7 @@ class intrusive_sutter_list
T result; T result;
while (!try_pop(result)) while (!try_pop(result))
{ {
boost::this_thread::yield(); std::this_thread::yield();
} }
return result; return result;
} }
......
...@@ -90,7 +90,7 @@ class lockfree_list ...@@ -90,7 +90,7 @@ class lockfree_list
T result; T result;
while (!try_pop(result)) while (!try_pop(result))
{ {
boost::this_thread::yield(); std::this_thread::yield();
} }
return result; return result;
} }
......
#include <thread>
#include <vector> #include <vector>
#include <cstddef> #include <cstddef>
#include <sstream> #include <sstream>
#include <iostream> #include <iostream>
#include <stdexcept> #include <stdexcept>
#include <boost/thread.hpp> //#include <boost/thread.hpp>
#include <boost/progress.hpp> #include <boost/progress.hpp>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
...@@ -31,246 +32,248 @@ namespace { ...@@ -31,246 +32,248 @@ namespace {
template<typename Queue, typename Allocator> template<typename Queue, typename Allocator>
void producer(Queue& q, Allocator& a, size_t begin, size_t end) void producer(Queue& q, Allocator& a, size_t begin, size_t end)
{ {
for ( ; begin != end; ++begin) for ( ; begin != end; ++begin)
{ {
q.push(a(begin)); q.push(a(begin));
} }
} }
template<typename Queue, typename Processor> template<typename Queue, typename Processor>
void consumer(Queue& q, Processor& p, size_t num_messages) void consumer(Queue& q, Processor& p, size_t num_messages)
{ {
// vector<bool> scales better (in memory) than bool[num_messages] // vector<bool> scales better (in memory) than bool[num_messages]
// std::vector<bool> received(num_messages); // std::vector<bool> received(num_messages);
// for (size_t i = 0; i < num_messages; ++i) received[i] = false; // for (size_t i = 0; i < num_messages; ++i) received[i] = false;
for (size_t i = 0; i < num_messages; ++i) for (size_t i = 0; i < num_messages; ++i)
{ {
size_t value; size_t value;
p(q.pop(), value); p(q.pop(), value);
/* /*
if (value >= num_messages) if (value >= num_messages)
{ {
throw std::runtime_error("value out of bounds"); throw std::runtime_error("value out of bounds");
} }
else if (received[value]) else if (received[value])
{ {
std::ostringstream oss; std::ostringstream oss;
oss << "ERROR: received element nr. " << value << " two times"; oss << "ERROR: received element nr. " << value << " two times";
throw std::runtime_error(oss.str()); throw std::runtime_error(oss.str());
} }
received[value] = true; received[value] = true;
*/ */
} }
// done // done
} }
void usage() void usage()
{ {
cout << "usage:" << endl cout << "usage:" << endl
<< "queue_test [messages] [producer threads] " << "queue_test [messages] [producer threads] "
"[list impl.] {format string}" << endl "[list impl.] {format string}" << endl
<< " available implementations:" << endl << " available implementations:" << endl
<< " - sutter_list" << endl << " - sutter_list" << endl
<< " - intrusive_sutter_list" << endl << " - intrusive_sutter_list" << endl
<< " - blocking_sutter_list" << endl << " - blocking_sutter_list" << endl
<< " - cached_stack" << endl << " - cached_stack" << endl
<< " - blocking_cached_stack" << endl << " - blocking_cached_stack" << endl
<< " - blocking_cached_stack2" << endl << " - blocking_cached_stack2" << endl
<< " - lockfree_list" << endl << " - lockfree_list" << endl
<< endl << endl
<< " possible format string variables: " << endl << " possible format string variables: " << endl
<< " - $MESSAGES" << endl << " - MESSAGES" << endl
<< " - $PRODUCERS" << endl << " - MSG_IN_MILLION" << endl
<< " - $TIME" << endl << " - PRODUCERS" << endl
<< endl << " - TIME" << endl
<< "example: ./queue_test 10000 10 cached_list \"$MESSAGES $TIME\"" << endl
<< endl; << "example: ./queue_test 10000 10 cached_stack \"MESSAGES TIME\""
<< endl;
} }
template<typename Queue, typename Allocator, typename Processor> template<typename Queue, typename Allocator, typename Processor>
double run_test(size_t num_messages, size_t num_producers, double run_test(size_t num_messages, size_t num_producers,
Allocator element_allocator, Processor element_processor) Allocator element_allocator, Processor element_processor)
{ {
size_t num_messages_per_producer = num_messages / num_producers; size_t num_messages_per_producer = num_messages / num_producers;
// measurement // measurement
boost::timer t0; boost::timer t0;
// locals // locals
Queue list; Queue list;
std::vector<boost::thread*> producer_threads(num_producers); std::vector<std::thread*> producer_threads(num_producers);
for (size_t i = 0; i < num_producers; ++i) for (size_t i = 0; i < num_producers; ++i)
{ {
producer_threads[i] = new boost::thread(producer<Queue, Allocator>, producer_threads[i] = new std::thread(producer<Queue, Allocator>,
boost::ref(list), std::ref(list),
boost::ref(element_allocator), std::ref(element_allocator),
i * num_messages_per_producer, i * num_messages_per_producer,
(i+1) * num_messages_per_producer); (i+1) * num_messages_per_producer);
} }
// run consumer in main thread // run consumer in main thread
consumer(list, element_processor, num_messages); consumer(list, element_processor, num_messages);
// print result // print result
return t0.elapsed(); return t0.elapsed();
} }
struct cs_element struct cs_element
{ {
size_t value; size_t value;
std::atomic<cs_element*> next; std::atomic<cs_element*> next;
cs_element(size_t val = 0) : value(val), next(0) { } cs_element(size_t val = 0) : value(val), next(0) { }
}; };
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
if (argc < 4 || argc > 5) if (argc < 4 || argc > 5)
{ {
usage(); usage();
return -1; return -1;
} }
size_t num_messages = boost::lexical_cast<size_t>(argv[1]); size_t num_messages = boost::lexical_cast<size_t>(argv[1]);
size_t num_producers = boost::lexical_cast<size_t>(argv[2]); size_t num_producers = boost::lexical_cast<size_t>(argv[2]);
if (num_messages == 0 || num_producers == 0) if (num_messages == 0 || num_producers == 0)
{ {
cerr << "invalid arguments" << endl; cerr << "invalid arguments" << endl;
return -2; return -2;
} }
if ((num_messages % num_producers) != 0) if ((num_messages % num_producers) != 0)
{ {
cerr << "(num_messages % num_producers) != 0" << endl; cerr << "(num_messages % num_producers) != 0" << endl;
return -3; return -3;
} }
std::string format_string; std::string format_string;
if (argc == 5) if (argc == 5)
{ {
format_string = argv[4]; format_string = argv[4];
} }
else else
{ {
format_string = "$MESSAGES $TIME"; format_string = "$MESSAGES $TIME";
} }
std::string list_name = argv[3]; std::string list_name = argv[3];
double elapsed_time; double elapsed_time;
if (list_name == "sutter_list") if (list_name == "sutter_list")
{ {
elapsed_time = run_test<sutter_list<size_t>>( elapsed_time = run_test<sutter_list<size_t>>(
num_messages, num_messages,
num_producers, num_producers,
[] (size_t value) -> size_t* { [] (size_t value) -> size_t* {
return new size_t(value); return new size_t(value);
}, },
[] (size_t* value, size_t& storage) { [] (size_t* value, size_t& storage) {
storage = *value; storage = *value;
delete value; delete value;
} }
); );
} }
else if (list_name == "intrusive_sutter_list") else if (list_name == "intrusive_sutter_list")
{ {
typedef intrusive_sutter_list<size_t> isl; typedef intrusive_sutter_list<size_t> isl;
elapsed_time = run_test<isl>( elapsed_time = run_test<isl>(
num_messages, num_messages,
num_producers, num_producers,
[] (size_t value) -> isl::node* { [] (size_t value) -> isl::node* {
return new isl::node(value); return new isl::node(value);
}, },
[] (const size_t& value, size_t& storage) { [] (const size_t& value, size_t& storage) {
storage = value; storage = value;
} }
); );
} }
else if (list_name == "lockfree_list") else if (list_name == "lockfree_list")
{ {
typedef lockfree_list<size_t> isl; typedef lockfree_list<size_t> isl;
elapsed_time = run_test<isl>( elapsed_time = run_test<isl>(
num_messages, num_messages,
num_producers, num_producers,
[] (size_t value) -> isl::node* { [] (size_t value) -> isl::node* {
return new isl::node(value); return new isl::node(value);
}, },
[] (const size_t& value, size_t& storage) { [] (const size_t& value, size_t& storage) {
storage = value; storage = value;
} }
); );
} }
else if (list_name == "blocking_sutter_list") else if (list_name == "blocking_sutter_list")
{ {
elapsed_time = run_test<blocking_sutter_list<size_t>>( elapsed_time = run_test<blocking_sutter_list<size_t>>(
num_messages, num_messages,
num_producers, num_producers,
[] (size_t value) -> size_t* { [] (size_t value) -> size_t* {
return new size_t(value); return new size_t(value);
}, },
[] (size_t* value, size_t& storage) { [] (size_t* value, size_t& storage) {
storage = *value; storage = *value;
delete value; delete value;
} }
); );
} }
else if (list_name == "cached_stack") else if (list_name == "cached_stack")
{ {
elapsed_time = run_test<cached_stack<cs_element>>( elapsed_time = run_test<cached_stack<cs_element>>(
num_messages, num_messages,
num_producers, num_producers,
[] (size_t value) -> cs_element* { [] (size_t value) -> cs_element* {
return new cs_element(value); return new cs_element(value);
}, },
[] (cs_element* e, size_t& storage) { [] (cs_element* e, size_t& storage) {
storage = e->value; storage = e->value;
delete e; delete e;
} }
); );
} }
else if (list_name == "blocking_cached_stack") else if (list_name == "blocking_cached_stack")
{ {
elapsed_time = run_test<blocking_cached_stack<cs_element>>( elapsed_time = run_test<blocking_cached_stack<cs_element>>(
num_messages, num_messages,
num_producers, num_producers,
[] (size_t value) -> cs_element* { [] (size_t value) -> cs_element* {
return new cs_element(value); return new cs_element(value);
}, },
[] (cs_element* e, size_t& storage) { [] (cs_element* e, size_t& storage) {
storage = e->value; storage = e->value;
delete e; delete e;
} }
); );
} }
else if (list_name == "blocking_cached_stack2") else if (list_name == "blocking_cached_stack2")
{ {
elapsed_time = run_test<blocking_cached_stack2<cs_element>>( elapsed_time = run_test<blocking_cached_stack2<cs_element>>(
num_messages, num_messages,
num_producers, num_producers,
[] (size_t value) -> cs_element* { [] (size_t value) -> cs_element* {
return new cs_element(value); return new cs_element(value);
}, },
[] (cs_element* e, size_t& storage) { [] (cs_element* e, size_t& storage) {
storage = e->value; storage = e->value;
delete e; delete e;
} }
); );
} }
else else
{ {
cerr << "unknown list" << endl; cerr << "unknown list" << endl;
usage(); usage();
return -4; return -4;
} }
// build output message // build output message
std::vector<std::pair<std::string, std::string>> replacements = { std::vector<std::pair<std::string, std::string>> replacements = {
{ "$MESSAGES", argv[1] }, { "MESSAGES", argv[1] },
{ "$PRODUCERS", argv[2] }, { "PRODUCERS", argv[2] },
{ "$TIME", boost::lexical_cast<std::string>(elapsed_time) } { "MSG_IN_MILLION", boost::lexical_cast<std::string>(num_messages / 1000000.0) },
}; { "TIME", boost::lexical_cast<std::string>(elapsed_time) }
for (auto i = replacements.begin(); i != replacements.end(); ++i) };
{ for (auto i = replacements.begin(); i != replacements.end(); ++i)
const std::string& needle = i->first; {
const std::string& value = i->second; const std::string& needle = i->first;
std::string::size_type pos = format_string.find(needle); const std::string& value = i->second;
if (pos != std::string::npos) std::string::size_type pos = format_string.find(needle);
{ if (pos != std::string::npos)
format_string.replace(pos, pos + needle.size(), value); {
} format_string.replace(pos, pos + needle.size(), value);
} }
cout << format_string << endl; }
// done cout << format_string << endl;
return 0; // done
return 0;
} }
...@@ -59,7 +59,7 @@ class sutter_list ...@@ -59,7 +59,7 @@ class sutter_list
// acquire exclusivity // acquire exclusivity
while (m_producer_lock.exchange(true)) while (m_producer_lock.exchange(true))
{ {
boost::this_thread::yield(); std::this_thread::yield();
} }
// publish & swing last forward // publish & swing last forward
m_last->next = tmp; m_last->next = tmp;
...@@ -96,7 +96,7 @@ class sutter_list ...@@ -96,7 +96,7 @@ class sutter_list
T* result = try_pop(); T* result = try_pop();
while (!result) while (!result)
{ {
boost::this_thread::yield(); std::this_thread::yield();
result = try_pop(); result = try_pop();
} }
return result; return result;
......
...@@ -9,6 +9,19 @@ ...@@ -9,6 +9,19 @@
#include "cppa/util/single_reader_queue.hpp" #include "cppa/util/single_reader_queue.hpp"
//#define DEBUG_RESULTS
// "config"
namespace {
const size_t slave_messages = 1000000;
const size_t trials = 10;
} // namespace <anonymous>
using cppa::util::single_reader_queue; using cppa::util::single_reader_queue;
using std::cout; using std::cout;
...@@ -107,7 +120,7 @@ class locked_queue ...@@ -107,7 +120,7 @@ class locked_queue
} }
} }
void push(element_type* new_element) void push_back(element_type* new_element)
{ {
lock_type guard(m_mtx); lock_type guard(m_mtx);
if (m_pub.empty()) if (m_pub.empty())
...@@ -150,43 +163,75 @@ void slave(Queue& q, size_t from, size_t to) ...@@ -150,43 +163,75 @@ void slave(Queue& q, size_t from, size_t to)
} }
template<typename Queue, size_t num_slaves, size_t num_slave_msgs> template<typename Queue, size_t num_slaves, size_t num_slave_msgs>
void master(Queue& q) void master()
{ {
static const size_t num_msgs = (num_slaves) * (num_slave_msgs); static const size_t num_msgs = (num_slaves) * (num_slave_msgs);
static const size_t calc_result = ((num_msgs)*(num_msgs + 1)) / 2; static const size_t calc_result = ((num_msgs)*(num_msgs + 1)) / 2;
boost::timer t0;
for (size_t i = 0; i < num_slaves; ++i) //cout << num_slaves << " workers; running test";
{ //cout.flush();
size_t from = (i * num_slave_msgs) + 1;
size_t to = from + num_slave_msgs; double elapsed[trials];
boost::thread(slave<Queue>, boost::ref(q), from, to).detach();
} for (size_t i = 0; i < trials; ++i)
size_t result = 0;
size_t min_val = calc_result;
size_t max_val = 0;
for (size_t i = 0; i < num_msgs; ++i)
{ {
queue_element* e = q.pop();
result += e->value; //cout << " ... " << (i + 1);
min_val = std::min(min_val, e->value); //cout.flush();
max_val = std::max(max_val, e->value);
delete e; Queue q;
boost::timer t0;
for (size_t j = 0; j < num_slaves; ++j)
{
size_t from = (j * num_slave_msgs) + 1;
size_t to = from + num_slave_msgs;
boost::thread(slave<Queue>, boost::ref(q), from, to).detach();
}
size_t result = 0;
# ifdef DEBUG_RESULTS
size_t min_val = calc_result;
size_t max_val = 0;
# endif
for (size_t j = 0; j < num_msgs; ++j)
{
queue_element* e = q.pop();
result += e->value;
# ifdef DEBUG_RESULTS
min_val = std::min(min_val, e->value);
max_val = std::max(max_val, e->value);
# endif
delete e;
}
if (result != calc_result)
{
cerr << "ERROR: result = " << result
<< " (should be: " << calc_result << ")"
# ifdef DEBUG_RESULTS
<< endl << "min: " << min_val
<< endl << "max: " << max_val
# endif
<< endl;
}
elapsed[i] = t0.elapsed();
//cout << t0.elapsed() << " " << num_slaves << endl;
} }
if (result != calc_result) //cout << endl;
double sum = 0;
//cout << "runtimes = { ";
for (size_t i = 0; i < trials; ++i)
{ {
cerr << "ERROR: result = " << result //cout << (i == 0 ? "" : ", ") << elapsed[i];
<< " (should be: " << calc_result << ")" << endl sum += elapsed[i];
<< "min: " << min_val << endl
<< "max: " << max_val << endl;
} }
cout << t0.elapsed() << " " << num_slaves << endl; //cout << " }" << endl;
//cout << "AVG = " << (sum / trials) << endl;
cout << (sum / trials) << " " << num_slaves << endl;
} }
namespace { const size_t slave_messages = 1000000; }
template<size_t Pos, size_t Max, size_t Step, template<size_t Pos, size_t Max, size_t Step,
template<size_t> class Stmt> template<size_t> class Stmt>
struct static_for struct static_for
...@@ -222,8 +267,7 @@ struct test_step ...@@ -222,8 +267,7 @@ struct test_step
template<typename QueueToken> template<typename QueueToken>
static inline void _(QueueToken) static inline void _(QueueToken)
{ {
typename QueueToken::type q; boost::thread t0(master<typename QueueToken::type, NumThreads, slave_messages>);
boost::thread t0(master<typename QueueToken::type, NumThreads, slave_messages>, boost::ref(q));
t0.join(); t0.join();
} }
}; };
...@@ -237,8 +281,13 @@ void test_q_impl() ...@@ -237,8 +281,13 @@ void test_q_impl()
void test__queue_performance() void test__queue_performance()
{ {
cout << "Format: "
"(average value of 10 runs) "
// "(standard deviation) "
"(number of worker threads)"
<< endl;
cout << "locked_queue:" << endl; cout << "locked_queue:" << endl;
// test_q_impl<locked_queue<queue_element>>(); test_q_impl<locked_queue<queue_element>>();
cout << endl; cout << endl;
cout << "single_reader_queue:" << endl; cout << "single_reader_queue:" << endl;
test_q_impl<single_reader_queue<queue_element>>(); test_q_impl<single_reader_queue<queue_element>>();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment