blob: c6ca40e897056cd34b2b1dbecd861e3f7e234094 [file]
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
/**@file
* Compare alternative implementations for BlockingQueue.
*/
#include "qpid/sys/BlockingQueue.h"
#include "qpid/sys/Thread.h"
#include "qpid/sys/Monitor.h"
#include "qpid/sys/Runnable.h"
#include "qpid/sys/Time.h"
#include <boost/test/test_tools.hpp>
#include <boost/bind.hpp>
#include <deque>
#include <vector>
#include <iostream>
#include "time.h"
using namespace qpid::sys;
using namespace std;
template <class T> class DualVectorDualLockQueue {
public:
/** Optionally specify initial capacity of the queue to minimize
* re-allocation.
*/
DualVectorDualLockQueue(size_t capacity=16) {
pushVec.reserve(capacity);
popVec.reserve(capacity);
popIter = popVec.end();
}
/** Push a data item onto the back of the queue */
void push(const T& data) {
Mutex::ScopedLock l(pushLock);
pushVec.push_back(data);
}
/** If the queue is non-empty, pop the front item into data and
* return true. If the queue is empty, return false
*/
bool tryPop(T& data) {
Mutex::ScopedLock l(popLock);
if (popIter == popVec.end()) {
popVec.clear();
Mutex::ScopedLock l(pushLock);
pushVec.swap(popVec);
popIter = popVec.begin();
}
if (popIter == popVec.end())
return false;
else {
data = *popIter++;
return true;
}
}
private:
Mutex pushLock, popLock;
std::vector<T> pushVec, popVec;
typename std::vector<T>::iterator popIter;
};
template <class T> struct LockedDequeQueue : public BlockingQueue<T> {
/** size_t ignored, can't pre-allocate space in a dequeue */
LockedDequeQueue(size_t=0) {};
};
// ================ Test code.
/** Pause by sleeping */
void nsleep(const Duration& delay) {
static Monitor m;
AbsTime stop(now(), delay);
while (now() < stop)
m.wait(stop);
}
/** Pause by spinning */
void nspin(const Duration& delay) {
AbsTime stop(now(), delay);
while (now() < stop)
;
}
/** Unlocked fake queue for comparison */
struct NullQueue {
NullQueue(int items=0) : npush(items), npop(items) {}
void push(int) { --npush; }
bool tryPop(int& n) {
if (npop == 0)
return false;
else {
n=npop--;
return true;
}
}
volatile int npush, npop;
};
// Global test parameters.
int items;
Duration delay(0);
boost::function<void()> npause;
template <class Q>
struct Pusher : public Runnable {
Pusher(Q& q) : queue(q) {}
void run() {
for (int i=items; i > 0; i--) {
queue.push(i);
npause();
}
}
Q& queue;
};
template <class Q>
struct Popper : public Runnable {
Popper(Q& q) : queue(q) {}
void run() {
for (int i=items; i > 0; i--) {
int n;
if (queue.tryPop(n))
BOOST_REQUIRE_EQUAL(i,n);
npause();
}
}
Q& queue;
};
ostream& operator<<(ostream& out, const Duration& d) {
return out << double(d)/TIME_MSEC << " msecs";
}
void report(const char* s, const Duration &d) {
cout << s << ": " << d
<< " (" << (double(items)*TIME_SEC)/d << " push-pops/sec" << ")"
<< endl;
}
template <class Q, class PusherT=Pusher<Q>, class PopperT=Popper<Q> >
struct Timer {
static Duration time() {
cout << endl << "==" << typeid(Q).name() << endl;
Q queue(items);
PusherT pusher(queue);
PopperT popper(queue);
// Serial
AbsTime start=now();
pusher.run();
popper.run();
Duration serial(start,now());
report ("Serial", serial);
// Concurrent
start=now();
Thread pushThread(pusher);
Thread popThread(popper);
pushThread.join();
popThread.join();
Duration concurrent(start,now());
report ("Concurrent", concurrent);
cout << "Serial/concurrent: " << double(serial)/concurrent << endl;
return concurrent;
}
};
int test_main(int argc, char** argv) {
items = (argc > 1) ? atoi(argv[1]) : 250*1000;
delay = (argc > 2) ? atoi(argv[2]) : 4*1000;
npause=boost::bind(nspin, delay);
cout << "Push/pop " << items << " items, delay=" << delay << endl;
Timer<NullQueue>::time();
Duration dv = Timer<DualVectorDualLockQueue<int> >::time();
Duration d = Timer<LockedDequeQueue<int> >::time();
cout << endl;
cout << "Ratio deque/dual vector=" << double(d)/dv << endl;
return 0;
}
// namespace