DI: benchmark naive lock protected access

...which gives us the dramatic numbers we'd expect.
Especially the multithreaded variant contends drastically
This commit is contained in:
Fischlurch 2018-03-24 09:03:14 +01:00
parent d2dababf5c
commit ff256d9e57
4 changed files with 12 additions and 9 deletions

View file

@ -102,11 +102,11 @@ main (int, char**)
Depend<BlackHoleService> mystery;
cout << microbenchmark<8> ([&]()
cout << microbenchmark<1> ([&]()
{
0 == mystery().readMe();
}
,200000000)
,50000000)
<< endl;
LifecycleHook::trigger (ON_GLOBAL_SHUTDOWN);

View file

@ -74,7 +74,7 @@ namespace lib {
struct DependInject
{
using Factory = typename Depend<SRV>::Factory;
using Lock = typename Depend<SRV>::Lock;
/** configure dependency-injection for type SRV to build a subclass singleton
* @tparam SUB concrete subclass type to build on demand when invoking `Depend<SRV>`
@ -259,7 +259,7 @@ namespace lib {
static void
installFactory (Factory&& otherFac)
{
ClassLock<SRV> guard;
Lock guard;
if (Depend<SRV>::instance)
throw error::Logic("Attempt to reconfigure dependency injection after the fact. "
"The previously installed factory (typically Singleton) was already used."
@ -270,7 +270,7 @@ namespace lib {
static void
temporarilyInstallAlternateFactory (SRV*& stashInstance, Factory& stashFac, Factory&& newFac)
{
ClassLock<SRV> guard;
Lock guard;
stashFac = move(Depend<SRV>::factory);
stashInstance = Depend<SRV>::instance;
Depend<SRV>::factory = move(newFac);
@ -280,7 +280,7 @@ namespace lib {
static void
restoreOriginalFactory (SRV*& stashInstance, Factory& stashFac)
{
ClassLock<SRV> guard;
Lock guard;
Depend<SRV>::factory = move(stashFac);
Depend<SRV>::instance = stashInstance;
}
@ -288,7 +288,7 @@ namespace lib {
static void
activateServiceAccess (SRV& newInstance)
{
ClassLock<SRV> guard;
Lock guard;
if (Depend<SRV>::instance)
throw error::Logic("Attempt to activate an external service implementation, "
"but another instance has already been dependency-injected."
@ -300,7 +300,7 @@ namespace lib {
static void
deactivateServiceAccess()
{
ClassLock<SRV> guard;
Lock guard;
Depend<SRV>::instance = nullptr;
Depend<SRV>::factory = Depend<SRV>::disabledFactory;
}

View file

@ -170,6 +170,7 @@ namespace lib {
class Depend
{
using Factory = std::function<SRV*()>;
using Lock = ClassLock<SRV, NonrecursiveLock_NoWait>;
static SRV* instance;
static Factory factory;
@ -188,6 +189,7 @@ namespace lib {
SRV&
operator() ()
{
Lock guard;
if (!instance)
retrieveInstance();
// ENSURE (instance);
@ -198,7 +200,7 @@ namespace lib {
void
retrieveInstance()
{
// ClassLock<SRV> guard;
// Lock guard;
// if (!instance)
// {

View file

@ -1984,6 +1984,7 @@ The following table lists averaged results in relative numbers, in relation to a
|direct invoke shared local object | 15.13| 16.30| ''1.00''| 1.59|
|invoke existing object through unique_ptr | 60.76| 63.20| 1.20| 1.64|
|lazy init unprotected (not threadsafe) | 27.29| 26.57| 2.37| 3.58|
|lazy init always mutex protected | 179,62| 10917.18| 86.40| 6661.23|
These benchmarks used a dummy service class holding a volatile int, initialised to a random value. The complete code was visible to the compiler and thus eligible for inlining. After accessing this dummy object through the means listed in the table, the benchmarked code retrieved this value repeatedly and compared it to zero. The concurrent measurement used 8 threads (number of cores); as expected, the unprotected lazy init crashed several times randomly during those tests.
</pre>