Re: Why is java consumer/producer so much faster than C++
On 24/07/2012 21:45, Melzzzzz wrote:
This is version with array (identical implementation
as ArrayBlockingQueue in Java), instead of deque (I
used atomic type for count_). This executes about same speed as Java
as atomic type slows it down a bit.
Sadly, no noticing difference between deque and array.
Right, it may well depend on how smart the std::deque::allocator_type is.
Below is a version of the BlockingQueue using spin locks. Compared to
the original version in Java it is about 2.5 times faster on my
platform, provided that you remove std::rand() from the test, as it
seems to be a real bottleneck and does not give any precious
informations about the efficiency of the Queue (which is the thing to
measure after all).
Let me know your results.
---
#include <thread>
#include <cstdlib>
#include <functional>
#include <atomic>
#include <memory>
#include <type_traits>
class spinlock_mutex {
std::atomic_flag flag = ATOMIC_FLAG_INIT;
public:
void lock() {
while (flag.test_and_set(std::memory_order_acquire)) {
std::this_thread::yield();
};
}
void unlock() {
flag.clear(std::memory_order_release);
}
};
template <class T, class A = std::allocator<T >>
class BlockingQueue {
const std::size_t cap_;
A alloc_;
T* queue_;
std::size_t n = 0, b = 0, f = 0;
spinlock_mutex m_;
public:
BlockingQueue(const std::size_t cap, const A& alloc = A())
: cap_(cap), alloc_(alloc), queue_(alloc_.allocate(cap)) { }
~BlockingQueue() {
alloc_.deallocate(queue_, cap_);
}
static_assert(std::is_nothrow_move_constructible<T>::value,
"BlockingQueue cannot be exception-safe");
void put(const T t) {
for (;;) {
m_.lock();
if (n < cap_)
break;
m_.unlock();
}
alloc_.construct(&queue_[f], std::move(t));
f = ++f % cap_;
++n;
m_.unlock();
}
T take() {
for (;;) {
m_.lock();
if (n > 0)
break;
m_.unlock();
}
const T t = std::move(queue_[b]);
alloc_.destroy(&queue_[b]);
b = ++b % cap_;
--n;
m_.unlock();
return t;
}
};
int main() {
BlockingQueue<int> produced(100000);
const int nitems = 100000000;
std::srand(12345);
std::function<void() > f_prod = [&](){
int i = nitems;
while (i-- > 0) {
produced.put(i);
}
};
std::thread producer1(f_prod);
std::function<void() > f_cons = [&](){
const int size = 10000;
int arr[size];
int i = nitems;
while (i-- > 0) {
arr[std::rand() % size] = produced.take();
}
};
std::thread consumer1(f_cons);
producer1.join();
consumer1.join();
}