[WIP] eventify: towards treating all threads uniformly in Eventify #18
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "m.zych/fmsolvr:WIP/parallelization/intra-node/lgpl21+minimize"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
This change goes even further than the "eventify: passed empty lambda as main_thread's main function", since it completely removes creating ThreadingWrapper's instance for the main_thread. Superficially, creating instances of the ThreadingWrapper class exclusively for the worker threads seems as not ideal, because treating all threads, including the main_thread, uniformly would increase code reuse and regularity of the implementation. However, in programming languages, such as C++ or Rust, there exists an inherent asymmetry between the main_thread and threads spawned programmatically, that is, the main_thread is created automatically by the compiler, leaving relatively little control over that process to the programmer. In contrast, in OpenGL and Vulkan APIs, shaders written in GLSL are executed multiple times, for each vertex or fragment, and therefore the main functions, defined by each shader, are executed concurrently, per thread, in the SIMT execution model, which makes this asymmetry between threads disappear. Trying to hide this inherent asymmetry introduces serious problems: - How to define a std::thread object representing the main_thread? - Which thread should: submit a task_graph to the thread_pool and receive computational results of its execution? Unfortunately, these issues have not been meaningfully resolved in the Eventify.The ThreadingWrapper is a handle to an OS thread, which follows the RAII idiom, that is, it implicitly calls .join() in its destructor. However, this behaviour is problematic, [ISO C++] - Working Draft, C++20 Standard ~ http://open-std.org/jtc1/sc22/wg21/docs/papers/2020/n4868.pdf 32.4.3.4 Thread support library: Class thread - Destructor [Note: (...) implicitly (...) joining a joinable() thread in its destructor could result in difficult to debug (...) performance (...) bugs encountered only when an exception is thrown. These bugs can be avoided by ensuring that the destructor is never executed while the thread is still joinable.] unless cancellation (a stop request) is supported. [C++ reference] - std::jthread ~ https://en.cppreference.com/w/cpp/thread/jthread The class std::jthread represents a single thread of execution. It has the same general behavior as std::thread, except that std::jthread automatically joins on destruction, and can be cancelled/stopped in certain situations. Therefore, it is reasonable to conclude that Eventify's thread_pool suffers from the same exact problem as the ThreadingWrapper class, after all, its destructor blocks current thread of execution until the worker threads have finished executing all submitted tasks. Although, that conclusion is technically correct, Eventify clients are expected to usually create only one thread_pool, per a whole software system, to utilize compute resources efficiently. This idiomatic usage of the Eventify's thread_pool effectively ensures that its lifetime will strictly enclose the duration of execution of all submitted tasks by the worker threads, and therefore the mentioned performance problem will be avoided. However, the fact that in the vast majority of cases there will exist only one instance of the Eventify's thread_pool does not mean, that Eventify clients should not be able to create multiple independent thread_pool instances.