blob: 583cb40ecae6fe2c9cfd7ed524c7ddc4789cc56d [file] [log] [blame]
Moritz Pflanzerbeabe3b2017-08-31 14:56:32 +01001/*
Pablo Tello456e4792018-01-31 17:27:09 +00002 * Copyright (c) 2016-2018 ARM Limited.
Moritz Pflanzerbeabe3b2017-08-31 14:56:32 +01003 *
4 * SPDX-License-Identifier: MIT
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all
14 * copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24#include "arm_compute/runtime/IScheduler.h"
25
26#include <array>
27#include <cstdlib>
28#include <cstring>
29#include <fcntl.h>
Georgios Pinitas53d12272018-02-01 20:23:25 +000030#include <fstream>
31#include <map>
Moritz Pflanzerbeabe3b2017-08-31 14:56:32 +010032#include <sched.h>
33#include <sys/stat.h>
34#include <sys/types.h>
35#include <unistd.h>
36
Georgios Pinitas53d12272018-02-01 20:23:25 +000037#ifndef BARE_METAL
38#include <regex>
39#include <thread>
40#endif /* BARE_METAL */
41
Moritz Pflanzerbeabe3b2017-08-31 14:56:32 +010042namespace
43{
Georgios Pinitas53d12272018-02-01 20:23:25 +000044unsigned int get_threads_hint()
45{
46 unsigned int num_threads_hint = 1;
47
48#ifndef BARE_METAL
49 std::map<std::string, unsigned int> cpu_part_occurrence_map;
50
51 // CPU part regex
52 std::regex cpu_part_rgx(R"(.*CPU part.+?(?=:).+?(?=\w+)(\w+).*)");
53 std::smatch cpu_part_match;
54
55 // Read cpuinfo and get occurrence of each core
56 std::ifstream cpuinfo;
57 cpuinfo.open("/proc/cpuinfo", std::ios::in);
58 if(cpuinfo.is_open())
59 {
60 std::string line;
61 while(bool(getline(cpuinfo, line)))
62 {
63 if(std::regex_search(line.cbegin(), line.cend(), cpu_part_match, cpu_part_rgx))
64 {
65 std::string cpu_part = cpu_part_match[1];
66 if(cpu_part_occurrence_map.find(cpu_part) != cpu_part_occurrence_map.end())
67 {
68 cpu_part_occurrence_map[cpu_part]++;
69 }
70 else
71 {
72 cpu_part_occurrence_map[cpu_part] = 1;
73 }
74 }
75 }
76 }
77
78 // Get min number of threads
79 auto min_common_cores = std::min_element(cpu_part_occurrence_map.begin(), cpu_part_occurrence_map.end(),
80 [](const std::pair<std::string, unsigned int> &p1, const std::pair<std::string, unsigned int> &p2)
81 {
82 return p1.second < p2.second;
83 });
84
85 // Set thread hint
86 num_threads_hint = cpu_part_occurrence_map.empty() ? std::thread::hardware_concurrency() : min_common_cores->second;
87#endif /* BARE_METAL */
88
89 return num_threads_hint;
90}
91
Moritz Pflanzerbeabe3b2017-08-31 14:56:32 +010092unsigned int get_cpu_impl()
93{
94#ifndef BARE_METAL
95 int fd = open("/proc/cpuinfo", 0); // NOLINT
Pablo Tello456e4792018-01-31 17:27:09 +000096 std::array<char, 3000> buff{ {} };
Moritz Pflanzerbeabe3b2017-08-31 14:56:32 +010097 char *pos = nullptr;
98 char *end = nullptr;
99 bool foundid = false;
100
101 int cpu = sched_getcpu();
102
103 if(fd == -1)
104 {
105 return 0;
106 }
107
Pablo Tello456e4792018-01-31 17:27:09 +0000108 int charsread = read(fd, buff.data(), 3000);
Moritz Pflanzerbeabe3b2017-08-31 14:56:32 +0100109 pos = buff.data();
110 end = buff.data() + charsread;
111
112 close(fd);
113
114 /* So, to date I've encountered two formats for /proc/cpuinfo.
115 *
116 * One of them just lists processor : n for each processor (with no
117 * other info), then at the end lists part information for the current
118 * CPU.
119 *
120 * The other has an entire clause (including part number info) for each
121 * CPU in the system, with "processor : n" headers.
122 *
123 * We can cope with either of these formats by waiting to see
124 * "processor: n" (where n = our CPU ID), and then looking for the next
125 * "CPU part" field.
126 */
127 while(pos < end)
128 {
129 if(foundid && strncmp(pos, "CPU part", 8) == 0)
130 {
131 /* Found part number */
132 pos += 11;
133
134 for(char *ch = pos; ch < end; ch++)
135 {
136 if(*ch == '\n')
137 {
138 *ch = '\0';
139 break;
140 }
141 }
142
143 return strtoul(pos, nullptr, 0);
144 }
145
146 if(strncmp(pos, "processor", 9) == 0)
147 {
148 /* Found processor ID, see if it's ours. */
149 pos += 11;
150
151 for(char *ch = pos; ch < end; ch++)
152 {
153 if(*ch == '\n')
154 {
155 *ch = '\0';
156 break;
157 }
158 }
159
160 int num = strtol(pos, nullptr, 0);
161
162 if(num == cpu)
163 {
164 foundid = true;
165 }
166 }
167
168 while(pos < end)
169 {
170 char ch = *pos++;
171 if(ch == '\n' || ch == '\0')
172 {
173 break;
174 }
175 }
176 }
177#endif /* BARE_METAL */
178
179 return 0;
180}
181} // namespace
182
183namespace arm_compute
184{
185IScheduler::IScheduler()
186{
Georgios Pinitas53d12272018-02-01 20:23:25 +0000187 // Work out the best possible number of execution threads
188 _num_threads_hint = get_threads_hint();
189
190 // Work out the CPU implementation
Moritz Pflanzerbeabe3b2017-08-31 14:56:32 +0100191 switch(get_cpu_impl())
192 {
Pablo Tello6f6639d2017-12-04 17:30:07 +0000193 case 0xd0f:
194 _info.CPU = CPUTarget::A55_DOT;
195 break;
Moritz Pflanzerbeabe3b2017-08-31 14:56:32 +0100196 case 0xd03:
197 _info.CPU = CPUTarget::A53;
198 break;
199 default:
Moritz Pflanzer80373f62017-09-15 10:42:58 +0100200#ifdef __arm__
201 _info.CPU = CPUTarget::ARMV7;
202#elif __aarch64__
Moritz Pflanzerbeabe3b2017-08-31 14:56:32 +0100203 _info.CPU = CPUTarget::ARMV8;
Moritz Pflanzer80373f62017-09-15 10:42:58 +0100204#else /* __arm__ || __aarch64__ */
Moritz Pflanzerbeabe3b2017-08-31 14:56:32 +0100205 _info.CPU = CPUTarget::INTRINSICS;
Moritz Pflanzer80373f62017-09-15 10:42:58 +0100206#endif /* __arm__ || __aarch64__ */
Moritz Pflanzerbeabe3b2017-08-31 14:56:32 +0100207 break;
208 }
209
210 _info.L1_size = 31000;
211 _info.L2_size = 500000;
212}
213
214void IScheduler::set_target(CPUTarget target)
215{
216 _info.CPU = target;
217}
218
219CPUInfo IScheduler::cpu_info() const
220{
221 return _info;
222}
Georgios Pinitas53d12272018-02-01 20:23:25 +0000223
224unsigned int IScheduler::num_threads_hint() const
225{
226 return _num_threads_hint;
227}
Moritz Pflanzerbeabe3b2017-08-31 14:56:32 +0100228} // namespace arm_compute