xref: /oneTBB/src/tbbbind/tbb_bind.cpp (revision 51c0b2f7)
1*51c0b2f7Stbbdev /*
2*51c0b2f7Stbbdev     Copyright (c) 2019-2020 Intel Corporation
3*51c0b2f7Stbbdev 
4*51c0b2f7Stbbdev     Licensed under the Apache License, Version 2.0 (the "License");
5*51c0b2f7Stbbdev     you may not use this file except in compliance with the License.
6*51c0b2f7Stbbdev     You may obtain a copy of the License at
7*51c0b2f7Stbbdev 
8*51c0b2f7Stbbdev         http://www.apache.org/licenses/LICENSE-2.0
9*51c0b2f7Stbbdev 
10*51c0b2f7Stbbdev     Unless required by applicable law or agreed to in writing, software
11*51c0b2f7Stbbdev     distributed under the License is distributed on an "AS IS" BASIS,
12*51c0b2f7Stbbdev     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*51c0b2f7Stbbdev     See the License for the specific language governing permissions and
14*51c0b2f7Stbbdev     limitations under the License.
15*51c0b2f7Stbbdev */
16*51c0b2f7Stbbdev 
17*51c0b2f7Stbbdev #include "../tbb/assert_impl.h" // Out-of-line TBB assertion handling routines are instantiated here.
18*51c0b2f7Stbbdev #include "tbb/detail/_assert.h"
19*51c0b2f7Stbbdev 
20*51c0b2f7Stbbdev #if _MSC_VER && !__INTEL_COMPILER
21*51c0b2f7Stbbdev #pragma warning( push )
22*51c0b2f7Stbbdev #pragma warning( disable : 4100 )
23*51c0b2f7Stbbdev #endif
24*51c0b2f7Stbbdev #include <hwloc.h>
25*51c0b2f7Stbbdev #if _MSC_VER && !__INTEL_COMPILER
26*51c0b2f7Stbbdev #pragma warning( pop )
27*51c0b2f7Stbbdev #endif
28*51c0b2f7Stbbdev 
29*51c0b2f7Stbbdev #include <vector>
30*51c0b2f7Stbbdev 
31*51c0b2f7Stbbdev // Most of hwloc calls returns negative exit code on error.
32*51c0b2f7Stbbdev // This macro tracks error codes that are returned from the hwloc interfaces.
33*51c0b2f7Stbbdev #define assertion_hwloc_wrapper(command, ...) \
34*51c0b2f7Stbbdev         __TBB_ASSERT_EX( (command(__VA_ARGS__)) >= 0, "Error occurred during call to hwloc API.");
35*51c0b2f7Stbbdev 
36*51c0b2f7Stbbdev namespace tbb {
37*51c0b2f7Stbbdev namespace detail {
38*51c0b2f7Stbbdev namespace r1 {
39*51c0b2f7Stbbdev 
40*51c0b2f7Stbbdev //------------------------------------------------------------------------
41*51c0b2f7Stbbdev // Information about the machine's hardware TBB is happen to work on
42*51c0b2f7Stbbdev //------------------------------------------------------------------------
43*51c0b2f7Stbbdev class platform_topology {
44*51c0b2f7Stbbdev     friend class numa_affinity_handler;
45*51c0b2f7Stbbdev 
46*51c0b2f7Stbbdev     // TODO: add the `my_` prefix to the members
47*51c0b2f7Stbbdev     hwloc_topology_t topology;
48*51c0b2f7Stbbdev     hwloc_cpuset_t   process_cpu_affinity_mask;
49*51c0b2f7Stbbdev     hwloc_nodeset_t  process_node_affinity_mask;
50*51c0b2f7Stbbdev     std::vector<hwloc_cpuset_t>  affinity_masks_list;
51*51c0b2f7Stbbdev 
52*51c0b2f7Stbbdev     std::vector<int> default_concurrency_list;
53*51c0b2f7Stbbdev     std::vector<int> numa_indexes_list;
54*51c0b2f7Stbbdev     int  numa_nodes_count;
55*51c0b2f7Stbbdev 
56*51c0b2f7Stbbdev     enum init_stages { uninitialized,
57*51c0b2f7Stbbdev                        started,
58*51c0b2f7Stbbdev                        topology_allocated,
59*51c0b2f7Stbbdev                        topology_loaded,
60*51c0b2f7Stbbdev                        topology_parsed } initialization_state;
61*51c0b2f7Stbbdev 
62*51c0b2f7Stbbdev     // Binding threads to NUMA nodes that locates in another Windows Processor groups
63*51c0b2f7Stbbdev     // is allowed only if machine topology contains several Windows Processors groups
64*51c0b2f7Stbbdev     // and process affinity mask wasn`t limited manually (affinity mask cannot violates
65*51c0b2f7Stbbdev     // processors group boundaries).
66*51c0b2f7Stbbdev     bool intergroup_binding_allowed(size_t groups_num) { return groups_num > 1; }
67*51c0b2f7Stbbdev 
68*51c0b2f7Stbbdev     platform_topology() : topology(nullptr),
69*51c0b2f7Stbbdev                           process_cpu_affinity_mask(nullptr),
70*51c0b2f7Stbbdev                           process_node_affinity_mask(nullptr),
71*51c0b2f7Stbbdev                           numa_nodes_count(0),
72*51c0b2f7Stbbdev                           initialization_state(uninitialized) {}
73*51c0b2f7Stbbdev 
74*51c0b2f7Stbbdev public:
75*51c0b2f7Stbbdev     typedef hwloc_cpuset_t             affinity_mask;
76*51c0b2f7Stbbdev     typedef hwloc_const_cpuset_t const_affinity_mask;
77*51c0b2f7Stbbdev 
78*51c0b2f7Stbbdev     static platform_topology& instance() {
79*51c0b2f7Stbbdev         static platform_topology topology;
80*51c0b2f7Stbbdev         return topology;
81*51c0b2f7Stbbdev     }
82*51c0b2f7Stbbdev 
83*51c0b2f7Stbbdev     bool is_topology_parsed() { return initialization_state == topology_parsed; }
84*51c0b2f7Stbbdev 
85*51c0b2f7Stbbdev     void initialize( size_t groups_num ) {
86*51c0b2f7Stbbdev         if ( initialization_state != uninitialized )
87*51c0b2f7Stbbdev             return;
88*51c0b2f7Stbbdev         initialization_state = started;
89*51c0b2f7Stbbdev 
90*51c0b2f7Stbbdev         // Parse topology
91*51c0b2f7Stbbdev         if ( hwloc_topology_init( &topology ) == 0 ) {
92*51c0b2f7Stbbdev             initialization_state = topology_allocated;
93*51c0b2f7Stbbdev             if ( hwloc_topology_load( topology ) == 0 ) {
94*51c0b2f7Stbbdev                 initialization_state = topology_loaded;
95*51c0b2f7Stbbdev             }
96*51c0b2f7Stbbdev         }
97*51c0b2f7Stbbdev 
98*51c0b2f7Stbbdev         // Fill parameters with stubs if topology parsing is broken.
99*51c0b2f7Stbbdev         if ( initialization_state != topology_loaded ) {
100*51c0b2f7Stbbdev             if ( initialization_state == topology_allocated ) {
101*51c0b2f7Stbbdev                 hwloc_topology_destroy(topology);
102*51c0b2f7Stbbdev             }
103*51c0b2f7Stbbdev             numa_nodes_count = 1;
104*51c0b2f7Stbbdev             numa_indexes_list.push_back(-1);
105*51c0b2f7Stbbdev             default_concurrency_list.push_back(-1);
106*51c0b2f7Stbbdev             return;
107*51c0b2f7Stbbdev         }
108*51c0b2f7Stbbdev 
109*51c0b2f7Stbbdev         // Getting process affinity mask
110*51c0b2f7Stbbdev         if ( intergroup_binding_allowed(groups_num) ) {
111*51c0b2f7Stbbdev             process_cpu_affinity_mask  = hwloc_bitmap_dup(hwloc_topology_get_complete_cpuset (topology));
112*51c0b2f7Stbbdev             process_node_affinity_mask = hwloc_bitmap_dup(hwloc_topology_get_complete_nodeset(topology));
113*51c0b2f7Stbbdev         } else {
114*51c0b2f7Stbbdev             process_cpu_affinity_mask  = hwloc_bitmap_alloc();
115*51c0b2f7Stbbdev             process_node_affinity_mask = hwloc_bitmap_alloc();
116*51c0b2f7Stbbdev 
117*51c0b2f7Stbbdev             assertion_hwloc_wrapper(hwloc_get_cpubind, topology, process_cpu_affinity_mask, 0);
118*51c0b2f7Stbbdev             hwloc_cpuset_to_nodeset(topology, process_cpu_affinity_mask, process_node_affinity_mask);
119*51c0b2f7Stbbdev         }
120*51c0b2f7Stbbdev 
121*51c0b2f7Stbbdev         // Get the number of available NUMA nodes
122*51c0b2f7Stbbdev         // If system contains no NUMA nodes, HWLOC 1.11 returns an infinitely filled bitmap.
123*51c0b2f7Stbbdev         // hwloc_bitmap_weight() returns negative value for such bitmaps, so we use this check
124*51c0b2f7Stbbdev         // to change way of topology initialization.
125*51c0b2f7Stbbdev         numa_nodes_count = hwloc_bitmap_weight(process_node_affinity_mask);
126*51c0b2f7Stbbdev         if (numa_nodes_count <= 0) {
127*51c0b2f7Stbbdev             // numa_nodes_count may be empty if the process affinity mask is empty too (invalid case)
128*51c0b2f7Stbbdev             // or if some internal HWLOC error occurred.
129*51c0b2f7Stbbdev             // So we place -1 as index in this case.
130*51c0b2f7Stbbdev             numa_indexes_list.push_back(numa_nodes_count == 0 ? -1 : 0);
131*51c0b2f7Stbbdev             numa_nodes_count = 1;
132*51c0b2f7Stbbdev             default_concurrency_list.push_back(hwloc_bitmap_weight(process_cpu_affinity_mask));
133*51c0b2f7Stbbdev 
134*51c0b2f7Stbbdev             affinity_masks_list.push_back(hwloc_bitmap_dup(process_cpu_affinity_mask));
135*51c0b2f7Stbbdev             initialization_state = topology_parsed;
136*51c0b2f7Stbbdev             return;
137*51c0b2f7Stbbdev         }
138*51c0b2f7Stbbdev 
139*51c0b2f7Stbbdev         // Get NUMA logical indexes list
140*51c0b2f7Stbbdev         unsigned counter = 0;
141*51c0b2f7Stbbdev         int i = 0;
142*51c0b2f7Stbbdev         int max_numa_index = -1;
143*51c0b2f7Stbbdev         numa_indexes_list.resize(numa_nodes_count);
144*51c0b2f7Stbbdev         hwloc_obj_t node_buffer;
145*51c0b2f7Stbbdev         hwloc_bitmap_foreach_begin(i, process_node_affinity_mask) {
146*51c0b2f7Stbbdev             node_buffer = hwloc_get_obj_by_type(topology, HWLOC_OBJ_NUMANODE, i);
147*51c0b2f7Stbbdev             numa_indexes_list[counter] = static_cast<int>(node_buffer->logical_index);
148*51c0b2f7Stbbdev 
149*51c0b2f7Stbbdev             if ( numa_indexes_list[counter] > max_numa_index ) {
150*51c0b2f7Stbbdev                 max_numa_index = numa_indexes_list[counter];
151*51c0b2f7Stbbdev             }
152*51c0b2f7Stbbdev 
153*51c0b2f7Stbbdev             counter++;
154*51c0b2f7Stbbdev         } hwloc_bitmap_foreach_end();
155*51c0b2f7Stbbdev         __TBB_ASSERT(max_numa_index >= 0, "Maximal NUMA index must not be negative");
156*51c0b2f7Stbbdev 
157*51c0b2f7Stbbdev         // Fill concurrency and affinity masks lists
158*51c0b2f7Stbbdev         default_concurrency_list.resize(max_numa_index + 1);
159*51c0b2f7Stbbdev         affinity_masks_list.resize(max_numa_index + 1);
160*51c0b2f7Stbbdev 
161*51c0b2f7Stbbdev         int index = 0;
162*51c0b2f7Stbbdev         hwloc_bitmap_foreach_begin(i, process_node_affinity_mask) {
163*51c0b2f7Stbbdev             node_buffer = hwloc_get_obj_by_type(topology, HWLOC_OBJ_NUMANODE, i);
164*51c0b2f7Stbbdev             index = static_cast<int>(node_buffer->logical_index);
165*51c0b2f7Stbbdev 
166*51c0b2f7Stbbdev             hwloc_cpuset_t& current_mask = affinity_masks_list[index];
167*51c0b2f7Stbbdev             current_mask = hwloc_bitmap_dup(node_buffer->cpuset);
168*51c0b2f7Stbbdev 
169*51c0b2f7Stbbdev             hwloc_bitmap_and(current_mask, current_mask, process_cpu_affinity_mask);
170*51c0b2f7Stbbdev             __TBB_ASSERT(!hwloc_bitmap_iszero(current_mask), "hwloc detected unavailable NUMA node");
171*51c0b2f7Stbbdev             default_concurrency_list[index] = hwloc_bitmap_weight(current_mask);
172*51c0b2f7Stbbdev         } hwloc_bitmap_foreach_end();
173*51c0b2f7Stbbdev         initialization_state = topology_parsed;
174*51c0b2f7Stbbdev     }
175*51c0b2f7Stbbdev 
176*51c0b2f7Stbbdev     ~platform_topology() {
177*51c0b2f7Stbbdev         if ( is_topology_parsed() ) {
178*51c0b2f7Stbbdev             for (int i = 0; i < numa_nodes_count; i++) {
179*51c0b2f7Stbbdev                 hwloc_bitmap_free(affinity_masks_list[numa_indexes_list[i]]);
180*51c0b2f7Stbbdev             }
181*51c0b2f7Stbbdev             hwloc_bitmap_free(process_node_affinity_mask);
182*51c0b2f7Stbbdev             hwloc_bitmap_free(process_cpu_affinity_mask);
183*51c0b2f7Stbbdev         }
184*51c0b2f7Stbbdev 
185*51c0b2f7Stbbdev         if ( initialization_state >= topology_allocated ) {
186*51c0b2f7Stbbdev             hwloc_topology_destroy(topology);
187*51c0b2f7Stbbdev         }
188*51c0b2f7Stbbdev 
189*51c0b2f7Stbbdev         initialization_state = uninitialized;
190*51c0b2f7Stbbdev     }
191*51c0b2f7Stbbdev 
192*51c0b2f7Stbbdev     void fill(int& nodes_count, int*& indexes_list, int*& concurrency_list ) {
193*51c0b2f7Stbbdev         __TBB_ASSERT(is_topology_parsed(), "Trying to get access to uninitialized platform_topology");
194*51c0b2f7Stbbdev         nodes_count = numa_nodes_count;
195*51c0b2f7Stbbdev         indexes_list = &numa_indexes_list.front();
196*51c0b2f7Stbbdev         concurrency_list = &default_concurrency_list.front();
197*51c0b2f7Stbbdev     }
198*51c0b2f7Stbbdev 
199*51c0b2f7Stbbdev     affinity_mask allocate_process_affinity_mask() {
200*51c0b2f7Stbbdev         __TBB_ASSERT(is_topology_parsed(), "Trying to get access to uninitialized platform_topology");
201*51c0b2f7Stbbdev         return hwloc_bitmap_dup(process_cpu_affinity_mask);
202*51c0b2f7Stbbdev     }
203*51c0b2f7Stbbdev 
204*51c0b2f7Stbbdev     void free_affinity_mask( affinity_mask mask_to_free ) {
205*51c0b2f7Stbbdev         hwloc_bitmap_free(mask_to_free); // If bitmap is nullptr, no operation is performed.
206*51c0b2f7Stbbdev     }
207*51c0b2f7Stbbdev 
208*51c0b2f7Stbbdev     void store_current_affinity_mask( affinity_mask current_mask ) {
209*51c0b2f7Stbbdev         assertion_hwloc_wrapper(hwloc_get_cpubind, topology, current_mask, HWLOC_CPUBIND_THREAD);
210*51c0b2f7Stbbdev 
211*51c0b2f7Stbbdev         hwloc_bitmap_and(current_mask, current_mask, process_cpu_affinity_mask);
212*51c0b2f7Stbbdev         __TBB_ASSERT(!hwloc_bitmap_iszero(current_mask),
213*51c0b2f7Stbbdev             "Current affinity mask must intersects with process affinity mask");
214*51c0b2f7Stbbdev     }
215*51c0b2f7Stbbdev 
216*51c0b2f7Stbbdev     void set_new_affinity_mask( const_affinity_mask new_mask ) {
217*51c0b2f7Stbbdev         assertion_hwloc_wrapper(hwloc_set_cpubind, topology, new_mask, HWLOC_CPUBIND_THREAD);
218*51c0b2f7Stbbdev     }
219*51c0b2f7Stbbdev 
220*51c0b2f7Stbbdev     const_affinity_mask get_node_affinity_mask( int node_index ) {
221*51c0b2f7Stbbdev         __TBB_ASSERT((int)affinity_masks_list.size() > node_index,
222*51c0b2f7Stbbdev             "Trying to get affinity mask for uninitialized NUMA node");
223*51c0b2f7Stbbdev         return affinity_masks_list[node_index];
224*51c0b2f7Stbbdev     }
225*51c0b2f7Stbbdev };
226*51c0b2f7Stbbdev 
227*51c0b2f7Stbbdev class binding_handler {
228*51c0b2f7Stbbdev     // Following vector saves thread affinity mask on scheduler entry to return it to this thread
229*51c0b2f7Stbbdev     // on scheduler exit.
230*51c0b2f7Stbbdev     typedef std::vector<platform_topology::affinity_mask> affinity_masks_container;
231*51c0b2f7Stbbdev     affinity_masks_container affinity_backup;
232*51c0b2f7Stbbdev 
233*51c0b2f7Stbbdev public:
234*51c0b2f7Stbbdev     binding_handler( size_t size ) : affinity_backup(size) {
235*51c0b2f7Stbbdev         for (affinity_masks_container::iterator it = affinity_backup.begin();
236*51c0b2f7Stbbdev              it != affinity_backup.end(); it++) {
237*51c0b2f7Stbbdev             *it = platform_topology::instance().allocate_process_affinity_mask();
238*51c0b2f7Stbbdev         }
239*51c0b2f7Stbbdev     }
240*51c0b2f7Stbbdev 
241*51c0b2f7Stbbdev     ~binding_handler() {
242*51c0b2f7Stbbdev         for (affinity_masks_container::iterator it = affinity_backup.begin();
243*51c0b2f7Stbbdev              it != affinity_backup.end(); it++) {
244*51c0b2f7Stbbdev             platform_topology::instance().free_affinity_mask(*it);
245*51c0b2f7Stbbdev         }
246*51c0b2f7Stbbdev     }
247*51c0b2f7Stbbdev 
248*51c0b2f7Stbbdev     void bind_thread_to_node( unsigned slot_num, unsigned numa_node_id ) {
249*51c0b2f7Stbbdev         __TBB_ASSERT(slot_num < affinity_backup.size(),
250*51c0b2f7Stbbdev             "The slot number is greater than the number of slots in the arena");
251*51c0b2f7Stbbdev         __TBB_ASSERT(platform_topology::instance().is_topology_parsed(),
252*51c0b2f7Stbbdev             "Trying to get access to uninitialized platform_topology");
253*51c0b2f7Stbbdev         platform_topology::instance().store_current_affinity_mask(affinity_backup[slot_num]);
254*51c0b2f7Stbbdev 
255*51c0b2f7Stbbdev         platform_topology::instance().set_new_affinity_mask(
256*51c0b2f7Stbbdev             platform_topology::instance().get_node_affinity_mask(numa_node_id));
257*51c0b2f7Stbbdev     }
258*51c0b2f7Stbbdev 
259*51c0b2f7Stbbdev     void restore_previous_affinity_mask( unsigned slot_num ) {
260*51c0b2f7Stbbdev         __TBB_ASSERT(platform_topology::instance().is_topology_parsed(),
261*51c0b2f7Stbbdev             "Trying to get access to uninitialized platform_topology");
262*51c0b2f7Stbbdev         platform_topology::instance().set_new_affinity_mask(affinity_backup[slot_num]);
263*51c0b2f7Stbbdev     };
264*51c0b2f7Stbbdev 
265*51c0b2f7Stbbdev };
266*51c0b2f7Stbbdev 
267*51c0b2f7Stbbdev extern "C" { // exported to TBB interfaces
268*51c0b2f7Stbbdev 
269*51c0b2f7Stbbdev void __TBB_internal_initialize_numa_topology( size_t groups_num, int& nodes_count, int*& indexes_list, int*& concurrency_list ) {
270*51c0b2f7Stbbdev     platform_topology::instance().initialize(groups_num);
271*51c0b2f7Stbbdev     platform_topology::instance().fill(nodes_count, indexes_list, concurrency_list);
272*51c0b2f7Stbbdev }
273*51c0b2f7Stbbdev 
274*51c0b2f7Stbbdev binding_handler* __TBB_internal_allocate_binding_handler(int slot_num) {
275*51c0b2f7Stbbdev     __TBB_ASSERT(slot_num > 0, "Trying to create numa handler for 0 threads.");
276*51c0b2f7Stbbdev     return new binding_handler(slot_num);
277*51c0b2f7Stbbdev }
278*51c0b2f7Stbbdev 
279*51c0b2f7Stbbdev void __TBB_internal_deallocate_binding_handler(binding_handler* handler_ptr) {
280*51c0b2f7Stbbdev     __TBB_ASSERT(handler_ptr != nullptr, "Trying to deallocate nullptr pointer.");
281*51c0b2f7Stbbdev     delete handler_ptr;
282*51c0b2f7Stbbdev }
283*51c0b2f7Stbbdev 
284*51c0b2f7Stbbdev void __TBB_internal_bind_to_node(binding_handler* handler_ptr, int slot_num, int numa_id) {
285*51c0b2f7Stbbdev     __TBB_ASSERT(handler_ptr != nullptr, "Trying to get access to uninitialized metadata.");
286*51c0b2f7Stbbdev     __TBB_ASSERT(platform_topology::instance().is_topology_parsed(),
287*51c0b2f7Stbbdev         "Trying to get access to uninitialized platform_topology.");
288*51c0b2f7Stbbdev     handler_ptr->bind_thread_to_node(slot_num, numa_id);
289*51c0b2f7Stbbdev }
290*51c0b2f7Stbbdev 
291*51c0b2f7Stbbdev void __TBB_internal_restore_affinity(binding_handler* handler_ptr, int slot_num) {
292*51c0b2f7Stbbdev     __TBB_ASSERT(handler_ptr != nullptr, "Trying to get access to uninitialized metadata.");
293*51c0b2f7Stbbdev     __TBB_ASSERT(platform_topology::instance().is_topology_parsed(),
294*51c0b2f7Stbbdev         "Trying to get access to uninitialized platform_topology.");
295*51c0b2f7Stbbdev     handler_ptr->restore_previous_affinity_mask(slot_num);
296*51c0b2f7Stbbdev }
297*51c0b2f7Stbbdev 
298*51c0b2f7Stbbdev } // extern "C"
299*51c0b2f7Stbbdev 
300*51c0b2f7Stbbdev } // namespace r1
301*51c0b2f7Stbbdev } // namespace detail
302*51c0b2f7Stbbdev } // namespace tbb
303*51c0b2f7Stbbdev 
304*51c0b2f7Stbbdev #undef assertion_hwloc_wrapper
305