Bonita Montero
2024-07-19 16:26:26 UTC
#include <iostream>
#include <thread>
#include <vector>
#include <mutex>
#include <condition_variable>
#include <sys/resource.h>
using namespace std;
int main( int argc, char ** )
{
constexpr size_t ROUNDS = 10'000;
bool outside = true;
struct state
{
bool signalled = false;
mutex mtx;
condition_variable cv;
} a, b;
a.signalled = true;
int64_t last;
do
{
atomic_uint64_t sumSwitches( 0 );
auto thr = [&]( state &me, state &you )
{
auto switches = [&]()
{
rusage ru;
if( getrusage( RUSAGE_THREAD, &ru ) )
terminate();
return ru.ru_nvcsw;
};
uint64_t before = switches();
for( size_t r = ROUNDS; r--; )
{
unique_lock myLock( me.mtx );
while( !me.signalled )
me.cv.wait( myLock );
me.signalled = false;
myLock.unlock();
unique_lock yoursLock( you.mtx );
you.signalled = true;
if( !outside )
you.cv.notify_one();
yoursLock.unlock();
if( outside )
you.cv.notify_one();
}
sumSwitches.fetch_add( switches() - before, memory_order_relaxed );
};
vector<jthread> threads;
threads.emplace_back( thr, ref( a ), ref( b ) );
threads.emplace_back( thr, ref( b ), ref( a ) );
threads.resize( 0 );
int64_t ss = sumSwitches.load( memory_order_relaxed );
cout << (outside ? "out" : "in") << "side: " << ss;
if( outside )
last = ss;
else
cout << " " << (100.0 * (double)ss / (double)last * 100 + 0.5) / 100
<< "%";
cout << endl;
} while( !(outside = !outside) );
return 0;
}
outside: 19937
inside: 20030 100.471%
Result: locking from inside and outside is as efficient with
Linux and glibc, only half a percent more contextswitches
with locking from inside.
#include <thread>
#include <vector>
#include <mutex>
#include <condition_variable>
#include <sys/resource.h>
using namespace std;
int main( int argc, char ** )
{
constexpr size_t ROUNDS = 10'000;
bool outside = true;
struct state
{
bool signalled = false;
mutex mtx;
condition_variable cv;
} a, b;
a.signalled = true;
int64_t last;
do
{
atomic_uint64_t sumSwitches( 0 );
auto thr = [&]( state &me, state &you )
{
auto switches = [&]()
{
rusage ru;
if( getrusage( RUSAGE_THREAD, &ru ) )
terminate();
return ru.ru_nvcsw;
};
uint64_t before = switches();
for( size_t r = ROUNDS; r--; )
{
unique_lock myLock( me.mtx );
while( !me.signalled )
me.cv.wait( myLock );
me.signalled = false;
myLock.unlock();
unique_lock yoursLock( you.mtx );
you.signalled = true;
if( !outside )
you.cv.notify_one();
yoursLock.unlock();
if( outside )
you.cv.notify_one();
}
sumSwitches.fetch_add( switches() - before, memory_order_relaxed );
};
vector<jthread> threads;
threads.emplace_back( thr, ref( a ), ref( b ) );
threads.emplace_back( thr, ref( b ), ref( a ) );
threads.resize( 0 );
int64_t ss = sumSwitches.load( memory_order_relaxed );
cout << (outside ? "out" : "in") << "side: " << ss;
if( outside )
last = ss;
else
cout << " " << (100.0 * (double)ss / (double)last * 100 + 0.5) / 100
<< "%";
cout << endl;
} while( !(outside = !outside) );
return 0;
}
outside: 19937
inside: 20030 100.471%
Result: locking from inside and outside is as efficient with
Linux and glibc, only half a percent more contextswitches
with locking from inside.