Skip to content

Commit 3c4e6ae

Browse files
sunxiaoxia2022wangleisakladiev
authored
[CPU] Setting parallel to tbb static partitioner or tbb auto partitoner by thread pool (#30167)
### Details: - *Setting parallel to TBB STATIC partitioner or TBB AUTO partitioner through by thread pool* - oneDNN PR: [openvinotoolkit/oneDNN#291] ### Tickets: - *CVS-165229* --------- Co-authored-by: Shen, Wanglei <wanglei.shen@intel.com> Co-authored-by: Alina Kladieva <alina.kladieva@intel.com>
1 parent 60cdd99 commit 3c4e6ae

File tree

93 files changed

+1426
-468
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

93 files changed

+1426
-468
lines changed

cmake/dependencies.cmake

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ unset(_ov_download_tbb_done CACHE)
7171
# or ENABLE_SYSTEM_TBB is OFF
7272
#
7373
function(ov_download_tbb)
74-
if(_ov_download_tbb_done OR NOT THREADING MATCHES "^(TBB|TBB_AUTO)$")
74+
if(_ov_download_tbb_done OR NOT THREADING MATCHES "^(TBB|TBB_AUTO|TBB_ADAPTIVE)$")
7575
return()
7676
endif()
7777
set(_ov_download_tbb_done ON CACHE INTERNAL "Whether prebuilt TBB is already downloaded")

cmake/features.cmake

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,15 @@ ov_dependent_option (ENABLE_PKGCONFIG_GEN "Enable openvino.pc pkg-config file ge
8888
# OpenVINO Runtime specific options
8989
#
9090

91-
# "OneDNN library based on OMP or TBB or Sequential implementation: TBB|OMP|SEQ"
92-
set(THREADING_DEFAULT "TBB")
91+
# "OneDNN library based on OMP or TBB or Sequential implementation: TBB|OMP|SEQ|TBB_ADAPTIVE"
92+
if(AARCH64)
93+
set(THREADING_DEFAULT "TBB")
94+
else()
95+
set(THREADING_DEFAULT "TBB_ADAPTIVE")
96+
endif()
97+
9398

94-
set(THREADING_OPTIONS "TBB" "TBB_AUTO" "SEQ" "OMP")
99+
set(THREADING_OPTIONS "TBB" "TBB_AUTO" "SEQ" "OMP" "TBB_ADAPTIVE")
95100

96101
set(THREADING "${THREADING_DEFAULT}" CACHE STRING "Threading")
97102
set_property(CACHE THREADING PROPERTY STRINGS ${THREADING_OPTIONS})
@@ -109,7 +114,7 @@ endif()
109114

110115
ov_dependent_option (ENABLE_INTEL_OPENMP "Enables usage of Intel OpenMP instead of default compiler one" ${ENABLE_INTEL_OPENMP_DEFAULT} "THREADING STREQUAL OMP" OFF)
111116

112-
if((THREADING STREQUAL "TBB" OR THREADING STREQUAL "TBB_AUTO") AND
117+
if((THREADING STREQUAL "TBB" OR THREADING STREQUAL "TBB_AUTO" OR THREADING STREQUAL "TBB_ADAPTIVE") AND
113118
(BUILD_SHARED_LIBS OR (LINUX AND X86_64)))
114119
set(ENABLE_TBBBIND_2_5_DEFAULT ON)
115120
else()

cmake/templates/OpenVINOConfig.cmake.in

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ endmacro()
169169

170170
macro(_ov_find_tbb)
171171
set(_ov_threading "@THREADING@")
172-
if(_ov_threading STREQUAL "TBB" OR _ov_threading STREQUAL "TBB_AUTO")
172+
if(_ov_threading STREQUAL "TBB" OR _ov_threading STREQUAL "TBB_AUTO" OR _ov_threading STREQUAL "TBB_ADAPTIVE")
173173
set(enable_pkgconfig_tbb "@tbb_FOUND@")
174174

175175
# try tbb.pc
@@ -563,7 +563,7 @@ if(_ov_as_external_package)
563563

564564
# WA for cmake version < 3.16 which does not export
565565
# IMPORTED_LINK_DEPENDENT_LIBRARIES_** properties if no PUBLIC dependencies for the library
566-
if(THREADING STREQUAL "TBB" OR THREADING STREQUAL "TBB_AUTO")
566+
if(THREADING STREQUAL "TBB" OR THREADING STREQUAL "TBB_AUTO" OR THREADING STREQUAL "TBB_ADAPTIVE")
567567
foreach(type RELEASE DEBUG RELWITHDEBINFO MINSIZEREL)
568568
foreach(tbb_target TBB::tbb TBB::tbbmalloc PkgConfig::tbb)
569569
if(TARGET ${tbb_target})

src/bindings/python/src/openvino/properties/intel_cpu/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
# Copyright (C) 2018-2025 Intel Corporation
33
# SPDX-License-Identifier: Apache-2.0
44

5+
# Enums
6+
from openvino._pyopenvino.properties.intel_cpu import TbbPartitioner
7+
58
# Properties
69
import openvino._pyopenvino.properties.intel_cpu as __intel_cpu
710
from openvino.properties._properties import __make_properties

src/bindings/python/src/pyopenvino/core/properties/properties.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,11 +108,16 @@ void regmodule_properties(py::module m) {
108108
py::module m_intel_cpu =
109109
m_properties.def_submodule("intel_cpu", "openvino.properties.intel_cpu submodule that simulates ov::intel_cpu");
110110

111+
py::enum_<ov::intel_cpu::TbbPartitioner>(m_intel_cpu, "TbbPartitioner", py::arithmetic())
112+
.value("STATIC", ov::intel_cpu::TbbPartitioner::STATIC)
113+
.value("AUTO", ov::intel_cpu::TbbPartitioner::AUTO);
114+
111115
// Submodule intel_cpu property
112116
wrap_property_RW(m_intel_cpu, ov::intel_cpu::denormals_optimization, "denormals_optimization");
113117
wrap_property_RW(m_intel_cpu,
114118
ov::intel_cpu::sparse_weights_decompression_rate,
115119
"sparse_weights_decompression_rate");
120+
wrap_property_RW(m_intel_cpu, ov::intel_cpu::tbb_partitioner, "tbb_partitioner");
116121

117122
// Submodule intel_gpu
118123
py::module m_intel_gpu =

src/bindings/python/src/pyopenvino/utils/utils.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "openvino/core/meta_data.hpp"
1919
#include "openvino/frontend/decoder.hpp"
2020
#include "openvino/frontend/graph_iterator.hpp"
21+
#include "openvino/runtime/intel_cpu/properties.hpp"
2122
#include "openvino/runtime/properties.hpp"
2223

2324
using Version = ov::pass::Serialize::Version;
@@ -245,6 +246,8 @@ py::object from_ov_any(const ov::Any& any) {
245246
return py::cast(any.as<ov::hint::ExecutionMode>());
246247
} else if (any.is<ov::log::Level>()) {
247248
return py::cast(any.as<ov::log::Level>());
249+
} else if (any.is<ov::intel_cpu::TbbPartitioner>()) {
250+
return py::cast(any.as<ov::intel_cpu::TbbPartitioner>());
248251
} else if (any.is<ov::device::Type>()) {
249252
return py::cast(any.as<ov::device::Type>());
250253
} else if (any.is<ov::streams::Num>()) {
@@ -544,6 +547,8 @@ ov::Any py_object_to_any(const py::object& py_obj) {
544547
return py::cast<ov::hint::ExecutionMode>(py_obj);
545548
} else if (py::isinstance<ov::log::Level>(py_obj)) {
546549
return py::cast<ov::log::Level>(py_obj);
550+
} else if (py::isinstance<ov::intel_cpu::TbbPartitioner>(py_obj)) {
551+
return py::cast<ov::intel_cpu::TbbPartitioner>(py_obj);
547552
} else if (py::isinstance<ov::device::Type>(py_obj)) {
548553
return py::cast<ov::device::Type>(py_obj);
549554
} else if (py::isinstance<ov::streams::Num>(py_obj)) {

src/bindings/python/tests/test_runtime/test_properties.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,13 @@ def test_properties_rw_base():
116116
(log.Level.TRACE, "Level.TRACE", 4),
117117
),
118118
),
119+
(
120+
intel_cpu.TbbPartitioner,
121+
(
122+
(intel_cpu.TbbPartitioner.STATIC, "TbbPartitioner.STATIC", 1),
123+
(intel_cpu.TbbPartitioner.AUTO, "TbbPartitioner.AUTO", 2),
124+
),
125+
),
119126
(
120127
intel_auto.SchedulePolicy,
121128
(
@@ -365,6 +372,14 @@ def test_properties_ro(ov_property_ro, expected_value):
365372
(2.0, 2.0),
366373
),
367374
),
375+
(
376+
intel_cpu.tbb_partitioner,
377+
"TBB_PARTITIONER",
378+
(
379+
(intel_cpu.TbbPartitioner.STATIC, intel_cpu.TbbPartitioner.STATIC),
380+
(intel_cpu.TbbPartitioner.AUTO, intel_cpu.TbbPartitioner.AUTO),
381+
),
382+
),
368383
(
369384
intel_auto.device_bind_buffer,
370385
"DEVICE_BIND_BUFFER",

src/cmake/install_tbb.cmake

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ unset(_ov_dynamic_tbbbind_2_5_found)
6666
# install TBB
6767

6868
# define variables for OpenVINOConfig.cmake
69-
if(THREADING MATCHES "^(TBB|TBB_AUTO)$")
69+
if(THREADING MATCHES "^(TBB|TBB_AUTO|TBB_ADAPTIVE)$")
7070
set(OV_TBB_DIR "${TBB_DIR}")
7171
list(APPEND PATH_VARS "OV_TBB_DIR")
7272
endif()
@@ -80,7 +80,7 @@ endif()
8080
# - downloaded TBB should be a part of all packages
8181
# - custom TBB provided by users, needs to be a part of wheel packages
8282
# - system TBB also needs to be a part of wheel packages
83-
if(THREADING MATCHES "^(TBB|TBB_AUTO)$" AND
83+
if(THREADING MATCHES "^(TBB|TBB_AUTO|TBB_ADAPTIVE)$" AND
8484
( (DEFINED TBBROOT AND TBBROOT MATCHES ${TEMP}) OR
8585
(DEFINED TBBROOT OR DEFINED TBB_DIR OR DEFINED ENV{TBBROOT} OR
8686
DEFINED ENV{TBB_DIR}) OR ENABLE_SYSTEM_TBB ) )

src/cmake/ov_parallel.cmake

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ function(_ov_get_tbb_location tbb_target _tbb_lib_location_var)
7676
endfunction()
7777

7878
macro(ov_find_package_tbb)
79-
if(THREADING STREQUAL "TBB" OR THREADING STREQUAL "TBB_AUTO" AND NOT TBB_FOUND)
79+
if(THREADING STREQUAL "TBB" OR THREADING STREQUAL "TBB_AUTO" OR THREADING STREQUAL "TBB_ADAPTIVE" AND NOT TBB_FOUND)
8080
# conan generates TBBConfig.cmake files, which follows cmake's
8181
# SameMajorVersion scheme, while TBB itself follows AnyNewerVersion one
8282
# see https://cmake.org/cmake/help/latest/module/CMakePackageConfigHelpers.html#generating-a-package-version-file
@@ -340,7 +340,7 @@ macro(ov_find_package_openmp)
340340
endmacro()
341341

342342
function(ov_set_threading_interface_for TARGET_NAME)
343-
if(THREADING STREQUAL "TBB" OR THREADING STREQUAL "TBB_AUTO" AND NOT TBB_FOUND)
343+
if(THREADING STREQUAL "TBB" OR THREADING STREQUAL "TBB_AUTO" OR THREADING STREQUAL "TBB_ADAPTIVE" AND NOT TBB_FOUND)
344344
# find TBB
345345
ov_find_package_tbb()
346346

@@ -383,9 +383,13 @@ function(ov_set_threading_interface_for TARGET_NAME)
383383
add_library(openvino::threading ALIAS openvino_threading)
384384
endif()
385385

386-
if(THREADING STREQUAL "TBB" OR THREADING STREQUAL "TBB_AUTO")
386+
if(THREADING STREQUAL "TBB" OR THREADING STREQUAL "TBB_AUTO" OR THREADING STREQUAL "TBB_ADAPTIVE")
387387
if(TBB_FOUND)
388-
set(_ov_thread_define "OV_THREAD_TBB")
388+
if(THREADING STREQUAL "TBB_ADAPTIVE")
389+
set(_ov_thread_define "OV_THREAD_TBB_ADAPTIVE")
390+
else()
391+
set(_ov_thread_define "OV_THREAD_TBB")
392+
endif()
389393
set(_ov_threading_lib TBB::tbb)
390394
else()
391395
set(THREADING "SEQ" PARENT_SCOPE)

src/core/include/openvino/core/parallel.hpp

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,14 @@
1717
#include <cstddef>
1818
#include <type_traits>
1919

20-
#define OV_THREAD_TBB 0
21-
#define OV_THREAD_OMP 1
22-
#define OV_THREAD_SEQ 2
23-
#define OV_THREAD_TBB_AUTO 3
24-
25-
#if (OV_THREAD == OV_THREAD_TBB || OV_THREAD == OV_THREAD_TBB_AUTO)
20+
#define OV_THREAD_TBB 0
21+
#define OV_THREAD_OMP 1
22+
#define OV_THREAD_SEQ 2
23+
#define OV_THREAD_TBB_AUTO 3
24+
#define OV_THREAD_TBB_ADAPTIVE 4
25+
26+
#if (OV_THREAD == OV_THREAD_TBB || OV_THREAD == OV_THREAD_TBB_AUTO || OV_THREAD == OV_THREAD_TBB_ADAPTIVE)
27+
# define OV_THREAD_USE_TBB 1
2628
# ifndef NOMINMAX
2729
# define NOMINMAX
2830
# endif
@@ -66,7 +68,7 @@ inline int parallel_get_env_threads() {
6668
inline void parallel_set_max_nested_levels(int levels) {
6769
return;
6870
}
69-
# if OV_THREAD == OV_THREAD_TBB
71+
# if (OV_THREAD == OV_THREAD_TBB || OV_THREAD == OV_THREAD_TBB_ADAPTIVE)
7072
# define PARTITIONING , tbb::static_partitioner()
7173

7274
// The TBB version less than 2018u1 has no static_partitioner argument for
@@ -81,6 +83,7 @@ inline void parallel_set_max_nested_levels(int levels) {
8183
# define PARTITIONING
8284
# endif
8385
#elif OV_THREAD == OV_THREAD_OMP
86+
# define OV_THREAD_USE_TBB 0
8487
# include <omp.h>
8588
# if !defined(_OPENMP)
8689
# error Undefined OpenMP version.
@@ -162,6 +165,7 @@ inline int parallel_get_nested_level() {
162165
}
163166

164167
#elif OV_THREAD == OV_THREAD_SEQ
168+
# define OV_THREAD_USE_TBB 0
165169
# include <algorithm>
166170
inline int parallel_get_env_threads() {
167171
return 1;
@@ -231,7 +235,7 @@ namespace ov {
231235

232236
template <typename F>
233237
void parallel_nt(int nthr, const F& func) {
234-
#if (OV_THREAD == OV_THREAD_TBB || OV_THREAD == OV_THREAD_TBB_AUTO)
238+
#if OV_THREAD_USE_TBB
235239
if (nthr == 0)
236240
nthr = parallel_get_max_threads();
237241
if (nthr == 1) {
@@ -279,7 +283,7 @@ void parallel_nt_static(int nthr, const F& func) {
279283

280284
if (nthr == 0)
281285
nthr = parallel_get_max_threads();
282-
#if (OV_THREAD == OV_THREAD_TBB || OV_THREAD == OV_THREAD_TBB_AUTO)
286+
#if OV_THREAD_USE_TBB
283287
tbb::parallel_for(
284288
0,
285289
nthr,
@@ -305,7 +309,7 @@ void parallel_nt_static(int nthr, const F& func) {
305309

306310
template <typename I, typename F>
307311
void parallel_sort(I begin, I end, const F& comparator) {
308-
#if (OV_THREAD == OV_THREAD_TBB || OV_THREAD == OV_THREAD_TBB_AUTO)
312+
#if OV_THREAD_USE_TBB
309313
tbb::parallel_sort(begin, end, comparator);
310314
#elif OV_THREAD == OV_THREAD_OMP
311315
// TODO: propose OpenMP version
@@ -317,7 +321,7 @@ void parallel_sort(I begin, I end, const F& comparator) {
317321

318322
template <typename T0, typename R, typename F>
319323
R parallel_sum(const T0& D0, const R& input, const F& func) {
320-
#if (OV_THREAD == OV_THREAD_TBB || OV_THREAD == OV_THREAD_TBB_AUTO)
324+
#if OV_THREAD_USE_TBB
321325
return _TBB_REDUCE_FUNC(
322326
tbb::blocked_range<T0>(0, D0),
323327
input,
@@ -351,7 +355,7 @@ R parallel_sum(const T0& D0, const R& input, const F& func) {
351355

352356
template <typename T0, typename T1, typename R, typename F>
353357
R parallel_sum2d(const T0& D0, const T1& D1, const R& input, const F& func) {
354-
#if (OV_THREAD == OV_THREAD_TBB || OV_THREAD == OV_THREAD_TBB_AUTO)
358+
#if OV_THREAD_USE_TBB
355359
return _TBB_REDUCE_FUNC(
356360
tbb::blocked_range2d<T0, T1>(0, D0, 0, D1),
357361
input,
@@ -391,7 +395,7 @@ R parallel_sum2d(const T0& D0, const T1& D1, const R& input, const F& func) {
391395
}
392396
template <typename T0, typename T1, typename T2, typename R, typename F>
393397
R parallel_sum3d(const T0& D0, const T1& D1, const T2& D2, const R& input, const F& func) {
394-
#if (OV_THREAD == OV_THREAD_TBB || OV_THREAD == OV_THREAD_TBB_AUTO)
398+
#if OV_THREAD_USE_TBB
395399
return _TBB_REDUCE_FUNC(
396400
tbb::blocked_range3d<T0, T1, T2>(0, D0, 0, D1, 0, D2),
397401
input,
@@ -524,7 +528,7 @@ void parallel_for(const T0& D0, const F& func) {
524528
if (D0 == T0(0)) {
525529
return;
526530
}
527-
#if OV_THREAD == OV_THREAD_TBB
531+
#if (OV_THREAD == OV_THREAD_TBB || OV_THREAD == OV_THREAD_TBB_ADAPTIVE)
528532
auto work_amount = static_cast<size_t>(D0);
529533
int nthr = parallel_get_max_threads();
530534
if (static_cast<size_t>(nthr) > work_amount)
@@ -590,7 +594,7 @@ void parallel_for2d(const T0& D0, const T1& D1, const F& func) {
590594
if (D0 == T0(0) || D1 == T1(0)) {
591595
return;
592596
}
593-
#if OV_THREAD == OV_THREAD_TBB
597+
#if (OV_THREAD == OV_THREAD_TBB || OV_THREAD == OV_THREAD_TBB_ADAPTIVE)
594598
auto work_amount = static_cast<size_t>(D0 * D1);
595599
int nthr = parallel_get_max_threads();
596600
if (static_cast<size_t>(nthr) > work_amount)
@@ -636,7 +640,7 @@ void parallel_for2d(const T0& D0, const T1& D1, const F& func) {
636640

637641
template <typename T0, typename T1, typename F>
638642
void parallel_for2d_dynamic(const T0& D0, const T1& D1, const F& func) {
639-
#if (OV_THREAD == OV_THREAD_TBB || OV_THREAD == OV_THREAD_TBB_AUTO)
643+
#if OV_THREAD_USE_TBB
640644
tbb::parallel_for(tbb::blocked_range2d<T0, T1>(0, D0, 0, D1), [=](const tbb::blocked_range2d<T0, T1>& r) {
641645
for (T0 d0 = r.rows().begin(); d0 < r.rows().end(); d0++) {
642646
for (T1 d1 = r.cols().begin(); d1 < r.cols().end(); d1++) {
@@ -674,7 +678,7 @@ void parallel_for3d(const T0& D0, const T1& D1, const T2& D2, const F& func) {
674678
if (D0 == T0(0) || D1 == T1(0) || D2 == T2(0)) {
675679
return;
676680
}
677-
#if OV_THREAD == OV_THREAD_TBB
681+
#if (OV_THREAD == OV_THREAD_TBB || OV_THREAD == OV_THREAD_TBB_ADAPTIVE)
678682
auto work_amount = static_cast<size_t>(D0 * D1 * D2);
679683
int nthr = parallel_get_max_threads();
680684
if (static_cast<size_t>(nthr) > work_amount)
@@ -720,7 +724,7 @@ void parallel_for3d(const T0& D0, const T1& D1, const T2& D2, const F& func) {
720724

721725
template <typename T0, typename T1, typename T2, typename F>
722726
void parallel_for3d_dynamic(const T0& D0, const T1& D1, const T2& D2, const F& func) {
723-
#if (OV_THREAD == OV_THREAD_TBB || OV_THREAD == OV_THREAD_TBB_AUTO)
727+
#if OV_THREAD_USE_TBB
724728
tbb::parallel_for(tbb::blocked_range3d<T0, T1, T2>(0, D0, 0, D1, 0, D2),
725729
[=](const tbb::blocked_range3d<T0, T1, T2>& r) {
726730
for (T0 d0 = r.pages().begin(); d0 < r.pages().end(); d0++) {
@@ -762,7 +766,7 @@ void parallel_for4d(const T0& D0, const T1& D1, const T2& D2, const T3& D3, cons
762766
if (D0 == T0(0) || D1 == T1(0) || D2 == T2(0) || D3 == T3(0)) {
763767
return;
764768
}
765-
#if OV_THREAD == OV_THREAD_TBB
769+
#if (OV_THREAD == OV_THREAD_TBB || OV_THREAD == OV_THREAD_TBB_ADAPTIVE)
766770
auto work_amount = static_cast<size_t>(D0 * D1 * D2 * D3);
767771
int nthr = parallel_get_max_threads();
768772
if (static_cast<size_t>(nthr) > work_amount)
@@ -838,7 +842,7 @@ void parallel_for5d(const T0& D0, const T1& D1, const T2& D2, const T3& D3, cons
838842
if (D0 == T0(0) || D1 == T1(0) || D2 == T2(0) || D3 == T3(0) || D4 == T4(0)) {
839843
return;
840844
}
841-
#if OV_THREAD == OV_THREAD_TBB
845+
#if (OV_THREAD == OV_THREAD_TBB || OV_THREAD == OV_THREAD_TBB_ADAPTIVE)
842846
auto work_amount = static_cast<size_t>(D0 * D1 * D2 * D3 * D4);
843847
int nthr = parallel_get_max_threads();
844848
if (static_cast<size_t>(nthr) > work_amount)
@@ -916,7 +920,7 @@ void parallel_for6d(const T0& D0, const T1& D1, const T2& D2, const T3& D3, cons
916920
if (D0 == T0(0) || D1 == T1(0) || D2 == T2(0) || D3 == T3(0) || D4 == T4(0) || D5 == T5(0)) {
917921
return;
918922
}
919-
#if OV_THREAD == OV_THREAD_TBB
923+
#if (OV_THREAD == OV_THREAD_TBB || OV_THREAD == OV_THREAD_TBB_ADAPTIVE)
920924
auto work_amount = static_cast<size_t>(D0 * D1 * D2 * D3 * D4 * D5);
921925
int nthr = parallel_get_max_threads();
922926
if (static_cast<size_t>(nthr) > work_amount)

0 commit comments

Comments
 (0)