-multithreaded addEntity(). Works good, but:

*IDManager has mutex (no multithreaded support)
 *allocating blocks has mutex (probably it's possible to do diffrent mutex for diffrent EntityInfo)
-addEntity() if slightly better optimized
This commit is contained in:
Mergul 2018-10-15 22:25:28 +02:00
parent 6e2c00f608
commit 2824bdb55b

View file

@ -9,6 +9,7 @@ import std.traits;
import core.stdc.stdlib; import core.stdc.stdlib;
import core.stdc.string; import core.stdc.string;
import core.atomic; import core.atomic;
import core.sync.mutex;
import ecs.entity; import ecs.entity;
import ecs.block_allocator; import ecs.block_allocator;
@ -57,6 +58,9 @@ class EntityManager
if (threads_count == 0) if (threads_count == 0)
threads_count = 0; threads_count = 0;
threads = Mallocator.instance.makeArray!ThreadData(threads_count); threads = Mallocator.instance.makeArray!ThreadData(threads_count);
add_mutex = Mallocator.instance.make!Mutex;
entity_block_alloc_mutex = Mallocator.instance.make!Mutex;
//event_manager = EventManager(this); //event_manager = EventManager(this);
//event_manager.manager = this; //event_manager.manager = this;
} }
@ -1280,12 +1284,15 @@ class EntityManager
export ref Entity addEntity(EntityTemplate* tmpl) export ref Entity addEntity(EntityTemplate* tmpl)
{ {
EntityInfo* info = tmpl.info; EntityInfo* info = tmpl.info;
EntitiesBlock* last_block = info.last_block;
ushort index = 0; ushort index = 0;
if (last_block)index = last_block.added_count.atomicOp!"+="(1); EntitiesBlock* block;
EntitiesBlock* block = findBlockWithFreeSpace(tmpl.info, index); do
if (block != last_block)index = block.added_count.atomicOp!"+="(1); {
block = findBlockWithFreeSpaceMT(info);
index = block.added_count.atomicOp!"+="(1);
}
while (block.entities_count + index > info.max_entities);
uint id = (block.entities_count + index - 1); //block.added_count); uint id = (block.entities_count + index - 1); //block.added_count);
@ -1299,10 +1306,12 @@ class EntityManager
} }
if (index == 1) if (index == 1)
blocks_to_update.add(block); threads[thread_id].blocks_to_update.add(block);
Entity* entity = cast(Entity*) start; Entity* entity = cast(Entity*) start;
add_mutex.lock_nothrow();
entity.id = id_manager.getNewID(); entity.id = id_manager.getNewID();
add_mutex.unlock_nothrow();
entity.updateID(); entity.updateID();
return *entity; return *entity;
@ -1311,21 +1320,65 @@ class EntityManager
/************************************************************************************************************************ /************************************************************************************************************************
*Return block with free space for selected EntityInfo. *Return block with free space for selected EntityInfo.
*/ */
private EntitiesBlock* findBlockWithFreeSpace(EntityInfo* info, uint new_index = 1) private EntitiesBlock* findBlockWithFreeSpace(EntityInfo* info)
{ {
EntitiesBlock* block = info.last_block; EntitiesBlock* block = info.last_block;
if (block is null) if (block is null)
{ {
block = cast(EntitiesBlock*) allocator.getBlock(); //AlignedMallocator.instance.alignedAllocate(page_size, page_size); block = cast(EntitiesBlock*) allocator.getBlock();
*block = EntitiesBlock(info); *block = EntitiesBlock(info);
block.id = 0; block.id = 0;
info.first_block = block; info.first_block = block;
info.last_block = block; info.last_block = block;
} }
else if (block.entities_count + new_index /*block.added_count*/ > info.max_entities) else if (block.entities_count >= info.max_entities)
{ {
EntitiesBlock* new_block = cast(EntitiesBlock*) allocator.getBlock(); //AlignedMallocator.instance.alignedAllocate(page_size, page_size); EntitiesBlock* new_block = cast(EntitiesBlock*) allocator.getBlock();
*new_block = EntitiesBlock(info);
new_block.prev_block = block;
block.next_block = new_block;
new_block.id = cast(ushort)(block.id + 1);
block = new_block;
info.last_block = block;
}
return block;
}
/************************************************************************************************************************
*Return block with free space for selected EntityInfo. Additional this function is multithread safe.
*/
private EntitiesBlock* findBlockWithFreeSpaceMT(EntityInfo* info)
{
EntitiesBlock* block = info.last_block;
if (block is null)
{
entity_block_alloc_mutex.lock_nothrow();
scope (exit)
entity_block_alloc_mutex.unlock_nothrow();
if (info.last_block != null)
return info.last_block;
block = cast(EntitiesBlock*) allocator.getBlock();
*block = EntitiesBlock(info);
block.id = 0;
info.first_block = block;
info.last_block = block;
}
else if (block.entities_count + block.added_count > info.max_entities)
{
EntitiesBlock* last_block = info.last_block;
entity_block_alloc_mutex.lock_nothrow();
scope (exit)
entity_block_alloc_mutex.unlock_nothrow();
if (info.last_block !is last_block)
return info.last_block;
EntitiesBlock* new_block = cast(EntitiesBlock*) allocator.getBlock();
*new_block = EntitiesBlock(info); *new_block = EntitiesBlock(info);
new_block.prev_block = block; new_block.prev_block = block;
block.next_block = new_block; block.next_block = new_block;
@ -1472,7 +1525,9 @@ class EntityManager
private void updateBlocks() private void updateBlocks()
{ {
foreach (block; blocks_to_update) foreach (ref thread; threads)
{
foreach (block; thread.blocks_to_update)
{ {
block.entities_count += block.added_count; block.entities_count += block.added_count;
if (block.entities_count > block.type_info.max_entities) if (block.entities_count > block.type_info.max_entities)
@ -1482,7 +1537,8 @@ class EntityManager
//block.added_count = 0; //block.added_count = 0;
block.added_count.atomicStore(cast(ushort) 0); block.added_count.atomicStore(cast(ushort) 0);
} }
blocks_to_update.clear(); thread.blocks_to_update.clear();
}
} }
private void removeEntities() private void removeEntities()
@ -1611,6 +1667,8 @@ class EntityManager
EntitiesBlock* last_block; EntitiesBlock* last_block;
///array of CallData. Contain data for System calls. ///array of CallData. Contain data for System calls.
Vector!(CallData) callers; Vector!(CallData) callers;
///Mutex used to managing block new alloction
} }
/************************************************************************************************************************ /************************************************************************************************************************
@ -1692,6 +1750,7 @@ class EntityManager
{ {
Vector!EntityID entities_to_remove; Vector!EntityID entities_to_remove;
Vector!ubyte change_entities_list; Vector!ubyte change_entities_list;
Vector!(EntitiesBlock*) blocks_to_update;
} }
static uint thread_id; static uint thread_id;
@ -1718,7 +1777,7 @@ class EntityManager
mixin EventManagerCode; mixin EventManagerCode;
//Vector!EntityID entities_to_remove; //Vector!EntityID entities_to_remove;
Vector!(EntitiesBlock*) blocks_to_update; //Vector!(EntitiesBlock*) blocks_to_update;
//Vector!ubyte change_entities_list; //Vector!ubyte change_entities_list;
void delegate(Job[] jobs) m_dispatch_jobs; void delegate(Job[] jobs) m_dispatch_jobs;
@ -1732,6 +1791,9 @@ class EntityManager
Vector!ComponentInfo components; Vector!ComponentInfo components;
Vector!EventInfo events; Vector!EventInfo events;
Mutex add_mutex;
Mutex entity_block_alloc_mutex;
CallDataAllocator m_call_data_allocator; CallDataAllocator m_call_data_allocator;
struct CallDataAllocator struct CallDataAllocator
{ {