Skip to content

ENH: Allow PoolMultiThreader work units beyond ITK_MAX_THREADS - #7126

Open
blowekamp wants to merge 3 commits into
InsightSoftwareConsortium:mainfrom
blowekamp:pool_work_units_unlimited
Open

blowekamp wants to merge 3 commits into
InsightSoftwareConsortium:mainfrom
blowekamp:pool_work_units_unlimited

Conversation

@blowekamp

Copy link
Copy Markdown
Member

PoolMultiThreader stores work-unit state in a growable vector, so its work-unit count is no longer capped by ITK_MAX_THREADS. ProcessObject and DomainThreader drop the matching upper clamp. PlatformMultiThreader keeps its limit.

Behavior and scope
  • PoolMultiThreader::SetNumberOfWorkUnits only enforces a minimum of one; the array grows before each dispatch, never while tasks hold pointers into it.
  • The global maximum number of threads and the thread pool size are unchanged.
  • Work units above the cap can now be requested through ProcessObject and DomainThreader; PlatformMultiThreader still clamps them.
  • The first commit renames m_ThreadInfoArray to m_WorkUnitInfoArray (no logic change).
AI assistance
  • Tool: GitHub Copilot in VS Code
  • Role: analysis of the work-unit limit, implementation, and local build/test runs
  • All changes were reviewed by the author; a regression found during testing (progress events) was traced and fixed before committing.
Test results

Clean configure (release preset) and full rebuild with no errors, then ctest -j30 with ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS=4: 5314/5314 passed.
itkMultiThreaderBaseTest now covers ParallelizeArray and SingleMethodExecute with ITK_MAX_THREADS + 1 work units on the pool; itkDataObjectAndProcessObjectTest covers the unclamped setter.

PR Checklist

  • No API changes were made (or the changes have been approved)
  • No major design changes were made (or the changes have been approved)
  • Added test (or behavior not changed)
  • Updated API documentation (or API not changed)
  • Added license to new files (if any)
  • Added Python wrapping to new files (if any) as described in ITK Software Guide Section 9.5
  • Added ITK examples for all new major features (if any)

Refer to the ITK Software Guide for
further development details if necessary.

@github-actions github-actions Bot added type:Enhancement Improvement of existing methods or implementation type:Testing Ensure that the purpose of a class is met/the results on a wide set of test cases are correct area:Core Issues affecting the Core module labels Nov 6, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The setter change breaks downstream overrides and virtual dispatch, while removing the clamp can cause backend work-unit count mismatches that leave per-work-unit results unfilled.

2 open issues
What changed in this PR

This PR lets ITK’s pool backend use more work units than ITK_MAX_THREADS, without increasing the thread pool size or removing the platform backend’s limit.

Changes:

  • Replace fixed pool work-unit storage with a vector grown before dispatch.
  • Remove upper work-unit clamps from ProcessObject and DomainThreader.
  • Add above-cap regression coverage and update thread-limit assertions.
File Description
Modules/​Core/​Common/​test/​itkTimeStampGTest.cxx Check the global thread limit without assuming work units are capped.
Modules/​Core/​Common/​test/​itkMultiThreaderBaseTest.cxx Test above-cap pool execution and calling-thread participation.
Modules/​Core/​Common/​test/​itkDataObjectAndProcessObjectTest.cxx Test above-cap work-unit requests.
Modules/​Core/​Common/​src/​itkPoolMultiThreader.cxx Grow work-unit storage before dispatch and remove the work-unit cap.
Modules/​Core/​Common/​include/​itkProcessObject.h Retain only the setter’s minimum bound.
Modules/​Core/​Common/​include/​itkPoolMultiThreader.h Declare vector storage, resizing helper, and setter override.
Modules/​Core/​Common/​include/​itkDomainThreader.h Remove the upper setter bound and clarify backend clamping.

Review effort: Balanced

* */
/** @ITKStartGrouping */
itkSetClampMacro(NumberOfWorkUnits, ThreadIdType, 1, ITK_MAX_THREADS);
void
void
SetNumberOfWorkUnits(ThreadIdType numberOfWorkUnits)
{
const ThreadIdType value = std::max<ThreadIdType>(1, numberOfWorkUnits);
The array holds per-work-unit state and task futures, not thread info.
SetNumberOfWorkUnits now only enforces a minimum of one. The selected
multithreader applies any limit it needs, so the ITK_MAX_THREADS cap
of the statically sized threader arrays no longer leaks into the API.
Work-unit state and task futures live in a vector that grows before
dispatch, so the number of work units is no longer capped by the
statically sized array. The pool still limits the worker threads.
PlatformMultiThreader keeps its ITK_MAX_THREADS limit.
@blowekamp
blowekamp force-pushed the pool_work_units_unlimited branch from a8c6c5c to 9717b71 Compare November 6, 2026 21:00
@blowekamp
blowekamp marked this pull request as ready for review November 7, 2026 13:24
@greptile-apps

greptile-apps Bot commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 4/5

[Medium impact] The change reaches shared ITK parallel-work settings and pool-based execution.

Restore the virtual DomainThreader setter before merging so existing subclass overrides keep working.

Findings

  1. P1 Subclass overrides stop working ▶
  2. P2 Image path lacks limit coverage ▶

Summary

PoolMultiThreader now uses growable storage and accepts work-unit counts above ITK_MAX_THREADS.

  • Pool work can use more units than the fixed thread limit.
  • Process objects and domain threaders keep larger work-unit requests.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Work-unit request] --> B[ProcessObject or DomainThreader]
  B --> C{Selected threader}
  C --> D[PoolMultiThreader]
  C --> E[PlatformMultiThreader]
  D --> F[Grow storage before dispatch]
  F --> G[Queue work units on existing pool]
  E --> H[Clamp work units to thread limit]
Loading

Reviews (1) · Last reviewed commit: "ENH: Allow PoolMultiThreader work units ..." · Reviewed by Greptile

Comment on lines +112 to +113
void
SetNumberOfWorkUnits(ThreadIdType numberOfWorkUnits)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Subclass overrides stop working

DomainThreader::SetNumberOfWorkUnits is no longer virtual. The replaced itkSetClampMacro declared a virtual setter, so downstream subclasses that declare this method with override now fail to compile. Subclasses without override also stop receiving calls made through a DomainThreader pointer. Keep virtual on the replacement setter.

Suggested change
void
SetNumberOfWorkUnits(ThreadIdType numberOfWorkUnits)
virtual void
SetNumberOfWorkUnits(ThreadIdType numberOfWorkUnits)

Comment on lines +203 to +215
const itk::ThreadIdType moreWorkUnits = itk::ITK_MAX_THREADS + 1;
const itk::PoolMultiThreader::Pointer poolThreader = itk::PoolMultiThreader::New();
poolThreader->SetNumberOfWorkUnits(moreWorkUnits);
std::atomic<itk::SizeValueType> processedItems{ 0 };
poolThreader->ParallelizeArray(
0, moreWorkUnits, [&processedItems](itk::SizeValueType) { ++processedItems; }, nullptr);
result &= poolThreader->GetNumberOfWorkUnits() == moreWorkUnits;
result &= processedItems == moreWorkUnits;
SingleMethodExecutionData executionData{ std::this_thread::get_id() };
poolThreader->SetSingleMethod(SingleMethodExecution, &executionData);
poolThreader->SingleMethodExecute();
result &= executionData.CallCount == moreWorkUnits;
result &= executionData.CalledOnCallingThread;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Image path lacks limit coverage

The new tests cross ITK_MAX_THREADS through ParallelizeArray and SingleMethodExecute, but not ParallelizeImageRegion. That method has its own new allocation based on splitCount, so a regression there could pass both tests. Add a pool test with an image region that produces more than ITK_MAX_THREADS splits, and check that every part is visited exactly once.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:Core Issues affecting the Core module type:Enhancement Improvement of existing methods or implementation type:Testing Ensure that the purpose of a class is met/the results on a wide set of test cases are correct

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants