Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WASM improvements #1395

Merged
merged 6 commits into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion WASM_Support.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

# WASM Support

oneTBB extends its capabilities by offering robust support for ``WASM``.
oneTBB extends its capabilities by offering robust support for ``WASM`` (see ``Limitation`` sections).

``WASM`` stands for WebAssembly, a low-level binary format for executing code in web browsers.
It is designed to be a portable target for compilers and efficient to parse and execute.
Expand Down Expand Up @@ -58,3 +58,22 @@ To run tests, use:
ctest
```

# Limitations

While you can successfully build your application with oneTBB using WASM, you may not achieve optimal performance immediately. This is due to the limitation of nested Web Workers, where a Web Worker cannot schedule another worker without the participation of a browser thread. This can lead to unexpected performance outcomes, such as the application running in serial.
pavelkumbrasev marked this conversation as resolved.
Show resolved Hide resolved
This issue is [discussed](https://github.com/emscripten-core/emscripten/discussions/21963) in the Emscripten repository, which you can refer to for more information.
pavelkumbrasev marked this conversation as resolved.
Show resolved Hide resolved
There are two potential workarounds for this issue:
pavelkumbrasev marked this conversation as resolved.
Show resolved Hide resolved
1. The recommended solution is to use the ``-sPROXY_TO_PTHREAD`` flag. This flag splits the initial thread into a browser thread and a main thread (proxied by a Web Worker), effectively resolving the issue as the browser thread is always present in the event loop and can participate in Web Workers scheduling. Please refer to the Emscripten documentation for more details on using ``-sPROXY_TO_PTHREAD``, as in some cases, using this flag may require you to refactor your code.
pavelkumbrasev marked this conversation as resolved.
Show resolved Hide resolved
2. Another solution is to warm up the oneTBB thread pool before the first call to oneTBB. This approach forces the browser thread to participate in Web Workers scheduling.
pavelkumbrasev marked this conversation as resolved.
Show resolved Hide resolved
```cpp
int num_threads = tbb::this_task_arena::max_concurrency();
std::atomic<int> barrier{num_threads};
tbb::parallel_for(0, num_threads, [&barrier] (int) {
barrier--;
while (barrier > 0) {
// Send browser thread to event loop
std::this_thread::yield();
}
}, tbb::static_partitioner{});
```
However, be aware that this might cause delays on the browser side.
pavelkumbrasev marked this conversation as resolved.
Show resolved Hide resolved
16 changes: 10 additions & 6 deletions cmake/compilers/Clang.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,16 @@
# limitations under the License.

if (EMSCRIPTEN)
set(TBB_EMSCRIPTEN 1)
set(TBB_COMMON_COMPILE_FLAGS ${TBB_COMMON_COMPILE_FLAGS} -fexceptions)
set(TBB_TEST_LINK_FLAGS ${TBB_COMMON_LINK_FLAGS} -fexceptions -sINITIAL_MEMORY=65536000 -sALLOW_MEMORY_GROWTH=1 -sEXIT_RUNTIME=1)
if (NOT EMSCRIPTEN_WITHOUT_PTHREAD)
set_property(TARGET Threads::Threads PROPERTY INTERFACE_LINK_LIBRARIES "-pthread")
endif()
set(TBB_EMSCRIPTEN 1)
set(TBB_COMMON_COMPILE_FLAGS ${TBB_COMMON_COMPILE_FLAGS} -fexceptions)
set(TBB_TEST_LINK_FLAGS ${TBB_COMMON_LINK_FLAGS} -fexceptions -sINITIAL_MEMORY=65536000 -sALLOW_MEMORY_GROWTH=1 -sMALLOC=mimalloc -sEXIT_RUNTIME=1)
if (NOT EMSCRIPTEN_WITHOUT_PTHREAD)
set_property(TARGET Threads::Threads PROPERTY INTERFACE_LINK_LIBRARIES "-pthread")
endif()
set(TBB_EMSCRIPTEN_STACK_SIZE 65536)
set(TBB_LIB_COMPILE_FLAGS -D__TBB_EMSCRIPTEN_STACK_SIZE=${TBB_EMSCRIPTEN_STACK_SIZE})
set(TBB_TEST_LINK_FLAGS ${TBB_TEST_LINK_FLAGS} -sTOTAL_STACK=${TBB_EMSCRIPTEN_STACK_SIZE})
unset(TBB_EMSCRIPTEN_STACK_SIZE)
endif()

if (MINGW)
Expand Down
2 changes: 2 additions & 0 deletions src/tbb/global_control.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ class alignas(max_nfs_size) stack_size_control : public control_storage {
return hi - lo;
}();
return ThreadStackSizeDefault;
#elif defined(EMSCRIPTEN)
return __TBB_EMSCRIPTEN_STACK_SIZE;
#else
return ThreadStackSize;
#endif
Expand Down
7 changes: 7 additions & 0 deletions src/tbb/governor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@
#include <atomic>
#include <algorithm>

#ifdef EMSCRIPTEN
#include <emscripten/stack.h>
#endif

namespace tbb {
namespace detail {
namespace r1 {
Expand Down Expand Up @@ -145,6 +149,9 @@ static std::uintptr_t get_stack_base(std::size_t stack_size) {
NT_TIB* pteb = (NT_TIB*)NtCurrentTeb();
__TBB_ASSERT(&pteb < pteb->StackBase && &pteb > pteb->StackLimit, "invalid stack info in TEB");
return reinterpret_cast<std::uintptr_t>(pteb->StackBase);
#elif defined(EMSCRIPTEN)
suppress_unused_warning(stack_size);
return reinterpret_cast<std::uintptr_t>(emscripten_stack_get_base());
#else
// There is no portable way to get stack base address in Posix, so we use
// non-portable method (on all modern Linux) or the simplified approach
Expand Down
Loading