Skip to content

Commit a9387f4

Browse files
authored
Merge pull request #6222 from blowekamp/addSingleMultiThreader
ENH: Add SingleMultiThreader for caller-thread execution
2 parents ad0af23 + b81b6d6 commit a9387f4

12 files changed

Lines changed: 333 additions & 3 deletions

Modules/Core/Common/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ set_property(
4646
TBB
4747
Pool
4848
Platform
49+
Single
4950
)
5051

5152
# See if compiler preprocessor has the __FUNCTION__ directive used by itkExceptionMacro

Modules/Core/Common/include/itkMultiThreaderBase.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ class MultiThreaderBaseEnums
6464
First = Platform,
6565
Pool,
6666
TBB,
67-
Last = TBB,
67+
Single,
68+
Last = Single,
6869
Unknown = -1
6970
};
7071

@@ -168,6 +169,7 @@ class ITKCommon_EXPORT MultiThreaderBase : public Object
168169
static constexpr ThreaderEnum First = ThreaderEnum::First;
169170
static constexpr ThreaderEnum Pool = ThreaderEnum::Pool;
170171
static constexpr ThreaderEnum TBB = ThreaderEnum::TBB;
172+
static constexpr ThreaderEnum Single = ThreaderEnum::Single;
171173
static constexpr ThreaderEnum Last = ThreaderEnum::Last;
172174
static constexpr ThreaderEnum Unknown = ThreaderEnum::Unknown;
173175
#endif
@@ -188,6 +190,8 @@ class ITKCommon_EXPORT MultiThreaderBase : public Object
188190
return "Pool";
189191
case ThreaderEnum::TBB:
190192
return "TBB";
193+
case ThreaderEnum::Single:
194+
return "Single";
191195
case ThreaderEnum::Unknown:
192196
default:
193197
return "Unknown";
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/*=========================================================================
2+
*
3+
* Copyright NumFOCUS
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* https://www.apache.org/licenses/LICENSE-2.0.txt
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*=========================================================================*/
18+
#ifndef itkSingleMultiThreader_h
19+
#define itkSingleMultiThreader_h
20+
21+
#include "itkMultiThreaderBase.h"
22+
23+
namespace itk
24+
{
25+
/** \class SingleMultiThreader
26+
* \brief A class for performing multithreaded execution using only the
27+
* calling thread.
28+
*
29+
* All work is executed synchronously on the calling thread; no additional
30+
* threads are created. This is efficient when the application layer is
31+
* already managing parallelism (e.g., calling ITK filters from multiple
32+
* threads) and internal ITK threading overhead should be avoided.
33+
*
34+
* \ingroup OSSystemObjects
35+
*
36+
* \ingroup ITKCommon
37+
*/
38+
39+
class ITKCommon_EXPORT SingleMultiThreader : public MultiThreaderBase
40+
{
41+
public:
42+
ITK_DISALLOW_COPY_AND_MOVE(SingleMultiThreader);
43+
44+
/** Standard class type aliases. */
45+
using Self = SingleMultiThreader;
46+
using Superclass = MultiThreaderBase;
47+
using Pointer = SmartPointer<Self>;
48+
using ConstPointer = SmartPointer<const Self>;
49+
50+
/** Method for creation through the object factory. */
51+
itkNewMacro(Self);
52+
53+
/** \see LightObject::GetNameOfClass() */
54+
itkOverrideGetNameOfClassMacro(SingleMultiThreader);
55+
56+
/** Execute the SingleMethod (as defined by SetSingleMethod) on the
57+
* calling thread. NumberOfWorkUnits is always 1. */
58+
void
59+
SingleMethodExecute() override;
60+
61+
/** Set the SingleMethod to f() and the UserData field of the
62+
* WorkUnitInfo that is passed to it will be data. */
63+
void
64+
SetSingleMethod(ThreadFunctionType, void * data) override;
65+
66+
/** Parallelize an operation over an array by executing it serially on
67+
* the calling thread. */
68+
void
69+
ParallelizeArray(SizeValueType firstIndex,
70+
SizeValueType lastIndexPlus1,
71+
ArrayThreadingFunctorType aFunc,
72+
ProcessObject * filter) override;
73+
74+
/** Break up region into smaller chunks. SingleThreader processes
75+
* the entire region in a single call on the calling thread. */
76+
void
77+
ParallelizeImageRegion(unsigned int dimension,
78+
const IndexValueType index[],
79+
const SizeValueType size[],
80+
ThreadingFunctorType funcP,
81+
ProcessObject * filter) override;
82+
83+
/** SingleThreader always uses exactly one thread. */
84+
void
85+
SetMaximumNumberOfThreads(ThreadIdType numberOfThreads) override;
86+
87+
/** SingleThreader always uses exactly one work unit. */
88+
void
89+
SetNumberOfWorkUnits(ThreadIdType numberOfWorkUnits) override;
90+
91+
protected:
92+
SingleMultiThreader();
93+
~SingleMultiThreader() override;
94+
void
95+
PrintSelf(std::ostream & os, Indent indent) const override;
96+
97+
/** Friends of Multithreader.
98+
* ProcessObject is a friend so that it can call PrintSelf() on its
99+
* Multithreader. */
100+
friend class ProcessObject;
101+
};
102+
} // end namespace itk
103+
#endif // itkSingleMultiThreader_h

Modules/Core/Common/src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ set(
123123
itkOctreeNode.cxx
124124
itkOutputWindow.cxx
125125
itkPlatformMultiThreader.cxx
126+
itkSingleMultiThreader.cxx
126127
itkProcessObject.cxx
127128
itkProgressAccumulator.cxx
128129
itkProgressReporter.cxx

Modules/Core/Common/src/itkMultiThreaderBase.cxx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
*=========================================================================*/
2828
#include "itkMultiThreaderBase.h"
2929
#include "itkPlatformMultiThreader.h"
30+
#include "itkSingleMultiThreader.h"
3031

3132
#if defined(ITK_USE_POOL_MULTI_THREADER)
3233
# include "itkPoolMultiThreader.h"
@@ -207,6 +208,10 @@ MultiThreaderBase::ThreaderTypeFromString(std::string threaderString)
207208
{
208209
return ThreaderEnum::TBB;
209210
}
211+
else if (threaderString == "SINGLE")
212+
{
213+
return ThreaderEnum::Single;
214+
}
210215
else
211216
{
212217
return ThreaderEnum::Unknown;
@@ -404,6 +409,8 @@ MultiThreaderBase::New()
404409
#else
405410
itkGenericExceptionMacro("ITK has been built without TBB support!");
406411
#endif
412+
case ThreaderEnum::Single:
413+
return SingleMultiThreader::New();
407414
default:
408415
itkGenericExceptionMacro("MultiThreaderBase::GetGlobalDefaultThreader returned Unknown!");
409416
}
@@ -601,6 +608,8 @@ operator<<(std::ostream & out, const MultiThreaderBaseEnums::Threader value)
601608
return "itk::MultiThreaderBaseEnums::Threader::Pool";
602609
case MultiThreaderBaseEnums::Threader::TBB:
603610
return "itk::MultiThreaderBaseEnums::Threader::TBB";
611+
case MultiThreaderBaseEnums::Threader::Single:
612+
return "itk::MultiThreaderBaseEnums::Threader::Single";
604613
// TODO case MultiThreaderBaseEnums::Threader::Last:
605614
// return "itk::MultiThreaderBaseEnums::Threader::Last";
606615
case MultiThreaderBaseEnums::Threader::Unknown:
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/*=========================================================================
2+
*
3+
* Copyright NumFOCUS
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* https://www.apache.org/licenses/LICENSE-2.0.txt
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*=========================================================================*/
18+
#include "itkSingleMultiThreader.h"
19+
#include "itkNumericTraits.h"
20+
#include "itkProcessObject.h"
21+
#include "itkTotalProgressReporter.h"
22+
23+
namespace itk
24+
{
25+
26+
SingleMultiThreader::SingleMultiThreader()
27+
{
28+
m_NumberOfWorkUnits = 1;
29+
m_MaximumNumberOfThreads = 1;
30+
}
31+
32+
SingleMultiThreader::~SingleMultiThreader() = default;
33+
34+
void
35+
SingleMultiThreader::SetSingleMethod(ThreadFunctionType f, void * data)
36+
{
37+
m_SingleMethod = std::move(f);
38+
m_SingleData = data;
39+
}
40+
41+
void
42+
SingleMultiThreader::SetMaximumNumberOfThreads(ThreadIdType)
43+
{
44+
m_MaximumNumberOfThreads = 1;
45+
}
46+
47+
void
48+
SingleMultiThreader::SetNumberOfWorkUnits(ThreadIdType)
49+
{
50+
m_NumberOfWorkUnits = 1;
51+
}
52+
53+
void
54+
SingleMultiThreader::SingleMethodExecute()
55+
{
56+
if (!m_SingleMethod)
57+
{
58+
itkExceptionStringMacro("No single method set!");
59+
}
60+
61+
WorkUnitInfo workUnitInfo;
62+
workUnitInfo.WorkUnitID = 0;
63+
workUnitInfo.NumberOfWorkUnits = 1;
64+
workUnitInfo.UserData = m_SingleData;
65+
workUnitInfo.ThreadFunction = m_SingleMethod;
66+
workUnitInfo.ThreadExitCode = WorkUnitInfo::ThreadExitCodeEnum::SUCCESS;
67+
68+
// Invoke the method directly on the calling thread.
69+
// Since no thread boundary is crossed, exceptions propagate naturally
70+
// to the caller, preserving the original exception type and message.
71+
m_SingleMethod(&workUnitInfo);
72+
}
73+
74+
void
75+
SingleMultiThreader::ParallelizeArray(SizeValueType firstIndex,
76+
SizeValueType lastIndexPlus1,
77+
ArrayThreadingFunctorType aFunc,
78+
ProcessObject * filter)
79+
{
80+
if (!this->GetUpdateProgress())
81+
{
82+
filter = nullptr;
83+
}
84+
85+
if (firstIndex + 1 < lastIndexPlus1)
86+
{
87+
const SizeValueType count = lastIndexPlus1 - firstIndex;
88+
TotalProgressReporter reporter(filter, count);
89+
for (SizeValueType i = firstIndex; i < lastIndexPlus1; ++i)
90+
{
91+
reporter.CheckAbortGenerateData();
92+
aFunc(i);
93+
reporter.CompletedPixel();
94+
}
95+
}
96+
else if (firstIndex + 1 == lastIndexPlus1)
97+
{
98+
// Match MultiThreaderBase::ParallelizeArray: progress reaches 1.0 on RAII exit.
99+
const ProgressReporter progress(filter, 0, 1);
100+
aFunc(firstIndex);
101+
}
102+
// else: empty or reversed range — nothing to execute
103+
}
104+
105+
void
106+
SingleMultiThreader::ParallelizeImageRegion(unsigned int dimension,
107+
const IndexValueType index[],
108+
const SizeValueType size[],
109+
ThreadingFunctorType funcP,
110+
ProcessObject * filter)
111+
{
112+
(void)dimension; // The full region is passed directly; dimension is implicit in index/size.
113+
if (!this->GetUpdateProgress())
114+
{
115+
filter = nullptr;
116+
}
117+
118+
const ProgressReporter progress(filter, 0, 1);
119+
120+
funcP(index, size);
121+
}
122+
123+
void
124+
SingleMultiThreader::PrintSelf(std::ostream & os, Indent indent) const
125+
{
126+
Superclass::PrintSelf(os, indent);
127+
}
128+
129+
} // namespace itk

Modules/Core/Common/test/CMakeLists.txt

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,18 @@ set_tests_properties(
658658
ENVIRONMENT
659659
"ITK_GLOBAL_DEFAULT_THREADER=Pool"
660660
)
661+
itk_add_test(
662+
NAME itkMultiThreaderBaseTestSingle
663+
COMMAND
664+
ITKCommon2TestDriver
665+
itkMultiThreaderBaseTest
666+
)
667+
set_tests_properties(
668+
itkMultiThreaderBaseTestSingle
669+
PROPERTIES
670+
ENVIRONMENT
671+
"ITK_GLOBAL_DEFAULT_THREADER=Single"
672+
)
661673
itk_add_test(
662674
NAME itkMultiThreaderBaseTest3
663675
COMMAND
@@ -694,6 +706,20 @@ set_tests_properties(
694706
"ITK_GLOBAL_DEFAULT_THREADER=pOoL"
695707
) # tests letter case too
696708

709+
itk_add_test(
710+
NAME itkMultiThreaderTypeFromEnvironmentTestSingle
711+
COMMAND
712+
ITKCommon2TestDriver
713+
itkMultiThreaderTypeFromEnvironmentTest
714+
SiNgLe
715+
)
716+
set_tests_properties(
717+
itkMultiThreaderTypeFromEnvironmentTestSingle
718+
PROPERTIES
719+
ENVIRONMENT
720+
"ITK_GLOBAL_DEFAULT_THREADER=sInGlE"
721+
) # tests letter case too
722+
697723
if(Module_ITKTBB) # ITK_USE_TBB is not yet defined here
698724
itk_add_test(
699725
NAME itkMultiThreaderBaseTestTBB
@@ -760,6 +786,18 @@ set_tests_properties(
760786
ENVIRONMENT
761787
"ITK_GLOBAL_DEFAULT_THREADER=Pool"
762788
)
789+
itk_add_test(
790+
NAME itkMultiThreaderParallelizeArrayTestSingle
791+
COMMAND
792+
ITKCommon2TestDriver
793+
itkMultiThreaderParallelizeArrayTest
794+
)
795+
set_tests_properties(
796+
itkMultiThreaderParallelizeArrayTestSingle
797+
PROPERTIES
798+
ENVIRONMENT
799+
"ITK_GLOBAL_DEFAULT_THREADER=Single"
800+
)
763801
itk_add_test(
764802
NAME itkMultiThreaderParallelizeArrayTest3
765803
COMMAND

Modules/Core/Common/test/itkMultiThreaderBaseTest.cxx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "itkMultiThreaderBase.h"
2121
#include "itkPlatformMultiThreader.h"
2222
#include "itkPoolMultiThreader.h"
23+
#include "itkSingleMultiThreader.h"
2324
#ifdef ITK_USE_TBB
2425
# include "itkTBBMultiThreader.h"
2526
#endif
@@ -122,6 +123,7 @@ itkMultiThreaderBaseTest(int argc, char * argv[])
122123

123124
TEST_SINGLE_CLASS(PlatformMultiThreader);
124125
TEST_SINGLE_CLASS(PoolMultiThreader);
126+
TEST_SINGLE_CLASS(SingleMultiThreader);
125127
#ifdef ITK_USE_TBB
126128
TEST_SINGLE_CLASS(TBBMultiThreader);
127129
#endif
@@ -154,6 +156,7 @@ itkMultiThreaderBaseTest(int argc, char * argv[])
154156
// itk::MultiThreaderBaseEnums::Threader::First,
155157
itk::MultiThreaderBaseEnums::Threader::Pool,
156158
itk::MultiThreaderBaseEnums::Threader::TBB,
159+
itk::MultiThreaderBaseEnums::Threader::Single,
157160
// itk::MultiThreaderBaseEnums::Threader::Last,
158161
itk::MultiThreaderBaseEnums::Threader::Unknown
159162
};

0 commit comments

Comments
 (0)