|
Boost : |
From: Howard Hinnant (hinnant_at_[hidden])
Date: 2008-02-09 14:02:43
On Feb 8, 2008, at 6:19 PM, Phil Endecott wrote:
> If you can share the code, I'll see how it performs on my Linux
> systems.
Thanks Phil! Sorry for the delay. I wanted to update my test to use
the latest std::syntax and rerun them again. First, here's the code:
#include <date_time>
#include <mutex>
#include <thread>
#include <iostream>
typedef std::mutex Mutex;
typedef std::unique_lock<Mutex> Lock;
Mutex cout_mutex;
unsigned random_gen(unsigned& random_next)
{
random_next = random_next * 1103515245 + 12345;
return (random_next >> 16) & 0x7FFF;
}
struct Chopstick
{
Chopstick() : random_next_(1) {}
Mutex mut_;
unsigned random_next_;
};
unsigned eat(unsigned i)
{
while (i)
--i;
return i;
}
unsigned eater(int id, Chopstick& left, Chopstick& right)
{
{
std::lock_guard<Mutex> _(cout_mutex);
std::cout << "Thread " << id << " starting\n";
}
unsigned i = 10000000;
unsigned u = 0;
while (--i)
{
Lock l(left.mut_, std::defer_lock);
Lock r(right.mut_, std::defer_lock);
std::lock(l, r);
u += eat(random_gen(left.random_next_) +
random_gen(right.random_next_));
}
{
std::lock_guard<Mutex> _(cout_mutex);
std::cout << "Thread " << id << " ending\n";
}
return u;
}
int main()
{
std::system_time t1 = std::get_system_time();
Chopstick A, B, C, D, E;
std::thread T1(eater, 1, std::ref(A), std::ref(B));
std::thread T2(eater, 2, std::ref(B), std::ref(C));
std::thread T3(eater, 3, std::ref(C), std::ref(D));
std::thread T4(eater, 4, std::ref(D), std::ref(E));
std::thread T5(eater, 5, std::ref(E), std::ref(A));
T1.join();
T2.join();
T3.join();
T4.join();
T5.join();
std::system_time t2 = std::get_system_time();
std::cout << " time = " << (t2-t1).count() / 1.e9 << '\n';
}
If anyone sees any glaring problems with the test, please let me know,
thanks.
Now for results just now run.
Machine: iMac 2.16 GHz Intel Core 2 Duo, OS X 10.5.1.
Lock in order:
template <class L1, class L2>
void
lock(L1& l1, L2& l2)
{
if (l1.mutex() < l2.mutex())
{
unique_lock<L1> u1(l1);
unique_lock<L2> u2(l2);
u1.release();
u2.release();
}
else
{
unique_lock<L2> u2(l2);
unique_lock<L1> u1(l1);
u1.release();
u2.release();
}
}
$./a.out
Thread 1 starting
Thread 2 starting
Thread 3 starting
Thread 4 starting
Thread 5 starting
Thread 1 ending
Thread 2 ending
Thread 3 ending
Thread 5 ending
Thread 4 ending
time = 62.0437
$./a.out
Thread 1 starting
Thread 2 starting
Thread 3 starting
Thread 4 starting
Thread 5 starting
Thread 1 ending
Thread 2 ending
Thread 3 ending
Thread 5 ending
Thread 4 ending
time = 145.683
$./a.out
Thread 1 starting
Thread 2 starting
Thread 3 starting
Thread 4 starting
Thread 5 starting
Thread 1 ending
Thread 2 ending
Thread 3 ending
Thread 5 ending
Thread 4 ending
time = 157.347
Interesting side note: During the first run my disk backup utility
kicked in. This extra competition for resources actually made the
test case run faster!
Try/backoff/without yield
template <class L1, class L2>
void
lock(L1& l1, L2& l2)
{
while (true)
{
{
unique_lock<L1> u1(l1);
if (l2.try_lock())
{
u1.release();
break;
}
}
// std::this_thread::yield();
{
unique_lock<L2> u2(l2);
if (l1.try_lock())
{
u2.release();
break;
}
}
// std::this_thread::yield();
}
}
$./a.out
Thread 1 starting
Thread 2 starting
Thread 3 starting
Thread 4 starting
Thread 5 starting
Thread 4 ending
Thread 5 ending
Thread 1 ending
Thread 2 ending
Thread 3 ending
time = 32.3726
$./a.out
Thread 1 starting
Thread 2 starting
Thread 3 starting
Thread 4 starting
Thread 5 starting
Thread 5 ending
Thread 4 ending
Thread 1 ending
Thread 3 ending
Thread 2 ending
time = 29.4687
$./a.out
Thread 1 starting
Thread 2 starting
Thread 3 starting
Thread 4 starting
Thread 5 starting
Thread 5 ending
Thread 1 ending
Thread 4 ending
Thread 3 ending
Thread 2 ending
time = 56.226
I have no explanation for the longer time in the third run.
Try/backoff/yield
template <class L1, class L2>
void
lock(L1& l1, L2& l2)
{
while (true)
{
{
unique_lock<L1> u1(l1);
if (l2.try_lock())
{
u1.release();
break;
}
}
std::this_thread::yield();
{
unique_lock<L2> u2(l2);
if (l1.try_lock())
{
u2.release();
break;
}
}
std::this_thread::yield();
}
}
$./a.out
Thread 1 starting
Thread 2 starting
Thread 3 starting
Thread 4 starting
Thread 5 starting
Thread 4 ending
Thread 5 ending
Thread 1 ending
Thread 3 ending
Thread 2 ending
time = 11.216
$./a.out
Thread 1 starting
Thread 2 starting
Thread 3 starting
Thread 4 starting
Thread 5 starting
Thread 4 ending
Thread 5 ending
Thread 1 ending
Thread 3 ending
Thread 2 ending
time = 10.6229
$./a.out
Thread 1 starting
Thread 2 starting
Thread 3 starting
Thread 4 starting
Thread 5 starting
Thread 4 ending
Thread 5 ending
Thread 1 ending
Thread 3 ending
Thread 2 ending
time = 11.3284
-Howard
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk