2009-09-26 23:00:55 +02:00
|
|
|
|
/*
|
|
|
|
|
|
TypedCounter(Test) - managing a set of type based contexts
|
2010-12-17 23:28:49 +01:00
|
|
|
|
|
Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
* there is no entity "Lumiera.org" which holds any copyrights
* Lumiera source code is provided under the GPL Version 2+
== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''
The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!
The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
|
|
|
|
Copyright (C)
|
|
|
|
|
|
2009, Hermann Vosseler <Ichthyostega@web.de>
|
2010-12-17 23:28:49 +01:00
|
|
|
|
|
Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
* there is no entity "Lumiera.org" which holds any copyrights
* Lumiera source code is provided under the GPL Version 2+
== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''
The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!
The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
|
|
|
|
**Lumiera** is free software; you can redistribute it and/or modify it
|
|
|
|
|
|
under the terms of the GNU General Public License as published by the
|
|
|
|
|
|
Free Software Foundation; either version 2 of the License, or (at your
|
|
|
|
|
|
option) any later version. See the file COPYING for further details.
|
2010-12-17 23:28:49 +01:00
|
|
|
|
|
Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
* there is no entity "Lumiera.org" which holds any copyrights
* Lumiera source code is provided under the GPL Version 2+
== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''
The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!
The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
|
|
|
|
* *****************************************************************/
|
2009-09-26 23:00:55 +02:00
|
|
|
|
|
|
|
|
|
|
|
2009-09-27 17:29:05 +02:00
|
|
|
|
/** @file typed-counter-test.cpp
|
|
|
|
|
|
** Stress test to verify type-based contexts.
|
|
|
|
|
|
** Besides a simple usage (unit) test, this test performs a massively multithreaded
|
|
|
|
|
|
** test of the type-based contexts, through use of the TypedCounter. The idea behind
|
|
|
|
|
|
** this facility is to provide a context, in which type-IDs can be allocated. In the
|
|
|
|
|
|
** case of the TypedCounter, these type-IDs are used to index into a vector of counters,
|
|
|
|
|
|
** this way allowing to access a counter for a given type.
|
2017-01-14 03:07:48 +01:00
|
|
|
|
**
|
2009-09-28 17:54:50 +02:00
|
|
|
|
** This test builds several "families", each sharing a TypedCounter. Each of these
|
2009-09-27 17:29:05 +02:00
|
|
|
|
** families runs a set of member threads, which concurrently access the TypedCounter of
|
|
|
|
|
|
** this family. After waiting for all threads to finish, we compare the checksum built
|
|
|
|
|
|
** within the target objects with the checksum collected through the TypedCounters.
|
|
|
|
|
|
**
|
|
|
|
|
|
** @see thread-wrapper-test.cpp
|
|
|
|
|
|
** @see thread-wrapper-join-test.cpp
|
|
|
|
|
|
**
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2009-09-26 23:00:55 +02:00
|
|
|
|
#include "lib/test/run.hpp"
|
2009-09-27 02:46:26 +02:00
|
|
|
|
#include "lib/typed-counter.hpp"
|
2023-10-04 22:41:00 +02:00
|
|
|
|
#include "lib/test/microbenchmark.hpp"
|
2024-11-13 02:23:23 +01:00
|
|
|
|
#include "lib/random.hpp"
|
2023-10-04 22:41:00 +02:00
|
|
|
|
#include "lib/util.hpp"
|
2009-09-26 23:00:55 +02:00
|
|
|
|
|
2024-11-17 19:45:41 +01:00
|
|
|
|
#include <cstdlib>
|
2023-10-04 22:41:00 +02:00
|
|
|
|
#include <utility>
|
|
|
|
|
|
#include <array>
|
2009-09-26 23:00:55 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace lib {
|
|
|
|
|
|
namespace test{
|
2009-09-27 17:29:05 +02:00
|
|
|
|
|
|
|
|
|
|
using util::isnil;
|
2024-11-13 02:23:23 +01:00
|
|
|
|
using lib::rani;
|
2009-09-26 23:00:55 +02:00
|
|
|
|
|
|
|
|
|
|
|
2023-10-04 22:41:00 +02:00
|
|
|
|
namespace { // test parametrisation...
|
|
|
|
|
|
const uint MAX_INDEX = 10; ///< number of distinct types / counters
|
|
|
|
|
|
const uint NUM_THREADS = 100; ///< number of threads to run in parallel
|
|
|
|
|
|
const uint NUM_ITERATIONS = 10000; ///< number of repeated random accesses per Thread
|
2009-09-26 23:00:55 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-10-24 23:06:36 +02:00
|
|
|
|
/***********************************************************************************//**
|
2023-10-04 22:41:00 +02:00
|
|
|
|
* @test verify the TypedCounter, which allows to maintain a counter-per-type.
|
|
|
|
|
|
* - demonstrate behaviour
|
|
|
|
|
|
* - concurrent test
|
2009-09-27 02:46:26 +02:00
|
|
|
|
* @see TypedAllocationManager
|
|
|
|
|
|
* @see typed-counter.hpp
|
2009-09-26 23:00:55 +02:00
|
|
|
|
*/
|
|
|
|
|
|
class TypedCounter_test : public Test
|
|
|
|
|
|
{
|
|
|
|
|
|
|
2009-09-27 02:46:26 +02:00
|
|
|
|
void
|
2017-01-14 03:07:48 +01:00
|
|
|
|
run (Arg)
|
2009-09-27 17:29:05 +02:00
|
|
|
|
{
|
|
|
|
|
|
simpleUsageTest();
|
|
|
|
|
|
tortureTest();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2023-10-04 22:41:00 +02:00
|
|
|
|
simpleUsageTest()
|
2009-09-27 17:29:05 +02:00
|
|
|
|
{
|
|
|
|
|
|
TypedCounter myCounter;
|
2010-12-10 02:55:40 +01:00
|
|
|
|
CHECK (isnil (myCounter));
|
|
|
|
|
|
CHECK (0==myCounter.size());
|
2009-09-27 17:29:05 +02:00
|
|
|
|
|
2010-12-10 02:55:40 +01:00
|
|
|
|
CHECK (0 == myCounter.get<short>());
|
2015-11-01 23:45:06 +01:00
|
|
|
|
CHECK (0 < myCounter.size());
|
|
|
|
|
|
// probably greater than 1;
|
|
|
|
|
|
// other parts of the application allocate type-IDs as well
|
2009-09-27 17:29:05 +02:00
|
|
|
|
|
2015-11-01 23:45:06 +01:00
|
|
|
|
// now allocate a counter for a type not seen yet
|
|
|
|
|
|
struct X { };
|
|
|
|
|
|
struct U { };
|
2009-09-27 17:29:05 +02:00
|
|
|
|
|
2015-11-01 23:45:06 +01:00
|
|
|
|
CHECK (0 == myCounter.get<X>());
|
|
|
|
|
|
size_t sX = myCounter.size();
|
2009-09-27 17:29:05 +02:00
|
|
|
|
|
2015-11-01 23:45:06 +01:00
|
|
|
|
CHECK (0 == myCounter.get<U>());
|
|
|
|
|
|
CHECK (sX + 1 == myCounter.size());
|
|
|
|
|
|
CHECK (0 == myCounter.get<X>());
|
|
|
|
|
|
CHECK (sX + 1 == myCounter.size());
|
2009-09-27 17:29:05 +02:00
|
|
|
|
|
2015-11-01 23:45:06 +01:00
|
|
|
|
CHECK (-1 == myCounter.dec<X>());
|
|
|
|
|
|
CHECK (-2 == myCounter.dec<X>());
|
|
|
|
|
|
CHECK (+1 == myCounter.inc<U>());
|
2009-09-27 17:29:05 +02:00
|
|
|
|
|
2015-11-01 23:45:06 +01:00
|
|
|
|
CHECK (-2 == myCounter.get<X>());
|
|
|
|
|
|
CHECK (+1 == myCounter.get<U>());
|
|
|
|
|
|
|
|
|
|
|
|
// each new type has gotten a new "slot" (i.e. a distinct type-ID)
|
|
|
|
|
|
IxID typeID_short = TypedContext<TypedCounter>::ID<short>::get();
|
|
|
|
|
|
IxID typeID_X = TypedContext<TypedCounter>::ID<X>::get();
|
|
|
|
|
|
IxID typeID_U = TypedContext<TypedCounter>::ID<U>::get();
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (0 < typeID_short);
|
|
|
|
|
|
CHECK (0 < typeID_X);
|
|
|
|
|
|
CHECK (0 < typeID_U);
|
|
|
|
|
|
CHECK (typeID_short < typeID_X);
|
|
|
|
|
|
CHECK (typeID_X < typeID_U);
|
|
|
|
|
|
// type-IDs are allocated in the order of first usage
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (sX + 1 == myCounter.size());
|
2009-09-27 17:29:05 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-10-04 22:41:00 +02:00
|
|
|
|
|
|
|
|
|
|
/** parametrised marker type to designate a counter to be incremented */
|
|
|
|
|
|
template<size_t i>
|
|
|
|
|
|
struct Dummy { };
|
|
|
|
|
|
|
|
|
|
|
|
template<size_t i>
|
|
|
|
|
|
static void
|
|
|
|
|
|
increment (TypedCounter& counter)
|
|
|
|
|
|
{
|
|
|
|
|
|
counter.inc<Dummy<i>>();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Helper for #tortureTest():
|
|
|
|
|
|
* Build a table of functors, where the i-th entry invokes the function
|
2025-04-26 23:59:29 +02:00
|
|
|
|
* `increment<i>()`, which leads to incrementing the counter for `Dummy<i>`.
|
2023-10-04 22:41:00 +02:00
|
|
|
|
*/
|
|
|
|
|
|
template<size_t...I>
|
|
|
|
|
|
static auto
|
|
|
|
|
|
buildOperatorsTable(std::index_sequence<I...>)
|
|
|
|
|
|
{
|
|
|
|
|
|
using Operator = void(*)(TypedCounter&);
|
|
|
|
|
|
return std::array<Operator, MAX_INDEX>{increment<I>...};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template<size_t...I>
|
|
|
|
|
|
static size_t
|
|
|
|
|
|
sumAllCounters(TypedCounter& counter, std::index_sequence<I...>)
|
|
|
|
|
|
{
|
|
|
|
|
|
return (counter.get<Dummy<I>>() + ... );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @test verify TypedCounter concurrency safety
|
|
|
|
|
|
* - use a set of types `Dummy<i>` to access a corresponding counter
|
|
|
|
|
|
* - run a large number of threads in parallel, each incrementing
|
|
|
|
|
|
* a randomly picked counter; this is achieved by using a table
|
|
|
|
|
|
* of »increment operators«, where each one is tied to a specific
|
2025-04-26 23:59:29 +02:00
|
|
|
|
* `Dummy<i>`.
|
2023-10-04 22:41:00 +02:00
|
|
|
|
*/
|
|
|
|
|
|
void
|
|
|
|
|
|
tortureTest()
|
2009-09-26 23:00:55 +02:00
|
|
|
|
{
|
2024-11-13 02:23:23 +01:00
|
|
|
|
seedRand();
|
2009-09-27 02:46:26 +02:00
|
|
|
|
|
2023-10-04 22:41:00 +02:00
|
|
|
|
using IDX = std::make_index_sequence<MAX_INDEX>;
|
|
|
|
|
|
auto operators = buildOperatorsTable(IDX{});
|
|
|
|
|
|
|
|
|
|
|
|
TypedCounter testCounter;
|
2009-09-27 02:46:26 +02:00
|
|
|
|
|
2024-11-17 19:45:41 +01:00
|
|
|
|
auto testSubject = [&
|
|
|
|
|
|
,gen = makeRandGen()]
|
|
|
|
|
|
(size_t) mutable -> size_t
|
2023-10-04 22:41:00 +02:00
|
|
|
|
{
|
2024-11-17 19:45:41 +01:00
|
|
|
|
uint i = gen.i(MAX_INDEX);
|
2023-10-04 22:41:00 +02:00
|
|
|
|
operators[i](testCounter);
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
};
|
2009-09-27 02:46:26 +02:00
|
|
|
|
|
2023-10-04 22:41:00 +02:00
|
|
|
|
threadBenchmark<NUM_THREADS> (testSubject, NUM_ITERATIONS);
|
2009-09-27 02:46:26 +02:00
|
|
|
|
|
2023-10-04 22:41:00 +02:00
|
|
|
|
size_t expectedIncrements = NUM_THREADS * NUM_ITERATIONS;
|
|
|
|
|
|
CHECK (sumAllCounters(testCounter, IDX{}) == expectedIncrements);
|
2009-09-26 23:00:55 +02:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Register this test class... */
|
|
|
|
|
|
LAUNCHER (TypedCounter_test, "unit common");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}} // namespace lib::test
|