blob: e8b21c942da97464f941e9389bd4aeca2ab72465 [file] [log] [blame]
surmeh013537c2c2018-05-18 16:31:43 +01001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
David Beckecb56cd2018-09-05 12:52:57 +01003// SPDX-License-Identifier: MIT
surmeh013537c2c2018-05-18 16:31:43 +01004//
5
6#include "ClContextControl.hpp"
7
8#include "armnn/Exceptions.hpp"
9
10#ifdef ARMCOMPUTECL_ENABLED
11#include <arm_compute/core/CL/CLKernelLibrary.h>
12#include <arm_compute/runtime/CL/CLScheduler.h>
13#endif
14
15#include <boost/assert.hpp>
16#include <boost/format.hpp>
17#include <boost/log/trivial.hpp>
18#include <boost/polymorphic_cast.hpp>
telsoa01c577f2c2018-08-31 09:22:23 +010019#include <boost/core/ignore_unused.hpp>
surmeh013537c2c2018-05-18 16:31:43 +010020
21#include "LeakChecking.hpp"
22
23namespace cl
24{
25class Context;
26class CommandQueue;
27class Device;
28}
29
30namespace armnn
31{
32
telsoa01c577f2c2018-08-31 09:22:23 +010033ClContextControl::ClContextControl(IGpuAccTunedParameters* clTunedParameters,
34 bool profilingEnabled)
surmeh013537c2c2018-05-18 16:31:43 +010035 : m_clTunedParameters(boost::polymorphic_downcast<ClTunedParameters*>(clTunedParameters))
telsoa01c577f2c2018-08-31 09:22:23 +010036 , m_ProfilingEnabled(profilingEnabled)
surmeh013537c2c2018-05-18 16:31:43 +010037{
telsoa01c577f2c2018-08-31 09:22:23 +010038 // Ignore m_ProfilingEnabled if unused to avoid compiling problems when ArmCompute is disabled.
39 boost::ignore_unused(m_ProfilingEnabled);
40
surmeh013537c2c2018-05-18 16:31:43 +010041#ifdef ARMCOMPUTECL_ENABLED
42 try
43 {
44 std::vector<cl::Platform> platforms;
45 cl::Platform::get(&platforms);
46
telsoa01c577f2c2018-08-31 09:22:23 +010047 // Selects default platform for the first element.
surmeh013537c2c2018-05-18 16:31:43 +010048 cl::Platform::setDefault(platforms[0]);
49
50 std::vector<cl::Device> devices;
51 platforms[0].getDevices(CL_DEVICE_TYPE_GPU, &devices);
52
telsoa01c577f2c2018-08-31 09:22:23 +010053 // Selects default device for the first element.
surmeh013537c2c2018-05-18 16:31:43 +010054 cl::Device::setDefault(devices[0]);
55 }
56 catch (const cl::Error& clError)
57 {
58 throw ClRuntimeUnavailableException(boost::str(boost::format(
59 "Could not initialize the CL runtime. Error description: %1%. CL error code: %2%"
60 ) % clError.what() % clError.err()));
61 }
62
telsoa01c577f2c2018-08-31 09:22:23 +010063 // Removes the use of global CL context.
surmeh013537c2c2018-05-18 16:31:43 +010064 cl::Context::setDefault(cl::Context{});
65 BOOST_ASSERT(cl::Context::getDefault()() == NULL);
66
telsoa01c577f2c2018-08-31 09:22:23 +010067 // Removes the use of global CL command queue.
surmeh013537c2c2018-05-18 16:31:43 +010068 cl::CommandQueue::setDefault(cl::CommandQueue{});
69 BOOST_ASSERT(cl::CommandQueue::getDefault()() == NULL);
70
telsoa01c577f2c2018-08-31 09:22:23 +010071 // Always load the OpenCL runtime.
surmeh013537c2c2018-05-18 16:31:43 +010072 LoadOpenClRuntime();
73#endif
74}
75
76ClContextControl::~ClContextControl()
77{
78#ifdef ARMCOMPUTECL_ENABLED
telsoa01c577f2c2018-08-31 09:22:23 +010079 // Load the OpencCL runtime without the tuned parameters to free the memory for them.
surmeh013537c2c2018-05-18 16:31:43 +010080 try
81 {
82 UnloadOpenClRuntime();
83 }
84 catch (const cl::Error& clError)
85 {
telsoa01c577f2c2018-08-31 09:22:23 +010086 // This should not happen, it is ignored if it does.
surmeh013537c2c2018-05-18 16:31:43 +010087
88 // Coverity fix: BOOST_LOG_TRIVIAL (previously used here to report the error) may throw an
89 // exception of type std::length_error.
90 // Using stderr instead in this context as there is no point in nesting try-catch blocks here.
91 std::cerr << "A CL error occurred unloading the runtime tuner parameters: "
92 << clError.what() << ". CL error code is: " << clError.err() << std::endl;
93 }
94#endif
95}
96
97void ClContextControl::LoadOpenClRuntime()
98{
99 DoLoadOpenClRuntime(true);
100}
101
102void ClContextControl::UnloadOpenClRuntime()
103{
104 DoLoadOpenClRuntime(false);
105}
106
107void ClContextControl::DoLoadOpenClRuntime(bool useTunedParameters)
108{
109#ifdef ARMCOMPUTECL_ENABLED
110 cl::Device device = cl::Device::getDefault();
111 cl::Context context;
112 cl::CommandQueue commandQueue;
113
114 if (arm_compute::CLScheduler::get().context()() != NULL)
115 {
telsoa01c577f2c2018-08-31 09:22:23 +0100116 // Wait for all queued CL requests to finish before reinitialising it.
surmeh013537c2c2018-05-18 16:31:43 +0100117 arm_compute::CLScheduler::get().sync();
118 }
119
120 try
121 {
122 arm_compute::CLKernelLibrary::get().clear_programs_cache();
telsoa01c577f2c2018-08-31 09:22:23 +0100123 // Initialise the scheduler with a dummy context to release the LLVM data (which only happens when there are no
surmeh013537c2c2018-05-18 16:31:43 +0100124 // context references); it is initialised again, with a proper context, later.
125 arm_compute::CLScheduler::get().init(context, commandQueue, device);
126 arm_compute::CLKernelLibrary::get().init(".", context, device);
127
128 {
129 //
telsoa01c577f2c2018-08-31 09:22:23 +0100130 // Here we replace the context with a new one in which
131 // the memory leak checks show it as an extra allocation but
132 // because of the scope of the leak checks, it doesn't count
surmeh013537c2c2018-05-18 16:31:43 +0100133 // the disposal of the original object. On the other hand it
134 // does count the creation of this context which it flags
135 // as a memory leak. By adding the following line we prevent
136 // this to happen.
137 //
138 ARMNN_DISABLE_LEAK_CHECKING_IN_SCOPE();
139 context = cl::Context(device);
140 }
141
telsoa01c577f2c2018-08-31 09:22:23 +0100142 // NOTE: In this specific case profiling has to be enabled on the command queue
143 // in order for the CLTuner to work.
144 bool profilingNeededForClTuner = useTunedParameters && m_clTunedParameters &&
145 m_clTunedParameters->m_Mode == IGpuAccTunedParameters::Mode::UpdateTunedParameters;
surmeh013537c2c2018-05-18 16:31:43 +0100146
telsoa01c577f2c2018-08-31 09:22:23 +0100147 if (m_ProfilingEnabled || profilingNeededForClTuner)
surmeh013537c2c2018-05-18 16:31:43 +0100148 {
telsoa01c577f2c2018-08-31 09:22:23 +0100149 // Create a new queue with profiling enabled.
surmeh013537c2c2018-05-18 16:31:43 +0100150 commandQueue = cl::CommandQueue(context, device, CL_QUEUE_PROFILING_ENABLE);
151 }
152 else
153 {
telsoa01c577f2c2018-08-31 09:22:23 +0100154 // Use default queue.
surmeh013537c2c2018-05-18 16:31:43 +0100155 commandQueue = cl::CommandQueue(context, device);
156 }
157 }
158 catch (const cl::Error& clError)
159 {
160 throw ClRuntimeUnavailableException(boost::str(boost::format(
161 "Could not initialize the CL runtime. Error description: %1%. CL error code: %2%"
162 ) % clError.what() % clError.err()));
163 }
164
165 // Note the first argument (path to cl source code) will be ignored as they should be embedded in the armcompute.
166 arm_compute::CLKernelLibrary::get().init(".", context, device);
167
168 arm_compute::ICLTuner* tuner = nullptr;
169 if (useTunedParameters && m_clTunedParameters)
170 {
171 tuner = &m_clTunedParameters->m_Tuner;
172 }
173 arm_compute::CLScheduler::get().init(context, commandQueue, device, tuner);
174#endif
175}
176
177void ClContextControl::ClearClCache()
178{
179 DoLoadOpenClRuntime(true);
180}
181
telsoa01c577f2c2018-08-31 09:22:23 +0100182armnn::IGpuAccTunedParameters* IGpuAccTunedParameters::CreateRaw(armnn::IGpuAccTunedParameters::Mode mode)
surmeh013537c2c2018-05-18 16:31:43 +0100183{
184 return new ClTunedParameters(mode);
185}
186
telsoa01c577f2c2018-08-31 09:22:23 +0100187armnn::IGpuAccTunedParametersPtr IGpuAccTunedParameters::Create(armnn::IGpuAccTunedParameters::Mode mode)
surmeh013537c2c2018-05-18 16:31:43 +0100188{
telsoa01c577f2c2018-08-31 09:22:23 +0100189 return IGpuAccTunedParametersPtr(CreateRaw(mode), &IGpuAccTunedParameters::Destroy);
surmeh013537c2c2018-05-18 16:31:43 +0100190}
191
telsoa01c577f2c2018-08-31 09:22:23 +0100192void IGpuAccTunedParameters::Destroy(IGpuAccTunedParameters* params)
surmeh013537c2c2018-05-18 16:31:43 +0100193{
194 delete params;
195}
196
telsoa01c577f2c2018-08-31 09:22:23 +0100197ClTunedParameters::ClTunedParameters(armnn::IGpuAccTunedParameters::Mode mode)
surmeh013537c2c2018-05-18 16:31:43 +0100198 : m_Mode(mode)
199#ifdef ARMCOMPUTECL_ENABLED
200 , m_Tuner(mode == ClTunedParameters::Mode::UpdateTunedParameters)
201#endif
202{
203}
204
205void ClTunedParameters::Load(const char* filename)
206{
207#ifdef ARMCOMPUTECL_ENABLED
208 try
209 {
210 m_Tuner.load_from_file(filename);
211 }
212 catch (const std::exception& e)
213 {
214 throw armnn::Exception(std::string("Failed to load tuned parameters file '") + filename + "': " +
215 e.what());
216 }
217#endif
218}
219
220void ClTunedParameters::Save(const char* filename) const
221{
222#ifdef ARMCOMPUTECL_ENABLED
223 try
224 {
225 m_Tuner.save_to_file(filename);
226 }
227 catch (const std::exception& e)
228 {
229 throw armnn::Exception(std::string("Failed to save tuned parameters file to '") + filename + "': " +
230 e.what());
231 }
232#endif
233}
234
235} // namespace armnn