#include <set>
#include <unordered_set>
#include <algorithm>
+#include <thread>
#include "Meta/Combine.hpp"
#include "Meta/Matching.hpp"
parameters. Tags specified in the Signature are only used as
filters and will not be given as a parameter to the function.
+ The second parameter is default 1 (not multi-threaded). If the
+ second parameter threadCount is set to a value greater than 1, then
+ threadCount threads will be used.
+ Note that multi-threading is based on splitting the task of calling
+ the function across sections of entities. Thus if there are only
+ a small amount of entities in the manager, then using multiple
+ threads may not have as great of a speed-up.
+
Example:
\code{.cpp}
manager.forMatchingSignature<TypeList<C0, C1, T0>>([] (
std::size_t ID, C0& component0, C1& component1) {
// Lambda function contents here
- });
+ },
+ 4 // four threads
+ );
\endcode
Note, the ID given to the function is not permanent. An entity's ID
may change when cleanup() is called.
*/
template <typename Signature, typename Function>
- void forMatchingSignature(Function&& function)
+ void forMatchingSignature(Function&& function,
+ std::size_t threadCount = 1)
{
using SignatureComponents =
typename EC::Meta::Matching<Signature, ComponentsList>::type;
BitsetType signatureBitset =
BitsetType::template generateBitset<Signature>();
- for(std::size_t i = 0; i < currentSize; ++i)
+ if(threadCount <= 1)
{
- if(!std::get<bool>(entities[i]))
+ for(std::size_t i = 0; i < currentSize; ++i)
{
- continue;
- }
+ if(!std::get<bool>(entities[i]))
+ {
+ continue;
+ }
- if((signatureBitset & std::get<BitsetType>(entities[i]))
- == signatureBitset)
+ if((signatureBitset & std::get<BitsetType>(entities[i]))
+ == signatureBitset)
+ {
+ Helper::call(i, *this,
+ std::forward<Function>(function));
+ }
+ }
+ }
+ else
+ {
+ std::thread threads[threadCount];
+ std::size_t s = currentSize / threadCount;
+ for(std::size_t i = 0; i < threadCount; ++i)
+ {
+ std::size_t begin = s * i;
+ std::size_t end;
+ if(i == threadCount - 1)
+ {
+ end = currentSize;
+ }
+ else
+ {
+ end = s * (i + 1);
+ }
+ threads[i] = std::thread([this, &function, &signatureBitset]
+ (std::size_t begin,
+ std::size_t end) {
+ for(std::size_t i = begin; i < end; ++i)
+ {
+ if(!std::get<bool>(this->entities[i]))
+ {
+ continue;
+ }
+
+ if((signatureBitset
+ & std::get<BitsetType>(entities[i]))
+ == signatureBitset)
+ {
+ Helper::call(i, *this,
+ std::forward<Function>(function));
+ }
+ }
+ },
+ begin,
+ end);
+ }
+ for(std::size_t i = 0; i < threadCount; ++i)
{
- Helper::call(i, *this, std::forward<Function>(function));
+ threads[i].join();
}
}
}
private:
- std::unordered_map<std::size_t, std::function<void()> >
+ std::unordered_map<std::size_t, std::function<void(std::size_t)> >
forMatchingFunctions;
std::size_t functionIndex = 0;
forMatchingFunctions.emplace(std::make_pair(
functionIndex,
- [function, signatureBitset, helper, this] ()
+ [function, signatureBitset, helper, this]
+ (std::size_t threadCount)
{
- for(std::size_t i = 0; i < this->currentSize; ++i)
+ if(threadCount <= 1)
{
- if(!std::get<bool>(this->entities[i]))
+ for(std::size_t i = 0; i < this->currentSize; ++i)
{
- continue;
+ if(!std::get<bool>(this->entities[i]))
+ {
+ continue;
+ }
+ if((signatureBitset
+ & std::get<BitsetType>(this->entities[i]))
+ == signatureBitset)
+ {
+ helper.callInstance(i, *this, function);
+ }
+ }
+ }
+ else
+ {
+ std::thread threads[threadCount];
+ std::size_t s = this->currentSize / threadCount;
+ for(std::size_t i = 0; i < threadCount; ++ i)
+ {
+ std::size_t begin = s * i;
+ std::size_t end;
+ if(i == threadCount - 1)
+ {
+ end = this->currentSize;
+ }
+ else
+ {
+ end = s * (i + 1);
+ }
+ threads[i] = std::thread(
+ [this, &function, &signatureBitset, &helper]
+ (std::size_t begin,
+ std::size_t end) {
+ for(std::size_t i = begin; i < end; ++i)
+ {
+ if(!std::get<bool>(this->entities[i]))
+ {
+ continue;
+ }
+ if((signatureBitset
+ & std::get<BitsetType>(this->entities[i]))
+ == signatureBitset)
+ {
+ helper.callInstance(i, *this, function);
+ }
+ }
+ },
+ begin, end);
}
- if((signatureBitset
- & std::get<BitsetType>(this->entities[i]))
- == signatureBitset)
+ for(std::size_t i = 0; i < threadCount; ++i)
{
- helper.callInstance(i, *this, function);
+ threads[i].join();
}
}
}));
/*!
\brief Call all stored functions.
+ A second parameter can be optionally used to specify the number
+ of threads to use when calling the functions. Otherwise, this
+ function is by default not multi-threaded.
+ Note that multi-threading is based on splitting the task of calling
+ the functions across sections of entities. Thus if there are only
+ a small amount of entities in the manager, then using multiple
+ threads may not have as great of a speed-up.
+
Example:
\code{.cpp}
manager.addForMatchingFunction<TypeList<C0, C1, T0>>([] (
// call all stored functions
manager.callForMatchingFunctions();
+ // call all stored functions with 4 threads
+ manager.callForMatchingFunctions(4);
+
// remove all stored functions
manager.clearForMatchingFunctions();
\endcode
*/
- void callForMatchingFunctions()
+ void callForMatchingFunctions(std::size_t threadCount = 1)
{
for(auto functionIter = forMatchingFunctions.begin();
functionIter != forMatchingFunctions.end();
++functionIter)
{
- functionIter->second();
+ functionIter->second(threadCount);
}
}
/*!
\brief Call a specific stored function.
+ A second parameter can be optionally used to specify the number
+ of threads to use when calling the function. Otherwise, this
+ function is by default not multi-threaded.
+ Note that multi-threading is based on splitting the task of calling
+ the function across sections of entities. Thus if there are only
+ a small amount of entities in the manager, then using multiple
+ threads may not have as great of a speed-up.
+
Example:
\code{.cpp}
std::size_t id =
// call the previously added function
manager.callForMatchingFunction(id);
+
+ // call the previously added function with 4 threads
+ manager.callForMatchingFunction(id, 4);
\endcode
\return False if a function with the given id does not exist.
*/
- bool callForMatchingFunction(std::size_t id)
+ bool callForMatchingFunction(std::size_t id,
+ std::size_t threadCount = 1)
{
auto iter = forMatchingFunctions.find(id);
if(iter == forMatchingFunctions.end())
{
return false;
}
- iter->second();
+ iter->second(threadCount);
return true;
}
EXPECT_TRUE(manager.hasEntity(0));
}
+TEST(EC, MultiThreaded)
+{
+ EC::Manager<ListComponentsAll, ListTagsAll> manager;
+
+ for(unsigned int i = 0; i < 17; ++i)
+ {
+ manager.addEntity();
+ manager.addComponent<C0>(i, 0, 0);
+ EXPECT_EQ(0, manager.getEntityData<C0>(i).x);
+ EXPECT_EQ(0, manager.getEntityData<C0>(i).y);
+ }
+
+ manager.forMatchingSignature<EC::Meta::TypeList<C0> >(
+ [] (const std::size_t& eid, C0& c) {
+ c.x = 1;
+ c.y = 2;
+ }
+ );
+
+ for(unsigned int i = 0; i < 17; ++i)
+ {
+ EXPECT_EQ(1, manager.getEntityData<C0>(i).x);
+ EXPECT_EQ(2, manager.getEntityData<C0>(i).y);
+ }
+
+ for(unsigned int i = 3; i < 17; ++i)
+ {
+ manager.deleteEntity(i);
+ }
+ manager.cleanup();
+
+ for(unsigned int i = 0; i < 3; ++i)
+ {
+ EXPECT_EQ(1, manager.getEntityData<C0>(i).x);
+ EXPECT_EQ(2, manager.getEntityData<C0>(i).y);
+ }
+
+ manager.forMatchingSignature<EC::Meta::TypeList<C0> >(
+ [] (const std::size_t& eid, C0& c) {
+ c.x = 3;
+ c.y = 4;
+ },
+ 8
+ );
+
+ for(unsigned int i = 0; i < 3; ++i)
+ {
+ EXPECT_EQ(3, manager.getEntityData<C0>(i).x);
+ EXPECT_EQ(4, manager.getEntityData<C0>(i).y);
+ }
+
+ manager.reset();
+
+ for(unsigned int i = 0; i < 17; ++i)
+ {
+ manager.addEntity();
+ manager.addComponent<C0>(i, 0, 0);
+ EXPECT_EQ(0, manager.getEntityData<C0>(i).x);
+ EXPECT_EQ(0, manager.getEntityData<C0>(i).y);
+ }
+
+ auto f0 = manager.addForMatchingFunction<EC::Meta::TypeList<C0> >(
+ [] (const std::size_t& eid, C0& c) {
+ c.x = 1;
+ c.y = 2;
+ }
+ );
+
+ manager.callForMatchingFunctions(2);
+
+ for(unsigned int i = 0; i < 17; ++i)
+ {
+ EXPECT_EQ(1, manager.getEntityData<C0>(i).x);
+ EXPECT_EQ(2, manager.getEntityData<C0>(i).y);
+ }
+
+ auto f1 = manager.addForMatchingFunction<EC::Meta::TypeList<C0> >(
+ [] (const std::size_t& eid, C0& c) {
+ c.x = 3;
+ c.y = 4;
+ }
+ );
+
+ manager.callForMatchingFunction(f1, 4);
+
+ for(unsigned int i = 0; i < 17; ++i)
+ {
+ EXPECT_EQ(3, manager.getEntityData<C0>(i).x);
+ EXPECT_EQ(4, manager.getEntityData<C0>(i).y);
+ }
+
+ for(unsigned int i = 4; i < 17; ++i)
+ {
+ manager.deleteEntity(i);
+ }
+ manager.cleanup();
+
+ manager.callForMatchingFunction(f0, 8);
+
+ for(unsigned int i = 0; i < 4; ++i)
+ {
+ EXPECT_EQ(1, manager.getEntityData<C0>(i).x);
+ EXPECT_EQ(2, manager.getEntityData<C0>(i).y);
+ }
+
+ manager.callForMatchingFunction(f1, 8);
+
+ for(unsigned int i = 0; i < 4; ++i)
+ {
+ EXPECT_EQ(3, manager.getEntityData<C0>(i).x);
+ EXPECT_EQ(4, manager.getEntityData<C0>(i).y);
+ }
+}
+