DI: prepare benchmark of reference cases

_not_ using the dependency factory, rather direct access

 - to a shared object in the enclosing stack frame
 - to a heap allocated existing object accessed through uniqe_ptr
This commit is contained in:
Fischlurch 2018-03-24 07:48:59 +01:00
parent 685a9b84ee
commit 69f21d96af
3 changed files with 39 additions and 31 deletions

View file

@ -84,31 +84,36 @@ using lumiera::ON_GLOBAL_SHUTDOWN;
///////////////////////////////////////////////////////Usage
class BlackHoleService
: util::NonCopyable
{
volatile int theHole_ = rand() % 1000;
public:
int readMe() { return theHole_; }
};
int
main (int, char**)
{
std::srand(std::time(nullptr));
LifecycleHook::trigger (ON_GLOBAL_INIT);
// DependInject<long>::useSingleton ([&] { return "long{rand() % 100}"; });
// DependInject<long>::Local<std::string> dummy ([&]{ return new long{rand() % 100}; });
// Depend<BlackHoleService> mystery;
std::unique_ptr<BlackHoleService> mystery{new BlackHoleService};
BlackHoleService mist;
volatile int blackHole{0};
cout << "pling..."<<endl;
cout << "plong..."<< microbenchmark<8> ([&]()
{
//volatile int dummy =0;
//dummy == 0;
//++dummy;
blackHole == 0;
//++blackHole;
})
<< endl;
cout << "........"<< blackHole/8<<endl;
cout << microbenchmark<8> ([&]()
{
0 == mystery->readMe();
//0 == mist.readMe();
}
,300000000)
<< endl;
LifecycleHook::trigger (ON_GLOBAL_SHUTDOWN);
cout << "\n.gulp.\n";
// cout << "\n.gulp.\n";
return 0;
}

View file

@ -64,7 +64,7 @@ namespace lib {
namespace test{
namespace {
constexpr size_t NUM_MEASUREMENTS = 10000000;
constexpr size_t DEFAULT_RUNS = 10000000;
constexpr double SCALE = 1e6; // Results are in µ sec
}
@ -85,7 +85,7 @@ namespace test{
*/
template<size_t nThreads, class FUN>
inline double
microbenchmark(FUN const& subject)
microbenchmark(FUN const& subject, const size_t nRepeat = DEFAULT_RUNS)
{
using backend::ThreadJoinable;
using std::chrono::system_clock;
@ -95,16 +95,16 @@ namespace test{
struct Thread
: ThreadJoinable
{
Thread(FUN const& subject)
Thread(FUN const& subject, size_t loopCnt)
: ThreadJoinable("Micro-Benchmark"
,[subject, this]() // local copy of the test-subject-Functor
{
syncPoint(); // block until all threads are ready
auto start = system_clock::now();
for (size_t i=0; i < NUM_MEASUREMENTS; ++i)
subject();
duration = system_clock::now () - start;
})
,[=]() // local copy of the test-subject-Functor
{
syncPoint(); // block until all threads are ready
auto start = system_clock::now();
for (size_t i=0; i < loopCnt; ++i)
subject();
duration = system_clock::now () - start;
})
{ }
/** measured time within thread */
Dur duration{};
@ -113,7 +113,7 @@ namespace test{
std::vector<Thread> threads;
threads.reserve(nThreads);
for (size_t n=0; n<nThreads; ++n) // create test threads
threads.emplace_back (subject);
threads.emplace_back (subject, nRepeat);
for (auto& thread : threads)
thread.sync(); // start timing measurement
@ -125,7 +125,7 @@ namespace test{
sumDuration += thread.duration;
}
return sumDuration.count() / (nThreads * NUM_MEASUREMENTS) * SCALE;
return sumDuration.count() / (nThreads * nRepeat) * SCALE;
}

View file

@ -1927,7 +1927,7 @@ As we don't have a Prolog interpreter on board yet, we utilize a mock store with
{{{default(Obj)}}} is a predicate expressing that the object {{{Obj}}} can be considered the default setup under the given conditions. Using the //default// can be considered as a shortcut for actually finding an exact and unique solution. The latter would require to specify all sorts of detailed properties up to the point where only one single object can satisfy all conditions. On the other hand, leaving some properties unspecified would yield a set of solutions (and the user code issuing the query had to provide means for selecting one solution from this set). Just falling back on the //default// means that the user code actually doesn't care for any additional properties (as long as the properties he //does// care for are satisfied). Nothing is said specifically on //how//&amp;nbsp; this default gets configured; actually there can be rules //somewhere,// and, additionally, anything encountered once while asking for a default can be re-used as default under similar circumstances.
&amp;rarr; [[implementing defaults|DefaultsImplementation]]</pre>
</div>
<div title="DependencyFactory" creator="Ichthyostega" modifier="Ichthyostega" created="201803110155" modified="201803232220" tags="def Concepts draft" changecount="23">
<div title="DependencyFactory" creator="Ichthyostega" modifier="Ichthyostega" created="201803110155" modified="201803240643" tags="def Concepts draft" changecount="35">
<pre>//Access point to dependencies by-name.//
In the Lumiera code base, we refrain from building or using a full-blown Dependency Injection Container. A lot of FUD has been spread regarding Dependency Injection and Singletons, to the point that a majority of developers confuses and conflates the ~Inversion-of-Control principle (which is essential) with the use of a ~DI-Container. Today, you can not even mention the word &quot;Singleton&quot; without everyone yelling out &quot;Evil! Evil!&quot; -- while most of these people just feel comfortable living in the metadata hell.
@ -1978,9 +1978,12 @@ We acknowledge that such a dependency or service will be accessed frequently and
Our requirements on (optional) reconfigurability have some impact on the implementation technique though, since we need access to the instance pointer for individual service types. This basically rules out //Meyers Singleton// -- and so the adequate implementation technique for our usage pattern is //Double Checked Locking.// In the past, there was much debate about DCL being broken -- which indeed was true when //assuming full portability and arbitrary target platform.// Since our focus is primarily on ~PC-with-Linux systems, this argument seems rather theoretical though, since the x86/64 platform is known to employ rather strong memory and cache coherency constraints. With the advent of ARM systems, the situation has changed however. Anyway, since C++11 there is a portable solution for writing a correct DCL implementation, based on {{{std::atomic}}}.
To give some idea of the rough proportions of performance impact, in 2018 we conducted some micro benchmarks (using a 8 core AMD 64bit processor running Debian/Jessie building with GCC 4.9)
The following table lists averaged results in relative numbers
The following table lists averaged results in relative numbers, in relation to a single threaded direct non virtual member function invocation
| !Access Technique |&gt;| !development |&gt;| !optimised |
|~| single threaded|multithreaded | single threaded|multithreaded |
|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|
</pre>
</div>
<div title="DesignDecisions" modifier="Ichthyostega" created="200801062209" modified="201505310104" tags="decision design discuss Concepts" changecount="5">