• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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