Qt • Big dependency, subpar performance • Folly • Tons of features, but a huge dependency • Intel TBB • Good performance, but API is a bit clunky (more oriented on computations, not on scheduling), a lot of boilerplate in user code • threadpoolcpp (by inkooboo) • Awesome performance, not a lot of tests, complete lack of features • Tons of other smaller implementations
Part of std, but almost no features • boost::future • Has then (no more sugar, though), but requires boost • QFuture • Almost unusable outside QtConcurrent • Folly • Good API, lots of features, but requires Folly • Tons of other smaller implementations
• Trampolining helpers for too deep Future continuations • Noexcept and access specifications • Extra bookkeeping, getters and setters • Extra optimizations based on heuristics and assumptions • All this can be checked out in library source code though • Also it is not a talk about “fastest ever” task scheduler, sorry folks
IO-related • We want to allow user to specify their own subpools with custom limits • We want to allow user to bind subpool to one thread for resource- related operations
task in queue waits for subpool capacity? • Worker invokes taskFinished() • New task is sent to another worker • First worker goes to sleep, second worker wakes • Can we eliminate these extra sleeps? • Yes!
regular ones? Or… • After first schedule for the subpool we always know exact thread • No need to put them in main queue • We can schedule them directly from insertTaskInfo() • Is it unfair? • Yes – they possibly will be scheduled before others • And no – thread bound tasks can’t be scheduled to another thread anyway • We need worker-specific queues for it
priority is just another uint8_t • How can we schedule it though? • We need to read our list from the left, but write to it in multiple points, not only adding to the end
to use, but too heavy • Simple spin lock based on std::atomic_flag for the rescue • Replacing lock in Worker • High concurrency – roughly the same • Low concurrency – 10-20% less overload • Replacing mainLock in TaskDispatcher • High concurrency – 40-70% less overhead • Low concurrency – 10-20% less overhead
We need to stop them and destroy workers somehow • No terminate() method for std::thread • except calling ~thread(), which requires terminate_handler • We need to stop them manually
payload) • Close to “real life” – tasks with some payload that start more tasks at the end of their life • Empty repost (1’000’000 jobs per each thread) • Similar to timed repost, but no payload – brutal on synchronization points • Timed avalanche (100’000 jobs, ~0.1ms payload) • One thread adds tons of tasks with some payload. Also close to “real life” because of the payload. • Empty avalanche (100’000 jobs) • One thread adds tons of empty tasks. More as a concurrency check than “real life”
meaningful data in Future, so Future<bool> (asynqro implementation doesn’t allow Future<void>) • Value of type T • Future<T> • Future<T> • Future<T> and it should be a continuation of Future from task
run() specifying the Failure type • Pros – easy to implement; straightforward solution • Cons – not scalable for more features • Wrapper structure • Cons – not a straightforward solution • Pros – gives us an inversion of control; easy to add more customization points • Let’s go with wrapper
PlainFailure • We have some functions returning Future<T, std::exception_ptr> that we want to run asynchronously • Solutions: • Implicit constructor for my_awesome_app::Failure class • Cast failure in every task using mapFailure() • Add failure cast ability to RunnerInfo