1#ifndef MECACELL_WORLD_H
2#define MECACELL_WORLD_H
5#include <unordered_set>
15#include <unordered_map>
29template <
typename Cell,
typename Integrator = Euler>
class World {
32 using Clock = std::chrono::high_resolution_clock;
33 std::unordered_map<std::string, std::map<size_t, double>>
benchResults;
55 template <
class,
class = MecaCell::
void_t<>>
78 for (
auto i = cells.begin(); i != cells.end();) {
90 double dt = 1.0 / 100.0;
104 DECLARE_HOOK(onAddCell, beginUpdate, preBehaviorUpdate, preDeleteDeadCellsUpdate,
105 postBehaviorUpdate, endUpdate, allForcesAppliedToCells, destructor)
156 const std::string &pluginName,
157 const std::string &hookName,
158 std::function<hook_s> fn) {
159 hooks[
eToUI(h)].push_back({ pluginName, hookName, std::move(fn) });
164 for (
auto &hw : hooks[
eToUI(h)]) hw.f(
this);
166 for (
auto &hw : hooks[
eToUI(h)]) {
167 auto t0 = Clock::now();
169 auto dt = std::chrono::duration<double, std::micro>(Clock::now() - t0).count();
195 template <
typename P,
typename... Rest>
198 registerPlugin(std::forward<P>(p))(
this);
208 for (
auto &h : hooks) h.clear();
231 Cell *c =
new Cell(std::forward<Args>(args)...);
278 runHooks(Hooks::allForcesAppliedToCells);
318 std::unordered_set<ordered_pair<cell_t *>> res;
319 for (
auto &c : cells) {
320 for (
auto &connected : c->getConnectedCells())
324 for (
auto &r : res) vecRes.
push_back(std::make_pair(r.first, r.second));
335 const size_t MIN_CHUNK_SIZE = 2;
336 const double AVG_TASKS_PER_THREAD = 3.0;
339 [](
auto *c) { c->updateBehavior(); });
342 for (
auto &c : cells) c->updateBehavior();
344 auto t0 = Clock::now();
346 const size_t MIN_CHUNK_SIZE = 2;
347 const double AVG_TASKS_PER_THREAD = 3.0;
350 [](
auto *c) { c->updateBehavior(); });
353 for (
auto &c : cells) c->updateBehavior();
354 auto dt = std::chrono::duration<double, std::micro>(Clock::now() - t0).count();
376 runHooks(Hooks::preDeleteDeadCellsUpdate);
378 runHooks(Hooks::postBehaviorUpdate);
384 auto demangle = [&](
const std::string &mangled) {
386 char* dem = abi::__cxa_demangle(mangled.c_str(),
nullptr,
nullptr, &status);
387 std::string s = (status == 0 && dem) ? dem : mangled;
391 std::map<std::string,std::map<std::string, std::map<size_t, double>>> nested;
394 auto pos = key.rfind(
'@');
395 std::string mangledP = (
pos != std::string::npos ? key.substr(0,
pos) : key);
396 std::string hookName = (
pos != std::string::npos ? key.substr(
pos + 1) :
"");
397 std::string pluginName = demangle(mangledP);
398 auto lt = pluginName.find(
'<');
399 pluginName = lt!=std::string::npos ? pluginName.substr(0,lt) : pluginName;
400 auto ns = pluginName.rfind(
"::");
401 pluginName = ns!=std::string::npos ? pluginName.substr(ns+2) : pluginName;
402 nested[pluginName][hookName] = frameMap;
405 std::ostringstream oss;
407 bool firstPlugin =
true;
408 for (
auto const& [plugin, hooks] : nested) {
409 if (!firstPlugin) oss <<
",";
411 oss <<
"\n \"" << plugin <<
"\": {";
413 bool firstHook =
true;
414 for (
auto const& [hook, frames] : hooks) {
415 if (!firstHook) oss <<
",";
417 oss <<
"\n \"" << hook <<
"\": {";
419 bool firstFrame =
true;
420 for (
auto const& [frm, us] : frames) {
421 if (!firstFrame) oss <<
",";
423 oss <<
"\n \"" << frm <<
"\": " << us;
435 std::cout <<
"Benchmarking is disabled. Enable it by setting World::benchmark to true.\n";
438 struct Stats {
double sum=0, mean=0, stddev=0;
size_t n=0; };
440 auto demangle = [&](
const std::string &mangled) {
442 char* dem = abi::__cxa_demangle(mangled.c_str(),
nullptr,
nullptr, &status);
443 std::string s = (status==0 && dem) ? dem : mangled;
448 auto formatTime = [&](
double us)->std::string {
449 std::ostringstream o;
450 if (us < 1e3) o << std::fixed<<std::setprecision(2)<<us <<
" us";
451 else if (us < 1e6) o << std::fixed<<std::setprecision(2)<<us/1e3<<
" ms";
452 else if (us < 6e7) o << std::fixed<<std::setprecision(2)<<us/1e6<<
" s";
453 else o << std::fixed<<std::setprecision(2)<<us/6e7<<
" min";
457 struct Row { std::string plugin, hook; Stats st; };
460 size_t maxP=0, maxH=0;
462 auto pos = key.rfind(
'@');
463 std::string mangledP = (
pos!=std::string::npos ? key.substr(0,
pos) : key);
464 std::string hookName = (
pos!=std::string::npos ? key.substr(
pos+1) :
"");
465 std::string pluginName = demangle(mangledP);
466 auto lt = pluginName.find(
'<');
467 pluginName = lt!=std::string::npos ? pluginName.substr(0,lt) : pluginName;
468 auto ns = pluginName.rfind(
"::");
469 pluginName = ns!=std::string::npos ? pluginName.substr(ns+2) : pluginName;
471 maxP =
std::max(maxP, pluginName.size());
472 maxH =
std::max(maxH, hookName.size());
474 Stats st; st.n = frameMap.size();
475 for (
auto const& [_f, us] : frameMap) st.sum += us;
477 st.mean = st.sum / st.n;
479 for (
auto const& [_f, us] : frameMap) {
480 double d = us - st.mean;
485 rows.
push_back({ pluginName, hookName, st });
489 [](
auto const &a,
auto const &b){
490 return a.st.sum > b.st.sum;
494 const size_t W_P =
std::min(maxP+2,
size_t(40));
495 const size_t W_H =
std::min(maxH+2,
size_t(30));
499 << std::left << std::setw(W_P) <<
"Plugin"
500 << std::left << std::setw(W_H) <<
"Hook"
502 << std::setw(W_N) <<
"Total"
503 << std::setw(W_N) <<
"Mean"
504 << std::setw(W_N) <<
"StdDev"
505 << std::setw(10) <<
"Frames"
507 << std::string(W_P + W_H + W_N*3 + 10,
'-') <<
"\n";
509 for (
auto const& r : rows) {
510 auto p = r.plugin.size() > W_P-1
511 ? r.plugin.substr(0, W_P-4) +
"..."
513 auto h = r.hook.size() > W_H-1
514 ? r.hook.substr(0, W_H-4) +
"..."
518 << std::left << std::setw(W_P) << p
519 << std::left << std::setw(W_H) << h
521 << std::setw(W_N) << formatTime(r.st.sum)
522 << std::setw(W_N) << formatTime(r.st.mean)
523 << std::setw(W_N) << formatTime(r.st.stddev)
524 << std::setw(10) << r.st.n
CellPlugin< cell_t > embedded_plugin_t
PrimoCell< CellBody > Cell
Where "everything" happens.
void registerPlugins(P &&p, Rest &&... otherPlugins)
Registers a plugin. A plugin is a class or struct that contains hooks.
void setUpdateBehaviorPeriod(size_t p)
sets the period at which the world must call the updateBehavior method of each cell....
size_t getNbUpdates() const
get the number of update since the creation of the world
void allForcesHaveBeenAppliedToCells()
this method triggers the allForcesAppliedToCells. It should be called by the embedded physics plugin ...
Cell * createCell(Args &&... args)
Creates a new cell and adds it through addCell()
void setDt(double d)
sets the amount by which time is increased at each update() call.
DECLARE_HOOK(onAddCell, beginUpdate, preBehaviorUpdate, preDeleteDeadCellsUpdate, postBehaviorUpdate, endUpdate, allForcesAppliedToCells, destructor) std World(size_t nThreads=0)
cells that are registered to be added
void setShuffleCells(bool s)
void addNewCells()
effectively adds the new cells that were registered by addCell triggers addCell hooks if there is som...
~World()
World's destructor. Triggers the destructor hooks and delete all cells.
void update()
main update method
void runHooks(const Hooks &h)
size_t updtBhvPeriod
+1 on cell add. Used for cell's unique ids
EXPORTABLE(World, KV(frame), KV(cells))
@ignore
void addCell(Cell *c)
adds a cell to the new cells batch (which will be added to the main cells container at the end of the...
std::string getBenchmarksJSON() const
void setParallelUpdateBehavior(bool p)
enables or disables the parallelisation of the cells' updateBehavior methods Only has effect if nbThr...
void registerHook(const Hooks &h, const std::string &pluginName, const std::string &hookName, std::function< hook_s > fn)
register a single hook method. cf registerPlugins()
std::vector< std::pair< cell_t *, cell_t * > > getConnectedCellsList()
returns a list of pair of connected cells
vector< Cell * > newCells
size_t nbAddedCells
+1 at each update. cf getNbUpdates()
std::unordered_map< std::string, std::map< size_t, double > > benchResults
void setNbThreads(size_t n)
void clearHooks()
end of recursion
void deleteDeadCells()
removes all cells marked dead
double dtMn
The amount by which time is increased every update.
bool parallelUpdateBehavior
period at which the world should call the cells updateBehavior method.
typename embedded_plugin_type< cell_t >::type cellPlugin_t
void showBenchmarksSummaries() const
std::chrono::high_resolution_clock Clock
size_t getUpdateBehaviorPeriod() const
size_t getNbThreads()
the size of the threadpool that can be used by plugins to launch asynchronous jobs and also directly ...
void callUpdateBehavior()
calls the updateBehavior of each cell, potentially in parallel (see parallelUpdateBehavior and nbThre...
Represents a cell in the simulation.
void setWorld(World *w)
Sets the world for the cell.
Simple threadpool, largely inspired by github.com/progschj/ThreadPool.
void setNbThreads(size_t n)
void autoChunks(Container &v, size_t minChunkSize, double avgTasksPerThread, F f)
A simple vector class template.
void reserve(size_t newCapacity)
Reserves storage for the specified number of elements.
void pop_back()
Removes the last element of the vector.
void push_back(const T &value)
Adds an element to the end of the vector.
iterator begin()
Returns an iterator to the first element.
iterator end()
Returns an iterator to the last element.
T & back()
Returns a reference to the last element.
void insert(iterator position, const T &value)
Inserts an element at the specified position.
bool empty() const
Checks if the vector is empty.
this file contains various miscellanious utility functions & helpers *
constexpr size_t eToUI(const T &t)
ordered_pair< T * > make_ordered_cell_pair(T *a, T *b)
typename make_void< Ts... >::type void_t
iostream cout
Standard output stream.
void shuffle(RandomIt first, RandomIt last)
Shuffles the elements in a range.
double sqrt(double x)
Computes the square root of a number.
double max(double a, double b)
Computes the maximum of two numbers.
double min(double a, double b)
Computes the minimum of two numbers.
static random_engine_t & globalRand()
access to the static global random engine.This pseudo - random generator is* used in random 3D vector...
typename T::embedded_plugin_t type