1 //
2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5
6 #include "ClBackendContext.hpp"
7 #include "ClContextControl.hpp"
8
9 #include <armnn/Logging.hpp>
10 #include <armnn/utility/Assert.hpp>
11 #include <armnn/utility/PolymorphicDowncast.hpp>
12
13 #include <arm_compute/core/CL/OpenCL.h>
14 #include <arm_compute/core/CL/CLKernelLibrary.h>
15 #include <arm_compute/runtime/CL/CLScheduler.h>
16 #include <arm_compute/runtime/CL/CLTunerTypes.h>
17
18 namespace armnn
19 {
20
21 struct ClBackendContext::ClContextControlWrapper
22 {
ClContextControlWrapperarmnn::ClBackendContext::ClContextControlWrapper23 ClContextControlWrapper(arm_compute::CLTuner* tuner,
24 bool profilingEnabled)
25 : m_ClContextControl(tuner, profilingEnabled)
26 {}
27
Syncarmnn::ClBackendContext::ClContextControlWrapper28 bool Sync()
29 {
30 if (arm_compute::CLScheduler::get().context()() != NULL)
31 {
32 // Waits for all queued CL requests to finish before unloading the network they may be using.
33 try
34 {
35 // Coverity fix: arm_compute::CLScheduler::sync() may throw an exception of type cl::Error.
36 arm_compute::CLScheduler::get().sync();
37 }
38 catch (const cl::Error&)
39 {
40 ARMNN_LOG(warning) << "Runtime::UnloadNetwork(): an error occurred while waiting for "
41 "the queued CL requests to finish";
42 return false;
43 }
44 }
45
46 return true;
47 }
48
ClearClCachearmnn::ClBackendContext::ClContextControlWrapper49 void ClearClCache()
50 {
51 if (arm_compute::CLScheduler::get().context()() != NULL)
52 {
53 // There are no loaded networks left, so clear the CL cache to free up memory
54 m_ClContextControl.ClearClCache();
55 }
56 }
57
58 ClContextControl m_ClContextControl;
59 };
60
LowerString(std::string value)61 std::string LowerString(std::string value)
62 {
63 std::transform(value.begin(), value.end(), value.begin(),
64 [](unsigned char c){ return std::tolower(c); });
65
66 return value;
67 }
68
69 enum class TuningLevel
70 {
71 None,
72 Rapid,
73 Normal,
74 Exhaustive
75 };
76
77
ParseTuningLevel(const BackendOptions::Var & value,TuningLevel defaultValue)78 TuningLevel ParseTuningLevel(const BackendOptions::Var& value, TuningLevel defaultValue)
79 {
80 if (value.IsInt())
81 {
82 int v = value.AsInt();
83 if (v > static_cast<int>(TuningLevel::Exhaustive) ||
84 v < static_cast<int>(TuningLevel::None))
85 {
86 ARMNN_LOG(warning) << "Invalid GpuAcc tuning level ("<< v << ") selected. "
87 "Using default(" << static_cast<int>(defaultValue) << ")";
88 } else
89 {
90 return static_cast<TuningLevel>(v);
91 }
92 }
93 return defaultValue;
94 }
95
ParseBoolean(const BackendOptions::Var & value,bool defaultValue)96 bool ParseBoolean(const BackendOptions::Var& value, bool defaultValue)
97 {
98 if (value.IsBool())
99 {
100 return value.AsBool();
101 }
102 return defaultValue;
103 }
104
ParseFile(const BackendOptions::Var & value,std::string defaultValue)105 std::string ParseFile(const BackendOptions::Var& value, std::string defaultValue)
106 {
107 if (value.IsString())
108 {
109 return value.AsString();
110 }
111 return defaultValue;
112 }
113
ConfigureTuner(arm_compute::CLTuner & tuner,TuningLevel level)114 void ConfigureTuner(arm_compute::CLTuner &tuner, TuningLevel level)
115 {
116 tuner.set_tune_new_kernels(true); // Turn on tuning initially.
117
118 switch (level)
119 {
120 case TuningLevel::Rapid:
121 tuner.set_tuner_mode(arm_compute::CLTunerMode::RAPID);
122 break;
123 case TuningLevel::Normal:
124 tuner.set_tuner_mode(arm_compute::CLTunerMode::NORMAL);
125 break;
126 case TuningLevel::Exhaustive:
127 tuner.set_tuner_mode(arm_compute::CLTunerMode::EXHAUSTIVE);
128 break;
129 case TuningLevel::None:
130 default:
131 tuner.set_tune_new_kernels(false); // Turn off tuning. Set to "use" only mode.
132 break;
133 }
134 }
135
ClBackendContext(const IRuntime::CreationOptions & options)136 ClBackendContext::ClBackendContext(const IRuntime::CreationOptions& options)
137 : IBackendContext(options)
138 , m_TuningFile()
139 {
140 bool kernelProfiling = options.m_EnableGpuProfiling;
141
142 arm_compute::CLTuner* tuner = nullptr;
143 bool useLegacyTunerAPI = options.m_GpuAccTunedParameters.get() != nullptr;
144 if (useLegacyTunerAPI)
145 {
146 auto clTunerParams = PolymorphicDowncast<ClTunedParameters*>(
147 options.m_GpuAccTunedParameters.get());
148 tuner = &clTunerParams->m_Tuner;
149
150 if (tuner)
151 {
152 auto ConvertTuningLevel = [](IGpuAccTunedParameters::TuningLevel level,
153 armnn::IGpuAccTunedParameters::Mode mode)
154 {
155 if (mode == armnn::IGpuAccTunedParameters::Mode::UseTunedParameters)
156 {
157 return TuningLevel::None;
158 }
159
160 switch(level)
161 {
162 case IGpuAccTunedParameters::TuningLevel::Rapid:
163 return TuningLevel::Rapid;
164 case IGpuAccTunedParameters::TuningLevel::Normal:
165 return TuningLevel::Normal;
166 case IGpuAccTunedParameters::TuningLevel::Exhaustive:
167 return TuningLevel::Exhaustive;
168 default:
169 {
170 ARMNN_ASSERT_MSG(false, "Tuning level not recognised.");
171 return TuningLevel::None;
172 }
173 }
174 };
175
176 TuningLevel tuningLevel = ConvertTuningLevel(clTunerParams->m_TuningLevel, clTunerParams->m_Mode);
177 ConfigureTuner(*tuner, tuningLevel);
178 }
179 }
180 else //New backend options API
181 {
182 const TuningLevel defaultTuningLevel = TuningLevel::None;
183 auto tuningLevel = defaultTuningLevel;
184
185 ParseOptions(options.m_BackendOptions, "GpuAcc", [&](std::string name, const BackendOptions::Var& value)
186 {
187 if (name == "KernelProfilingEnabled")
188 {
189 kernelProfiling |= ParseBoolean(value, false);
190 } else if (name == "TuningFile")
191 {
192 m_TuningFile = ParseFile(value, "");
193 } else if (name == "TuningLevel")
194 {
195 tuningLevel = ParseTuningLevel(value, defaultTuningLevel);
196 }
197 });
198
199 // Create the tuner, in tuning mode initially.
200 m_Tuner = std::make_unique<arm_compute::CLTuner>(true);
201
202 ConfigureTuner(*(m_Tuner.get()), tuningLevel);
203
204 if (!m_TuningFile.empty() && tuningLevel == TuningLevel::None)
205 {
206 try
207 {
208 m_Tuner->load_from_file(m_TuningFile.c_str());
209 }
210 catch (const std::exception& e)
211 {
212 ARMNN_LOG(warning) << "Could not load GpuAcc tuner data file.";
213 }
214 }
215 tuner = m_Tuner.get();
216 }
217
218 m_ClContextControlWrapper = std::make_unique<ClContextControlWrapper>(
219 tuner,
220 kernelProfiling
221 );
222 }
223
BeforeLoadNetwork(NetworkId)224 bool ClBackendContext::BeforeLoadNetwork(NetworkId)
225 {
226 return true;
227 }
228
AfterLoadNetwork(NetworkId networkId)229 bool ClBackendContext::AfterLoadNetwork(NetworkId networkId)
230 {
231 {
232 std::lock_guard<std::mutex> lockGuard(m_Mutex);
233 m_NetworkIds.insert(networkId);
234 }
235 return true;
236 }
237
BeforeUnloadNetwork(NetworkId)238 bool ClBackendContext::BeforeUnloadNetwork(NetworkId)
239 {
240 return m_ClContextControlWrapper->Sync();
241 }
242
AfterUnloadNetwork(NetworkId networkId)243 bool ClBackendContext::AfterUnloadNetwork(NetworkId networkId)
244 {
245 bool clearCache = false;
246 {
247 std::lock_guard<std::mutex> lockGuard(m_Mutex);
248 m_NetworkIds.erase(networkId);
249 clearCache = m_NetworkIds.empty();
250 }
251
252 if (clearCache)
253 {
254 m_ClContextControlWrapper->ClearClCache();
255 }
256
257 return true;
258 }
259
~ClBackendContext()260 ClBackendContext::~ClBackendContext()
261 {
262 if (m_Tuner && !m_TuningFile.empty())
263 {
264 try
265 {
266 m_Tuner->save_to_file(m_TuningFile.c_str());
267 }
268 catch(const std::exception& e)
269 {
270 ARMNN_LOG(warning) << "Could not save GpuAcc tuner data to file " << m_TuningFile;
271 }
272 }
273 }
274
275 } // namespace armnn