-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.string;
import core.atomic;
import core.sync.mutex;
import ecs.entity;
import ecs.block_allocator;
@ -57,6 +58,9 @@ class EntityManager
if (threads_count == 0)
threads_count = 0;
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.manager = this;
}
@ -1280,15 +1284,18 @@ class EntityManager
export ref Entity addEntity(EntityTemplate* tmpl)
{
EntityInfo* info = tmpl.info;
EntitiesBlock* last_block = info.last_block;
ushort index = 0;
if (last_block)index = last_block.added_count.atomicOp!"+="(1);
EntitiesBlock* block = findBlockWithFreeSpace(tmpl.info, index);
if (block != last_block)index = block.added_count.atomicOp!"+="(1);
EntitiesBlock* block;
do
{
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);
void* data_begin = block.dataBegin();
void* start = data_begin + EntityID.sizeof * id;
@ -1299,10 +1306,12 @@ class EntityManager
}
if (index == 1)
blocks_to_update.add(block);
threads[thread_id].blocks_to_update.add(block);
Entity* entity = cast(Entity*) start;
add_mutex.lock_nothrow();
entity.id = id_manager.getNewID();
add_mutex.unlock_nothrow();
entity.updateID();
return *entity;
@ -1311,21 +1320,65 @@ class EntityManager
/************************************************************************************************************************
*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;
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.id = 0;
info.first_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.prev_block = block;
block.next_block = new_block;
@ -1472,17 +1525,20 @@ class EntityManager
private void updateBlocks()
{
foreach (block; blocks_to_update)
foreach (ref thread; threads)
{
block.entities_count += block.added_count;
if (block.entities_count > block.type_info.max_entities)
foreach (block; thread.blocks_to_update)
{
block.entities_count = block.type_info.max_entities;
block.entities_count += block.added_count;
if (block.entities_count > block.type_info.max_entities)
{
block.entities_count = block.type_info.max_entities;
}
//block.added_count = 0;
block.added_count.atomicStore(cast(ushort) 0);
}
//block.added_count = 0;
block.added_count.atomicStore(cast(ushort) 0);
thread.blocks_to_update.clear();
}
blocks_to_update.clear();
}
private void removeEntities()
@ -1611,6 +1667,8 @@ class EntityManager
EntitiesBlock* last_block;
///array of CallData. Contain data for System calls.
Vector!(CallData) callers;
///Mutex used to managing block new alloction
}
/************************************************************************************************************************
@ -1692,6 +1750,7 @@ class EntityManager
{
Vector!EntityID entities_to_remove;
Vector!ubyte change_entities_list;
Vector!(EntitiesBlock*) blocks_to_update;
}
static uint thread_id;
@ -1718,7 +1777,7 @@ class EntityManager
mixin EventManagerCode;
//Vector!EntityID entities_to_remove;
Vector!(EntitiesBlock*) blocks_to_update;
//Vector!(EntitiesBlock*) blocks_to_update;
//Vector!ubyte change_entities_list;
void delegate(Job[] jobs) m_dispatch_jobs;
@ -1732,6 +1791,9 @@ class EntityManager
Vector!ComponentInfo components;
Vector!EventInfo events;
Mutex add_mutex;
Mutex entity_block_alloc_mutex;
CallDataAllocator m_call_data_allocator;
struct CallDataAllocator
{