Workers optimization and refactor
This commit is contained in:
parent
9f6ff5fad8
commit
b94a44c7a4
@ -19,7 +19,7 @@ export
|
|||||||
|
|
||||||
struct AssetHandle
|
struct AssetHandle
|
||||||
{
|
{
|
||||||
inline static constexpr std::uint32_t INVALID_ASSET_ID = UINT32_MAX;
|
static constexpr std::uint32_t INVALID_ASSET_ID = UINT32_MAX;
|
||||||
std::size_t slot = 0;
|
std::size_t slot = 0;
|
||||||
std::uint32_t id = 0;
|
std::uint32_t id = 0;
|
||||||
};
|
};
|
||||||
@ -96,7 +96,7 @@ export
|
|||||||
class AssetManager final : public ee_utils::ThreadedWorker<AssetStreamCommand, AssetStreamCommand::COMPLETED>
|
class AssetManager final : public ee_utils::ThreadedWorker<AssetStreamCommand, AssetStreamCommand::COMPLETED>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
AssetManager() : ee_utils::ThreadedWorker<AssetStreamCommand, AssetStreamCommand::COMPLETED>(L"Assets Stream")
|
AssetManager() : ThreadedWorker(L"Assets Stream", 250)
|
||||||
{
|
{
|
||||||
commandsMapping[AssetStreamCommand::LOAD] = std::bind_front(&AssetManager::LoadCmd, this);
|
commandsMapping[AssetStreamCommand::LOAD] = std::bind_front(&AssetManager::LoadCmd, this);
|
||||||
commandsMapping[AssetStreamCommand::FIND] = std::bind_front(&AssetManager::FindCmd, this);
|
commandsMapping[AssetStreamCommand::FIND] = std::bind_front(&AssetManager::FindCmd, this);
|
||||||
@ -105,7 +105,7 @@ export
|
|||||||
diskSystem = Allocate<NativeFileSystem>();
|
diskSystem = Allocate<NativeFileSystem>();
|
||||||
}
|
}
|
||||||
|
|
||||||
~AssetManager()
|
~AssetManager() override
|
||||||
{
|
{
|
||||||
Deallocate(diskSystem);
|
Deallocate(diskSystem);
|
||||||
}
|
}
|
||||||
@ -138,7 +138,7 @@ export
|
|||||||
command.out = &result;
|
command.out = &result;
|
||||||
|
|
||||||
Request(command);
|
Request(command);
|
||||||
while (command.out->load() == nullptr);
|
while (command.out->load() == nullptr) {}
|
||||||
return *reinterpret_cast<AssetHandle*>(command.out->load());
|
return *reinterpret_cast<AssetHandle*>(command.out->load());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,9 +167,9 @@ export
|
|||||||
return Get<T>(Find(id));
|
return Get<T>(Find(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Redirect(AssetHandle &handle, void *resource)
|
void Redirect(const AssetHandle &handle, void *resource)
|
||||||
{
|
{
|
||||||
AssetInfo &asset = activeAssets[handle.slot];
|
const AssetInfo &asset = activeAssets[handle.slot];
|
||||||
if (asset.resourceAddress == nullptr)
|
if (asset.resourceAddress == nullptr)
|
||||||
{
|
{
|
||||||
LOG(WARNING, "Attempt to redirect empty handle");
|
LOG(WARNING, "Attempt to redirect empty handle");
|
||||||
@ -262,7 +262,7 @@ export
|
|||||||
|
|
||||||
void Release() override
|
void Release() override
|
||||||
{
|
{
|
||||||
for (AssetInfo &asset : activeAssets)
|
for (const AssetInfo &asset : activeAssets)
|
||||||
{
|
{
|
||||||
//TODO: LEAK! Free resource
|
//TODO: LEAK! Free resource
|
||||||
if (asset.resourceAddress->resource.load() != nullptr)
|
if (asset.resourceAddress->resource.load() != nullptr)
|
||||||
@ -275,8 +275,8 @@ export
|
|||||||
}
|
}
|
||||||
activeAssets.clear();
|
activeAssets.clear();
|
||||||
|
|
||||||
for (auto &mapping : assetsMapping)
|
for (AssetHandle *&handle: assetsMapping | std::views::values)
|
||||||
Deallocate(mapping.second);
|
Deallocate(handle);
|
||||||
assetsMapping.clear();
|
assetsMapping.clear();
|
||||||
|
|
||||||
binaryStorage.clear();
|
binaryStorage.clear();
|
||||||
|
|||||||
@ -23,7 +23,7 @@ export
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Client();
|
Client();
|
||||||
~Client();
|
~Client() override;
|
||||||
void Init();
|
void Init();
|
||||||
|
|
||||||
//void Send(); -> Request(SEND)
|
//void Send(); -> Request(SEND)
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import utils;
|
|||||||
import views;
|
import views;
|
||||||
import Engine;
|
import Engine;
|
||||||
|
|
||||||
Client::Client() : ee_utils::ThreadedWorker<ClientCommand, ClientCommand::COMPLETED>(L"Client")
|
Client::Client() : ThreadedWorker(L"Client", 0)
|
||||||
{
|
{
|
||||||
commandsMapping[ClientCommand::SEND] = std::bind_front(&Client::SendCmd, this);
|
commandsMapping[ClientCommand::SEND] = std::bind_front(&Client::SendCmd, this);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -79,10 +79,10 @@ int main(int argc, char **argv)
|
|||||||
Engine::client->Start();
|
Engine::client->Start();
|
||||||
|
|
||||||
// Game loop
|
// Game loop
|
||||||
ee_engine::RendererCommand renderCmdBuffer; std::vector<ee_engine::RendererCommand> renderIncompletesBuffer;
|
ee_engine::RendererCommand renderCmdBuffer;
|
||||||
while(!glfwWindowShouldClose(window))
|
while(!glfwWindowShouldClose(window))
|
||||||
{
|
{
|
||||||
Engine::renderer->ProcessQueue(renderCmdBuffer, renderIncompletesBuffer);
|
Engine::renderer->ProcessQueue(renderCmdBuffer);
|
||||||
Engine::renderer->Draw();
|
Engine::renderer->Draw();
|
||||||
|
|
||||||
glfwSwapBuffers(window);
|
glfwSwapBuffers(window);
|
||||||
|
|||||||
@ -11,48 +11,60 @@ export namespace ee_utils
|
|||||||
};
|
};
|
||||||
template <typename T> concept IWorkerCommand = std::is_base_of_v<WorkerCommand, T>;
|
template <typename T> concept IWorkerCommand = std::is_base_of_v<WorkerCommand, T>;
|
||||||
|
|
||||||
template <IWorkerCommand TCommand, std::size_t maxCmdType>
|
template <IWorkerCommand TCommand, std::size_t MaxCmdTypeId, bool IdleDetection = false>
|
||||||
class Worker
|
class Worker
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void Request(const TCommand &command)
|
void Request(const TCommand &command)
|
||||||
{
|
{
|
||||||
commands.push(command);
|
commands.push(command); //TODO: filled queue case
|
||||||
}
|
}
|
||||||
|
|
||||||
void Request(const std::uint8_t type)
|
void Request(const std::uint8_t type)
|
||||||
{
|
{
|
||||||
commands.push(TCommand {.type = type});
|
commands.push(TCommand {.type = type}); //TODO: filled queue case
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProcessQueue(TCommand &buffer, std::vector<TCommand> &incompletesBuffer)
|
void ProcessQueue(TCommand &buffer) requires !IdleDetection
|
||||||
{
|
{
|
||||||
while (this->commands.pop(buffer))
|
while (this->commands.pop(buffer))
|
||||||
{
|
{
|
||||||
ProcessCommand(buffer);
|
ProcessCommand(buffer);
|
||||||
if (buffer.type != maxCmdType)
|
|
||||||
incompletesBuffer.push_back(buffer);
|
|
||||||
}
|
}
|
||||||
for (TCommand &command : incompletesBuffer)
|
}
|
||||||
|
|
||||||
|
std::uint32_t ProcessQueue(TCommand &buffer) requires IdleDetection
|
||||||
{
|
{
|
||||||
this->commands.push(command);
|
thread_local std::uint32_t idleCounter = 0;
|
||||||
|
if (!this->commands.pop(buffer))
|
||||||
|
{
|
||||||
|
idleCounter++;
|
||||||
|
return idleCounter;
|
||||||
}
|
}
|
||||||
incompletesBuffer.clear();
|
idleCounter = 0;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
ProcessCommand(buffer);
|
||||||
|
} while (this->commands.pop(buffer));
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::array<std::function<void(TCommand &command)>, maxCmdType + 1> commandsMapping;
|
// TODO: replace std::function with more optimized ref
|
||||||
// TODO: maybe I should use lockfree::queue ?
|
std::array<std::function<void(TCommand &command)>, MaxCmdTypeId + 1> commandsMapping;
|
||||||
|
// TODO: maybe I should use lockfree::queue ? Otherwise make consumer thread explicit for safety
|
||||||
boost::lockfree::spsc_queue<TCommand, boost::lockfree::capacity<512>> commands;
|
boost::lockfree::spsc_queue<TCommand, boost::lockfree::capacity<512>> commands;
|
||||||
|
|
||||||
Worker() {};
|
Worker() {};
|
||||||
|
|
||||||
void ProcessCommand(TCommand &command)
|
void ProcessCommand(TCommand &command)
|
||||||
{
|
{
|
||||||
if (command.type > maxCmdType || !commandsMapping[command.type])
|
if (command.type > MaxCmdTypeId || !commandsMapping[command.type])
|
||||||
{
|
{
|
||||||
LOG(WARNING, "Unhandled command. Type id: %d", command.type);
|
LOG(WARNING, "Unhandled command. Type id: %d", command.type);
|
||||||
command.type = maxCmdType;
|
command.type = MaxCmdTypeId;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,7 +80,7 @@ export namespace ee_utils
|
|||||||
|
|
||||||
void Start()
|
void Start()
|
||||||
{
|
{
|
||||||
if (isRunning)
|
if (isRunning.load())
|
||||||
{
|
{
|
||||||
LOG(CRITICAL, "Thread can be started only once before stop");
|
LOG(CRITICAL, "Thread can be started only once before stop");
|
||||||
throw std::logic_error("Thread can be started only once before stop");
|
throw std::logic_error("Thread can be started only once before stop");
|
||||||
@ -83,7 +95,7 @@ export namespace ee_utils
|
|||||||
|
|
||||||
void Shutdown()
|
void Shutdown()
|
||||||
{
|
{
|
||||||
if (isRunning)
|
if (isRunning.load())
|
||||||
thread.request_stop();
|
thread.request_stop();
|
||||||
else LOG(WARNING, "Tried to shutdown stopped thread");
|
else LOG(WARNING, "Tried to shutdown stopped thread");
|
||||||
}
|
}
|
||||||
@ -96,15 +108,22 @@ export namespace ee_utils
|
|||||||
};
|
};
|
||||||
template<typename T> concept IThreadedWorker = std::is_base_of_v<ThreadedWorkerBase, T>;
|
template<typename T> concept IThreadedWorker = std::is_base_of_v<ThreadedWorkerBase, T>;
|
||||||
|
|
||||||
template <IWorkerCommand TCommand, std::size_t maxCmdType>
|
enum class IdleBackoffMode : std::uint8_t
|
||||||
class ThreadedWorker : public Worker<TCommand, maxCmdType>, public ThreadedWorkerBase
|
{
|
||||||
|
DISABLED,
|
||||||
|
YIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
template <IWorkerCommand TCommand, std::size_t MaxCmdType, IdleBackoffMode IdleBackoffMode = IdleBackoffMode::DISABLED>
|
||||||
|
class ThreadedWorker : public Worker<TCommand, MaxCmdType, static_cast<bool>(IdleBackoffMode)>, public ThreadedWorkerBase
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
const wchar_t *threadName;
|
const wchar_t *threadName;
|
||||||
|
|
||||||
explicit ThreadedWorker<TCommand, maxCmdType>(const wchar_t *threadName)
|
explicit ThreadedWorker(const wchar_t *threadName, std::uint32_t yieldIdleTime = 0)
|
||||||
{
|
{
|
||||||
this->threadName = threadName;
|
this->threadName = threadName;
|
||||||
|
this->yieldIdleTime = yieldIdleTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Threading()
|
void Threading()
|
||||||
@ -120,11 +139,19 @@ export namespace ee_utils
|
|||||||
void Loop()
|
void Loop()
|
||||||
{
|
{
|
||||||
TCommand commandBuffer{};
|
TCommand commandBuffer{};
|
||||||
// ReSharper disable once CppTooWideScope
|
|
||||||
std::vector<TCommand> incompleteCommands;
|
|
||||||
while (!stoken->stop_requested())
|
while (!stoken->stop_requested())
|
||||||
{
|
{
|
||||||
this->ProcessQueue(commandBuffer, incompleteCommands);
|
if constexpr (IdleBackoffMode == IdleBackoffMode::DISABLED)
|
||||||
|
{
|
||||||
|
this->ProcessQueue(commandBuffer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const std::uint32_t idleCounter = this->ProcessQueue(commandBuffer);
|
||||||
|
|
||||||
|
if (idleCounter > yieldIdleTime)
|
||||||
|
std::this_thread::yield();
|
||||||
|
}
|
||||||
|
|
||||||
Update();
|
Update();
|
||||||
}
|
}
|
||||||
@ -134,6 +161,8 @@ export namespace ee_utils
|
|||||||
virtual void Release() = 0;
|
virtual void Release() = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
std::uint32_t yieldIdleTime;
|
||||||
|
|
||||||
void ReleaseStop()
|
void ReleaseStop()
|
||||||
{
|
{
|
||||||
Release();
|
Release();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user