v1.10 Wait For Pinned Tasks
This release adds a major new feature - WaitForPinnedTasks - suitable for performing work which could be OS blocking such as IO.
The mortification for this feature is that Some calls, such as file and network IO, may result in the thread being blocked whilst waiting on an external event which does not consume CPU resources to occur. If this was run on a standard enkiTS thread the task scheduler would have fewer threads able to perform computational work. Creating more threads than CPU cores offers one solution, but this will result in OS scheduling overhead which we wish to minimize. The WaitForPinnedTasks()
function permits an enkiTS thread to block at the OS level until it explicitly receives new work via a PinnedTask
. Developers can thus create extra threads which then loop calling WaitForPinnedTasks()
and RunPinnedTasks()
to perform IO/blocking work, minimizing OS scheduling overhead whilst keeping all CPU cores active with enkiTS threads.
WaitForPinnedTasks thread usage in C++:
- full example in example/WaitForPinnedTasks.cpp
- C example in example/WaitForPinnedTasks_c.c
#include "TaskScheduler.h"
enki::TaskScheduler g_TS;
struct RunPinnedTaskLoopTask : enki::IPinnedTask
{
void Execute() override
{
while( g_TS.GetIsRunning() )
{
g_TS.WaitForNewPinnedTasks(); // this thread will 'sleep' until there are new pinned tasks
g_TS.RunPinnedTasks();
}
}
};
struct PretendDoFileIO : enki::IPinnedTask
{
void Execute() override
{
// Do file IO
}
};
int main(int argc, const char * argv[])
{
enki::TaskSchedulerConfig config;
// In this example we create more threads than the hardware can run,
// because the IO thread will spend most of it's time idle or blocked
// and therefore not scheduled for CPU time by the OS
config.numTaskThreadsToCreate += 1;
g_TS.Initialize( config );
// in this example we place our IO threads at the end
RunPinnedTaskLoopTask runPinnedTaskLoopTasks;
runPinnedTaskLoopTasks.threadNum = g_TS.GetNumTaskThreads() - 1;
g_TS.AddPinnedTask( &runPinnedTaskLoopTasks );
// Send pretend file IO task to external thread FILE_IO
PretendDoFileIO pretendDoFileIO;
pretendDoFileIO.threadNum = runPinnedTaskLoopTasks.threadNum;
g_TS.AddPinnedTask( &pretendDoFileIO );
// ensure runPinnedTaskLoopTasks complete by explicitly calling shutdown
g_TS.WaitforAllAndShutdown();
return 0;
}
Screenshot of enkiTS in action in Avoyd Voxel Editor whilst CPU path tracing a scene. Avoyd uses additional enkITS threads using WaitForNewPinnedTasks()
to wait for PinnedTasks
which perform blocking IO (not shown in profile as these are hard to capture in a nice looking screenshot).
Support development of enkiTS through Github Sponsors or Patreon