xref: /oneTBB/test/tbb/test_numa_dist.cpp (revision 104fb5c9)
1 /*
2     Copyright (c) 2005-2022 Intel Corporation
3 
4     Licensed under the Apache License, Version 2.0 (the "License");
5     you may not use this file except in compliance with the License.
6     You may obtain a copy of the License at
7 
8         http://www.apache.org/licenses/LICENSE-2.0
9 
10     Unless required by applicable law or agreed to in writing, software
11     distributed under the License is distributed on an "AS IS" BASIS,
12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13     See the License for the specific language governing permissions and
14     limitations under the License.
15 */
16 
17 #include "common/test.h"
18 
19 #include <stdio.h>
20 #include "tbb/parallel_for.h"
21 #include "tbb/global_control.h"
22 #include "tbb/enumerable_thread_specific.h"
23 
24 #include "common/config.h"
25 #include "common/utils.h"
26 #include "common/utils_concurrency_limit.h"
27 #include "common/utils_report.h"
28 #include "common/vector_types.h"
29 #include "common/cpu_usertime.h"
30 #include "common/spin_barrier.h"
31 #include "common/exception_handling.h"
32 #include "common/concepts_common.h"
33 #include "test_partitioner.h"
34 
35 #include <cstddef>
36 #include <vector>
37 
38 //! \file test_numa_dist.cpp
39 //! \brief Test for [internal] functionality
40 #if _MSC_VER
41 #pragma warning (push)
42 // Suppress conditional expression is constant
43 #pragma warning (disable: 4127)
44 #if __TBB_MSVC_UNREACHABLE_CODE_IGNORED
45     // Suppress pointless "unreachable code" warning.
46     #pragma warning (disable: 4702)
47 #endif
48 #if defined(_Wp64)
49     // Workaround for overzealous compiler warnings in /Wp64 mode
50     #pragma warning (disable: 4267)
51 #endif
52 #define _SCL_SECURE_NO_WARNINGS
53 #endif //#if _MSC_VER
54 
55 
56 struct numa {
57     WORD processorGroupCount;
58     std::vector<DWORD> numaProcessors;
59     DWORD maxProcessors;
60     numa() : processorGroupCount(GetMaximumProcessorGroupCount()), maxProcessors(GetActiveProcessorCount(ALL_PROCESSOR_GROUPS)){
61         numaProcessors.resize(processorGroupCount);
62        for (WORD i = 0; i < processorGroupCount; i++) {
63             this->numaProcessors[i] = GetActiveProcessorCount((i));
64         }
65     }
66 };
67 
68 
69 int TestNumaDistribution(std::vector<DWORD> &validateProcgrp, int additionalParallelism, bool allThreads){
70     validateProcgrp.resize(GetMaximumProcessorGroupCount());
71     PROCESSOR_NUMBER proc;
72     struct numa nodes;
73     GetThreadIdealProcessorEx(GetCurrentThread(), &proc);
74     int master_thread_proc_grp = proc.Group;
75     int requested_parallelism;
76     if (allThreads)
77         requested_parallelism = additionalParallelism;
78     else
79         requested_parallelism = nodes.numaProcessors.at(master_thread_proc_grp) + additionalParallelism;
80     tbb::global_control global_limit(oneapi::tbb::global_control::max_allowed_parallelism, 1024);
81     tbb::enumerable_thread_specific< std::pair<int, int> > tls;
82     tbb::enumerable_thread_specific< double > tls_dummy;
83     tbb::static_partitioner s;
84 
85     utils::SpinBarrier sb(requested_parallelism);
86     oneapi::tbb::task_arena limited(requested_parallelism);
87     limited.execute([&]() {
88 
89         tbb::parallel_for(0, requested_parallelism, [&](int)
90             {
91                 PROCESSOR_NUMBER proc;
92                 if (GetThreadIdealProcessorEx(GetCurrentThread(), &proc))
93                 {
94                     tls.local() = std::pair<int, int>(proc.Group, proc.Number);
95                     sb.wait();
96                 }
97             }, s);
98         for (const auto& it : tls) {
99            validateProcgrp[it.first]++;
100         }
101       });
102 
103 
104     return master_thread_proc_grp;
105 }
106 
107 //! Testing Numa Thread Distribution Stability
108 //! \brief \ref stress
109 TEST_CASE("Numa stability for the same node") {
110     numa example;
111     std::vector<DWORD> validateProcgrp;
112 
113     int numaGrp = TestNumaDistribution(validateProcgrp,0, 0);
114     std::vector<DWORD> result(GetMaximumProcessorGroupCount(), 0);
115     result[numaGrp] = example.numaProcessors[numaGrp];
116     REQUIRE(validateProcgrp == result);
117 }
118 
119 //! Testing Numa Thread Distribution Overflow
120 //! \brief \ref stress
121 TEST_CASE("Numa overflow") {
122     numa example;
123     std::vector<DWORD> validateProcgrp;
124 
125     int numaGrp = TestNumaDistribution(validateProcgrp, 1, 0);
126     std::vector<DWORD> result(GetMaximumProcessorGroupCount(), 0);
127     if (example.processorGroupCount <= 1) { // for single Numa node
128        result[numaGrp] = example.numaProcessors[numaGrp] + 1;
129     } else {
130        result[numaGrp] = example.numaProcessors[numaGrp];
131        result[(numaGrp+1)% GetMaximumProcessorGroupCount()] = 1;
132     }
133     REQUIRE(validateProcgrp == result);
134 }
135 
136 //! Testing Numa Thread Distribution Maximum
137 //! \brief \ref stress
138 TEST_CASE("Numa all threads") {
139     numa example;
140     std::vector<DWORD> validateProcgrp;
141     TestNumaDistribution(validateProcgrp, example.maxProcessors, 1);
142     REQUIRE(validateProcgrp == example.numaProcessors);
143 }
144 
145 //! Testing Numa Thread Distribution Doubled Max
146 //! \brief \ref stress
147 TEST_CASE("Double threads") {
148     numa example;
149     std::vector<DWORD> validateProcgrp;
150     std::vector<DWORD> result(example.numaProcessors.size(), 0);
151     for (size_t i = 0; i < example.numaProcessors.size(); i++) result[i] = 2 * example.numaProcessors[i];
152     TestNumaDistribution(validateProcgrp, example.maxProcessors * 2, 1);
153     REQUIRE(validateProcgrp == result);
154 }
155 
156 #if _MSC_VER
157 #pragma warning (pop)
158 #endif
159