MLIR 24.0.0git
Threading.h
Go to the documentation of this file.
1//===- Threading.h - MLIR Threading Utilities -------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file defines various utilies for multithreaded processing within MLIR.
10// These utilities automatically handle many of the necessary threading
11// conditions, such as properly ordering diagnostics, observing if threading is
12// disabled, etc. These utilities should be used over other threading utilities
13// whenever feasible.
14//
15//===----------------------------------------------------------------------===//
16
17#ifndef MLIR_IR_THREADING_H
18#define MLIR_IR_THREADING_H
19
20#include "mlir/IR/Diagnostics.h"
21#include "llvm/ADT/Sequence.h"
22#include "llvm/Support/ThreadPool.h"
23#include <atomic>
24
25namespace mlir {
26/// Invoke the given function on the elements between [begin, end)
27/// asynchronously, passing each element and its worker index. If the given
28/// function returns a failure when processing any of the elements, execution is
29/// stopped and a failure is returned from this function. This means that in the
30/// case of failure, not all elements of the range will be processed.
31/// Diagnostics emitted during processing are ordered relative to the element's
32/// position within [begin, end). If the provided context does not have
33/// multi-threading enabled, this function always processes elements
34/// sequentially.
35///
36/// Worker indices are in [0, min(range size, maximum concurrency)), or zero
37/// when processing sequentially. Each index is used by only one task for the
38/// entire call, including while that task waits for nested parallel work. This
39/// allows callers to keep per-worker state without synchronization or
40/// thread-local storage. Indices are local to this invocation, not to a thread.
41template <typename IteratorT, typename FuncT>
43 IteratorT begin,
44 IteratorT end, FuncT &&func) {
45 unsigned numElements = static_cast<unsigned>(std::distance(begin, end));
46 if (numElements == 0)
47 return success();
48
49 // If multithreading is disabled or there is a small number of elements,
50 // process the elements directly on this thread.
51 if (!context->isMultithreadingEnabled() || numElements <= 1) {
52 for (; begin != end; ++begin)
53 if (failed(func(*begin, 0)))
54 return failure();
55 return success();
56 }
57
58 // Build a wrapper processing function that properly initializes a parallel
59 // diagnostic handler.
60 ParallelDiagnosticHandler handler(context);
61 std::atomic<unsigned> curIndex(0);
62 std::atomic<bool> processingFailed(false);
63 auto processFn = [&](unsigned workerId) {
64 while (!processingFailed) {
65 unsigned index = curIndex++;
66 if (index >= numElements)
67 break;
69 if (failed(func(*std::next(begin, index), workerId)))
70 processingFailed = true;
71 handler.eraseOrderIDForThread();
72 }
73 };
74
75 // Otherwise, process the elements in parallel.
76 llvm::ThreadPoolInterface &threadPool = context->getThreadPool();
77 llvm::ThreadPoolTaskGroup tasksGroup(threadPool);
78 size_t numActions = std::min(numElements, threadPool.getMaxConcurrency());
79 for (unsigned i = 0; i < numActions; ++i)
80 tasksGroup.async([&, i] { processFn(i); });
81 // If the current thread is a worker thread from the pool, then waiting for
82 // the task group allows the current thread to also participate in processing
83 // tasks from the group, which avoid any deadlock/starvation.
84 tasksGroup.wait();
85 return failure(processingFailed);
86}
87
88/// Invoke the given function on each element, with ordered diagnostics and
89/// early termination on failure. See failableParallelForEachWithWorkerId.
90template <typename IteratorT, typename FuncT>
91LogicalResult failableParallelForEach(MLIRContext *context, IteratorT begin,
92 IteratorT end, FuncT &&func) {
94 context, begin, end, [&](auto &&value, unsigned) {
95 return func(std::forward<decltype(value)>(value));
96 });
97}
98
99/// Invoke the given function on the elements in the provided range
100/// asynchronously. If the given function returns a failure when processing any
101/// of the elements, execution is stopped and a failure is returned from this
102/// function. This means that in the case of failure, not all elements of the
103/// range will be processed. Diagnostics emitted during processing are ordered
104/// relative to the element's position within the range. If the provided context
105/// does not have multi-threading enabled, this function always processes
106/// elements sequentially.
107template <typename RangeT, typename FuncT>
108LogicalResult failableParallelForEach(MLIRContext *context, RangeT &&range,
109 FuncT &&func) {
110 return failableParallelForEach(context, std::begin(range), std::end(range),
111 std::forward<FuncT>(func));
112}
113
114/// Invoke the given function on the elements between [begin, end)
115/// asynchronously. If the given function returns a failure when processing any
116/// of the elements, execution is stopped and a failure is returned from this
117/// function. This means that in the case of failure, not all elements of the
118/// range will be processed. Diagnostics emitted during processing are ordered
119/// relative to the element's position within [begin, end). If the provided
120/// context does not have multi-threading enabled, this function always
121/// processes elements sequentially.
122template <typename FuncT>
123LogicalResult failableParallelForEachN(MLIRContext *context, size_t begin,
124 size_t end, FuncT &&func) {
125 return failableParallelForEach(context, llvm::seq(begin, end),
126 std::forward<FuncT>(func));
127}
128
129/// Invoke the given function on the elements between [begin, end)
130/// asynchronously. Diagnostics emitted during processing are ordered relative
131/// to the element's position within [begin, end). If the provided context does
132/// not have multi-threading enabled, this function always processes elements
133/// sequentially.
134template <typename IteratorT, typename FuncT>
135void parallelForEach(MLIRContext *context, IteratorT begin, IteratorT end,
136 FuncT &&func) {
137 (void)failableParallelForEach(context, begin, end, [&](auto &&value) {
138 return func(std::forward<decltype(value)>(value)), success();
139 });
140}
141
142/// Invoke the given function on the elements in the provided range
143/// asynchronously. Diagnostics emitted during processing are ordered relative
144/// to the element's position within the range. If the provided context does not
145/// have multi-threading enabled, this function always processes elements
146/// sequentially.
147template <typename RangeT, typename FuncT>
148void parallelForEach(MLIRContext *context, RangeT &&range, FuncT &&func) {
149 parallelForEach(context, std::begin(range), std::end(range),
150 std::forward<FuncT>(func));
151}
152
153/// Invoke the given function on the elements between [begin, end)
154/// asynchronously. Diagnostics emitted during processing are ordered relative
155/// to the element's position within [begin, end). If the provided context does
156/// not have multi-threading enabled, this function always processes elements
157/// sequentially.
158template <typename FuncT>
159void parallelFor(MLIRContext *context, size_t begin, size_t end, FuncT &&func) {
160 parallelForEach(context, llvm::seq(begin, end), std::forward<FuncT>(func));
161}
162} // namespace mlir
163
164#endif // MLIR_IR_THREADING_H
return success()
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:64
llvm::ThreadPoolInterface & getThreadPool()
Return the thread pool used by this context.
bool isMultithreadingEnabled()
Return true if multi-threading is enabled by the context.
This class is a utility diagnostic handler for use when multi-threading some part of the compiler whe...
void eraseOrderIDForThread()
Remove the order id for the current thread.
void setOrderIDForThread(size_t orderID)
Set the order id for the current thread.
Include the generated interface declarations.
LogicalResult failableParallelForEachWithWorkerId(MLIRContext *context, IteratorT begin, IteratorT end, FuncT &&func)
Invoke the given function on the elements between [begin, end) asynchronously, passing each element a...
Definition Threading.h:42
LogicalResult failableParallelForEach(MLIRContext *context, IteratorT begin, IteratorT end, FuncT &&func)
Invoke the given function on each element, with ordered diagnostics and early termination on failure.
Definition Threading.h:91
void parallelForEach(MLIRContext *context, IteratorT begin, IteratorT end, FuncT &&func)
Invoke the given function on the elements between [begin, end) asynchronously.
Definition Threading.h:135
void parallelFor(MLIRContext *context, size_t begin, size_t end, FuncT &&func)
Invoke the given function on the elements between [begin, end) asynchronously.
Definition Threading.h:159
LogicalResult failableParallelForEachN(MLIRContext *context, size_t begin, size_t end, FuncT &&func)
Invoke the given function on the elements between [begin, end) asynchronously.
Definition Threading.h:123