1 //
2 // Copyright © 2017, 2023 Arm Ltd and Contributors. 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 arm_compute::CLGEMMHeuristicsHandle* heuristicsHandle,
25 bool profilingEnabled)
26 : m_ClContextControl(tuner, heuristicsHandle, profilingEnabled)
27 {}
28
Syncarmnn::ClBackendContext::ClContextControlWrapper29 bool Sync()
30 {
31 if (arm_compute::CLScheduler::get().context()() != NULL)
32 {
33 // Waits for all queued CL requests to finish before unloading the network they may be using.
34 try
35 {
36 // Coverity fix: arm_compute::CLScheduler::sync() may throw an exception of type cl::Error.
37 arm_compute::CLScheduler::get().sync();
38 }
39 catch (const cl::Error&)
40 {
41 ARMNN_LOG(warning) << "Runtime::UnloadNetwork(): an error occurred while waiting for "
42 "the queued CL requests to finish";
43 return false;
44 }
45 }
46
47 return true;
48 }
49
ClearClCachearmnn::ClBackendContext::ClContextControlWrapper50 void ClearClCache()
51 {
52 if (arm_compute::CLScheduler::get().context()() != NULL)
53 {
54 // There are no loaded networks left, so clear the CL cache to free up memory
55 m_ClContextControl.ClearClCache();
56 }
57 }
58
59 ClContextControl m_ClContextControl;
60 };
61
ClBackendContext(const IRuntime::CreationOptions & options)62 ClBackendContext::ClBackendContext(const IRuntime::CreationOptions& options)
63 : IBackendContext(options)
64 , m_TuningFile()
65 {
66 bool kernelProfiling = options.m_EnableGpuProfiling;
67
68 arm_compute::CLTuner* tuner = nullptr;
69 arm_compute::CLGEMMHeuristicsHandle* mlgoTuner = nullptr;
70 bool useLegacyTunerAPI = options.m_GpuAccTunedParameters.get() != nullptr;
71 if (useLegacyTunerAPI)
72 {
73 auto clTunerParams = PolymorphicDowncast<ClTunedParameters*>(
74 options.m_GpuAccTunedParameters.get());
75 tuner = &clTunerParams->m_Tuner;
76
77 if (tuner)
78 {
79 auto ConvertTuningLevel = [](IGpuAccTunedParameters::TuningLevel level,
80 armnn::IGpuAccTunedParameters::Mode mode)
81 {
82 if (mode == armnn::IGpuAccTunedParameters::Mode::UseTunedParameters)
83 {
84 return TuningLevel::None;
85 }
86
87 switch(level)
88 {
89 case IGpuAccTunedParameters::TuningLevel::Rapid:
90 return TuningLevel::Rapid;
91 case IGpuAccTunedParameters::TuningLevel::Normal:
92 return TuningLevel::Normal;
93 case IGpuAccTunedParameters::TuningLevel::Exhaustive:
94 return TuningLevel::Exhaustive;
95 default:
96 {
97 ARMNN_ASSERT_MSG(false, "Tuning level not recognised.");
98 return TuningLevel::None;
99 }
100 }
101 };
102
103 TuningLevel tuningLevel = ConvertTuningLevel(clTunerParams->m_TuningLevel, clTunerParams->m_Mode);
104 ConfigureTuner(*tuner, tuningLevel);
105 }
106 }
107 else //New backend options API
108 {
109 const TuningLevel defaultTuningLevel = TuningLevel::None;
110 auto tuningLevel = defaultTuningLevel;
111
112 ParseOptions(options.m_BackendOptions, "GpuAcc", [&](std::string name, const BackendOptions::Var& value)
113 {
114 if (name == "KernelProfilingEnabled")
115 {
116 kernelProfiling |= ParseBooleanBackendOption(value, false);
117 } else if (name == "TuningFile")
118 {
119 m_TuningFile = ParseStringBackendOption(value, "");
120 } else if (name == "TuningLevel")
121 {
122 tuningLevel = ParseTuningLevel(value, defaultTuningLevel);
123 }
124 else if (name == "MLGOTuningFilePath")
125 {
126 m_MLGOTuningFile = ParseStringBackendOption(value, "");
127 }
128 });
129
130 // Create the tuner, in tuning mode initially.
131 m_Tuner = std::make_unique<arm_compute::CLTuner>(true);
132
133 ConfigureTuner(*(m_Tuner.get()), tuningLevel);
134
135 if (!m_TuningFile.empty())
136 {
137 try
138 {
139 ARMNN_LOG(info) << "Loading Gpu tuning data from file: " << m_TuningFile;
140 m_Tuner->load_from_file(m_TuningFile.c_str());
141 }
142 catch (const std::exception& e)
143 {
144 // Warn if not tuning, otherwise tuning will generate new params
145 if (tuningLevel == TuningLevel::None)
146 {
147 ARMNN_LOG(warning) << "Could not load GpuAcc tuner data file.";
148 }
149 }
150 }
151
152 if (!m_MLGOTuningFile.empty())
153 {
154 try
155 {
156 ARMNN_LOG(info) << "Loading Gpu MLGO tuning data from file: " << m_TuningFile;
157 if(m_MLGOTuner.reload_from_file(m_MLGOTuningFile.c_str()))
158 {
159 mlgoTuner = &m_MLGOTuner;
160 }
161 }
162 catch (const std::exception& e)
163 {
164 ARMNN_LOG(warning) << "Could not load GpuAcc MLGO tuner data file.";
165 }
166 }
167
168 tuner = m_Tuner.get();
169 }
170
171 m_ClContextControlWrapper = std::make_unique<ClContextControlWrapper>(
172 tuner,
173 mlgoTuner,
174 kernelProfiling
175 );
176 }
177
BeforeLoadNetwork(NetworkId)178 bool ClBackendContext::BeforeLoadNetwork(NetworkId)
179 {
180 return true;
181 }
182
AfterLoadNetwork(NetworkId networkId)183 bool ClBackendContext::AfterLoadNetwork(NetworkId networkId)
184 {
185 {
186 std::lock_guard<std::mutex> lockGuard(m_Mutex);
187 m_NetworkIds.insert(networkId);
188 }
189 return true;
190 }
191
BeforeUnloadNetwork(NetworkId)192 bool ClBackendContext::BeforeUnloadNetwork(NetworkId)
193 {
194 return m_ClContextControlWrapper->Sync();
195 }
196
AfterUnloadNetwork(NetworkId networkId)197 bool ClBackendContext::AfterUnloadNetwork(NetworkId networkId)
198 {
199 bool clearCache = false;
200 {
201 std::lock_guard<std::mutex> lockGuard(m_Mutex);
202 m_NetworkIds.erase(networkId);
203 clearCache = m_NetworkIds.empty();
204 }
205
206 if (clearCache)
207 {
208 m_ClContextControlWrapper->ClearClCache();
209 }
210
211 return true;
212 }
213
AfterEnqueueWorkload(NetworkId)214 bool ClBackendContext::AfterEnqueueWorkload(NetworkId)
215 {
216 return m_ClContextControlWrapper->Sync();
217 }
218
~ClBackendContext()219 ClBackendContext::~ClBackendContext()
220 {
221 if (m_Tuner && !m_TuningFile.empty())
222 {
223 try
224 {
225 m_Tuner->save_to_file(m_TuningFile.c_str());
226 }
227 catch(const std::exception& e)
228 {
229 ARMNN_LOG(warning) << "Could not save GpuAcc tuner data to file " << m_TuningFile;
230 }
231 }
232 }
233
234 } // namespace armnn