MeshLib Documentation
Loading...
Searching...
No Matches
MRBitSetParallelFor.h
Go to the documentation of this file.
1#pragma once
2
3#include "MRBitSet.h"
4#include "MRParallel.h"
6#include <atomic>
7#include <cassert>
8#include <thread>
9
10namespace MR
11{
12
15
17template <typename Id>
18struct IdRange
19{
21 auto size() const { return end - beg; }
22};
23
24namespace BitSetParallel
25{
26
27template <typename IndexType>
29{
30 const size_t beginBlock = bitRange.beg / BitSet::bits_per_block;
31 const size_t endBlock = ( size_t( bitRange.end ) + BitSet::bits_per_block - 1 ) / BitSet::bits_per_block;
32 return tbb::blocked_range<size_t>( beginBlock, endBlock );
33}
34
35template <typename BS>
36inline auto blockRange( const BS & bs )
37{
38 const size_t endBlock = ( bs.size() + BS::bits_per_block - 1 ) / BS::bits_per_block;
39 return tbb::blocked_range<size_t>( 0, endBlock );
40}
41
42template <typename BS>
43inline auto bitRange( const BS & bs )
44{
45 return IdRange<typename BS::IndexType>{ bs.beginId(), bs.endId() };
46}
47
48template <typename IndexType>
49auto bitSubRange( const IdRange<IndexType> & bitRange, const tbb::blocked_range<size_t> & range, const tbb::blocked_range<size_t> & subRange )
50{
52 {
53 .beg = subRange.begin() > range.begin() ? IndexType( subRange.begin() * BitSet::bits_per_block ) : bitRange.beg,
54 .end = subRange.end() < range.end() ? IndexType( subRange.end() * BitSet::bits_per_block ) : bitRange.end
55 };
56}
57
58template <typename IndexType, typename CM, typename F>
59void ForAllRanged( const IdRange<IndexType> & bitRange, const CM & callMaker, F && f )
60{
61 const auto range = BitSetParallel::blockRange( bitRange );
62 tbb::parallel_for( range, [&]( const tbb::blocked_range<size_t> & subRange )
63 {
64 auto c = callMaker();
65 const auto bitSubRange = BitSetParallel::bitSubRange( bitRange, range, subRange );
66 for ( auto id = bitSubRange.beg; id < bitSubRange.end; ++id )
67 c( f, id, bitSubRange );
68 } );
69}
70
71template <typename BS, typename CM, typename F>
72inline void ForAllRanged( const BS & bs, const CM & callMaker, F && f )
73{
74 ForAllRanged( bitRange( bs ), callMaker, std::forward<F>( f ) );
75}
76
77template <typename IndexType, typename CM, typename F>
78bool ForAllRanged( const IdRange<IndexType> & bitRange, const CM & callMaker, F && f, ProgressCallback progressCb, size_t reportProgressEveryBit = 1024 )
79{
80 if ( !progressCb )
81 {
82 ForAllRanged( bitRange, callMaker, std::forward<F>( f ) );
83 return true;
84 }
85
86 auto callingThreadId = std::this_thread::get_id();
87 std::atomic<bool> keepGoing{ true };
88
89 // avoid false sharing with other local variables
90 // by putting processedBits in its own cache line
91 constexpr int hardware_destructive_interference_size = 64;
92 struct alignas( hardware_destructive_interference_size ) S
93 {
94 std::atomic<size_t> processedBits{ 0 };
95 } s;
96 static_assert( alignof( S ) == hardware_destructive_interference_size );
97 static_assert( sizeof( S ) == hardware_destructive_interference_size );
98
99 const auto range = BitSetParallel::blockRange( bitRange );
100 tbb::parallel_for( range, [&] ( const tbb::blocked_range<size_t>& subRange )
101 {
102 const auto bitSubRange = BitSetParallel::bitSubRange( bitRange, range, subRange );
103 size_t myProcessedBits = 0;
104 const bool report = std::this_thread::get_id() == callingThreadId;
105 auto c = callMaker();
106 for ( auto id = bitSubRange.beg; id < bitSubRange.end; ++id )
107 {
108 if ( !keepGoing.load( std::memory_order_relaxed ) )
109 break;
110 c( f, id, bitSubRange );
111 if ( ( ++myProcessedBits % reportProgressEveryBit ) == 0 )
112 {
113 if ( report )
114 {
115 if ( !progressCb( float( myProcessedBits + s.processedBits.load( std::memory_order_relaxed ) ) / bitRange.size() ) )
116 keepGoing.store( false, std::memory_order_relaxed );
117 }
118 else
119 {
120 s.processedBits.fetch_add( myProcessedBits, std::memory_order_relaxed );
121 myProcessedBits = 0;
122 }
123 }
124 }
125 const auto total = s.processedBits.fetch_add( myProcessedBits, std::memory_order_relaxed );
126 if ( report && !progressCb( float( total ) / bitRange.size() ) )
127 keepGoing.store( false, std::memory_order_relaxed );
128 } );
129 return keepGoing.load( std::memory_order_relaxed );
130}
131
132template <typename BS, typename CM, typename F>
133inline bool ForAllRanged( const BS & bs, const CM & callMaker, F && f, ProgressCallback progressCb, size_t reportProgressEveryBit = 1024 )
134{
135 return ForAllRanged( bitRange( bs ), callMaker, std::forward<F>( f ), progressCb, reportProgressEveryBit );
136}
137
138} // namespace BitSetParallel
139
145template <typename BS, typename ...F>
146inline auto BitSetParallelForAllRanged( const BS & bs, F &&... f )
147{
148 return BitSetParallel::ForAllRanged( bs, Parallel::CallSimplyMaker{}, std::forward<F>( f )... );
149}
150
157template <typename BS, typename L, typename ...F>
158inline auto BitSetParallelForAllRanged( const BS & bs, tbb::enumerable_thread_specific<L> & e, F &&... f )
159{
160 return BitSetParallel::ForAllRanged( bs, Parallel::CallWithTLSMaker<L>{ e }, std::forward<F>( f )... );
161}
162
167template <typename BS, typename F, typename ...Cb>
168inline auto BitSetParallelForAll( const BS & bs, F && f, Cb&&... cb )
169{
170 return BitSetParallel::ForAllRanged( bs, Parallel::CallSimplyMaker{}, [&f]( auto bit, auto && ) { f( bit ); }, std::forward<Cb>( cb )... );
171}
172
178template <typename BS, typename L, typename F, typename ...Cb>
179inline auto BitSetParallelForAll( const BS & bs, tbb::enumerable_thread_specific<L> & e, F && f, Cb&&... cb )
180{
181 return BitSetParallel::ForAllRanged( bs, Parallel::CallWithTLSMaker<L>{ e }, [&f]( auto bit, auto &&, auto & tls ) { f( bit, tls ); }, std::forward<Cb>( cb )... );
182}
183
188template <typename BS, typename F, typename ...Cb>
189inline auto BitSetParallelFor( const BS& bs, F && f, Cb&&... cb )
190{
191 return BitSetParallelForAll( bs, [&]( auto bit ) { if ( bs.test( bit ) ) f( bit ); }, std::forward<Cb>( cb )... );
192}
193
199template <typename BS, typename L, typename F, typename ...Cb>
200inline auto BitSetParallelFor( const BS& bs, tbb::enumerable_thread_specific<L> & e, F && f, Cb&&... cb )
201{
202 return BitSetParallelForAll( bs, e, [&]( auto bit, auto & tls ) { if ( bs.test( bit ) ) f( bit, tls ); }, std::forward<Cb>( cb )... );
203}
204
206
207} // namespace MR
Definition MRMesh/MRId.h:13
auto BitSetParallelForAll(const BS &bs, F &&f, Cb &&... cb)
Definition MRBitSetParallelFor.h:168
auto BitSetParallelForAllRanged(const BS &bs, F &&... f)
Definition MRBitSetParallelFor.h:146
auto BitSetParallelFor(const BS &bs, F &&f, Cb &&... cb)
Definition MRBitSetParallelFor.h:189
std::function< bool(float)> ProgressCallback
Definition MRMesh/MRMeshFwd.h:576
void ForAllRanged(const IdRange< IndexType > &bitRange, const CM &callMaker, F &&f)
Definition MRBitSetParallelFor.h:59
auto bitRange(const BS &bs)
Definition MRBitSetParallelFor.h:43
auto blockRange(const IdRange< IndexType > &bitRange)
Definition MRBitSetParallelFor.h:28
auto bitSubRange(const IdRange< IndexType > &bitRange, const tbb::blocked_range< size_t > &range, const tbb::blocked_range< size_t > &subRange)
Definition MRBitSetParallelFor.h:49
Definition MRCameraOrientationPlugin.h:8
range of indices [beg, end)
Definition MRBitSetParallelFor.h:19
auto size() const
Definition MRBitSetParallelFor.h:21
Id beg
Definition MRBitSetParallelFor.h:20
Id end
Definition MRBitSetParallelFor.h:20
Definition MRParallel.h:18
Definition MRParallel.h:32