blob: a286b28307343119bb0dab2219bff97f6308a0bd [file] [log] [blame]
telsoa01c577f2c2018-08-31 09:22:23 +01001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
David Beckecb56cd2018-09-05 12:52:57 +01003// SPDX-License-Identifier: MIT
telsoa01c577f2c2018-08-31 09:22:23 +01004//
telsoa01c577f2c2018-08-31 09:22:23 +01005
Aron Virginas-Tarc9cc8042018-11-01 16:15:57 +00006#include <Profiling.hpp>
7
David Beckac42efd2018-09-26 17:41:13 +01008#include <armnn/Descriptors.hpp>
9#include <armnn/IRuntime.hpp>
10#include <armnn/INetwork.hpp>
telsoa01c577f2c2018-08-31 09:22:23 +010011
Aron Virginas-Tar70104002018-10-24 15:33:28 +010012#include <boost/test/unit_test.hpp>
13#include <boost/algorithm/string.hpp>
14#include <boost/lexical_cast.hpp>
telsoa01c577f2c2018-08-31 09:22:23 +010015
Aron Virginas-Tar70104002018-10-24 15:33:28 +010016#include <sstream>
17#include <stack>
18#include <string>
19#include <vector>
20
21inline bool AreMatchingPair(const char opening, const char closing)
telsoa01c577f2c2018-08-31 09:22:23 +010022{
23 return (opening == '{' && closing == '}') || (opening == '[' && closing == ']');
24}
25
Aron Virginas-Tar70104002018-10-24 15:33:28 +010026inline bool AreParenthesesMatching(const std::string& exp)
telsoa01c577f2c2018-08-31 09:22:23 +010027{
28 std::stack<char> expStack;
29 for (size_t i = 0; i < exp.length(); ++i)
30 {
31 if (exp[i] == '{' || exp[i] == '[')
32 {
33 expStack.push(exp[i]);
34 }
35 else if (exp[i] == '}' || exp[i] == ']')
36 {
37 if (expStack.empty() || !AreMatchingPair(expStack.top(), exp[i]))
38 {
39 return false;
40 }
41 else
42 {
43 expStack.pop();
44 }
45 }
46 }
47 return expStack.empty();
48}
49
Aron Virginas-Tar70104002018-10-24 15:33:28 +010050inline std::vector<double> ExtractMeasurements(const std::string& exp)
telsoa01c577f2c2018-08-31 09:22:23 +010051{
52 std::vector<double> numbers;
53 bool inArray = false;
54 std::string numberString;
55 for (size_t i = 0; i < exp.size(); ++i)
56 {
57 if (exp[i] == '[')
58 {
59 inArray = true;
60 }
61 else if (exp[i] == ']' && inArray)
62 {
63 try
64 {
65 boost::trim_if(numberString, boost::is_any_of("\t,\n"));
66 numbers.push_back(std::stod(numberString));
67 }
68 catch (std::invalid_argument const& e)
69 {
70 BOOST_FAIL("Could not convert measurements to double: " + numberString);
71 }
72
73 numberString.clear();
74 inArray = false;
75 }
76 else if (exp[i] == ',' && inArray)
77 {
78 try
79 {
80 boost::trim_if(numberString, boost::is_any_of("\t,\n"));
81 numbers.push_back(std::stod(numberString));
82 }
83 catch (std::invalid_argument const& e)
84 {
85 BOOST_FAIL("Could not convert measurements to double: " + numberString);
86 }
87 numberString.clear();
88 }
89 else if (exp[i] != '[' && inArray && exp[i] != ',' && exp[i] != ' ')
90 {
91 numberString += exp[i];
92 }
93 }
94 return numbers;
95}
96
Aron Virginas-Tar70104002018-10-24 15:33:28 +010097inline std::vector<std::string> ExtractSections(const std::string& exp)
telsoa01c577f2c2018-08-31 09:22:23 +010098{
99 std::vector<std::string> sections;
100
101 std::stack<size_t> s;
102 for (size_t i = 0; i < exp.size(); i++)
103 {
104 if (exp.at(i) == '{')
105 {
106 s.push(i);
107 }
108 else if (exp.at(i) == '}')
109 {
110 size_t from = s.top();
111 s.pop();
112 sections.push_back(exp.substr(from, i - from + 1));
113 }
114 }
115
116 return sections;
117}
118
Aron Virginas-Tar70104002018-10-24 15:33:28 +0100119inline std::string SoftmaxProfilerTestSetupHelper(const std::vector<armnn::BackendId>& backends)
telsoa01c577f2c2018-08-31 09:22:23 +0100120{
121 using namespace armnn;
122
123 BOOST_CHECK(!backends.empty());
124
125 ProfilerManager& profilerManager = armnn::ProfilerManager::GetInstance();
126
127 // Create runtime in which test will run
128 IRuntime::CreationOptions options;
129 options.m_EnableGpuProfiling = backends.front() == armnn::Compute::GpuAcc;
130 IRuntimePtr runtime(IRuntime::Create(options));
131
132 // build up the structure of the network
133 INetworkPtr net(INetwork::Create());
134
135 IConnectableLayer* input = net->AddInputLayer(0, "input");
136 IConnectableLayer* softmax = net->AddSoftmaxLayer(SoftmaxDescriptor(), "softmax");
137 IConnectableLayer* output = net->AddOutputLayer(0, "output");
138
139 input->GetOutputSlot(0).Connect(softmax->GetInputSlot(0));
140 softmax->GetOutputSlot(0).Connect(output->GetInputSlot(0));
141
142 // set the tensors in the network
143 TensorInfo inputTensorInfo(TensorShape({1, 5}), DataType::QuantisedAsymm8);
144 inputTensorInfo.SetQuantizationOffset(100);
145 inputTensorInfo.SetQuantizationScale(10000.0f);
146 input->GetOutputSlot(0).SetTensorInfo(inputTensorInfo);
147
148 TensorInfo outputTensorInfo(TensorShape({1, 5}), DataType::QuantisedAsymm8);
149 outputTensorInfo.SetQuantizationOffset(0);
150 outputTensorInfo.SetQuantizationScale(1.0f / 256.0f);
151 softmax->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
152
153 // optimize the network
154 IOptimizedNetworkPtr optNet = Optimize(*net, backends, runtime->GetDeviceSpec());
155 if(!optNet)
156 {
157 BOOST_FAIL("Error occurred during Optimization, Optimize() returned nullptr.");
158 }
159 // load it into the runtime
160 NetworkId netId;
161 auto error = runtime->LoadNetwork(netId, std::move(optNet));
162 BOOST_TEST(error == Status::Success);
163
164 // create structures for input & output
165 std::vector<uint8_t> inputData
166 {
167 1, 10, 3, 200, 5
168 // one of inputs is sufficiently larger than the others to saturate softmax
169 };
170 std::vector<uint8_t> outputData(5);
171
172 armnn::InputTensors inputTensors
173 {
174 {0, armnn::ConstTensor(runtime->GetInputTensorInfo(netId, 0), inputData.data())}
175 };
176 armnn::OutputTensors outputTensors
177 {
178 {0, armnn::Tensor(runtime->GetOutputTensorInfo(netId, 0), outputData.data())}
179 };
180
181 runtime->GetProfiler(netId)->EnableProfiling(true);
182
183 // do the inferences
184 runtime->EnqueueWorkload(netId, inputTensors, outputTensors);
185 runtime->EnqueueWorkload(netId, inputTensors, outputTensors);
186 runtime->EnqueueWorkload(netId, inputTensors, outputTensors);
187
188 // retrieve the Profiler.Print() output
189 std::stringstream ss;
190 profilerManager.GetProfiler()->Print(ss);
191
192 return ss.str();
193}
194
Aron Virginas-Tar70104002018-10-24 15:33:28 +0100195inline void SoftmaxProfilerTestValidationHelper(std::string& result, const std::string& testData)
telsoa01c577f2c2018-08-31 09:22:23 +0100196{
197 // ensure all measurements are greater than zero
198 std::vector<double> measurementsVector = ExtractMeasurements(result);
199 BOOST_CHECK(!measurementsVector.empty());
200
201 // check sections contain raw and unit tags
202 // first ensure Parenthesis are balanced
203 if (AreParenthesesMatching(result))
204 {
205 // remove parent sections that will not have raw or unit tag
206 std::vector<std::string> sectionVector = ExtractSections(result);
207 for (size_t i = 0; i < sectionVector.size(); ++i)
208 {
209 if (boost::contains(sectionVector[i], "\"ArmNN\":")
210 || boost::contains(sectionVector[i], "\"inference_measurements\":"))
211 {
212 sectionVector.erase(sectionVector.begin() + static_cast<int>(i));
213 }
214 }
215 BOOST_CHECK(!sectionVector.empty());
216
217 BOOST_CHECK(std::all_of(sectionVector.begin(), sectionVector.end(),
218 [](std::string i) { return boost::contains(i, "\"raw\":"); }));
219
220 BOOST_CHECK(std::all_of(sectionVector.begin(), sectionVector.end(),
221 [](std::string i) { return boost::contains(i, "\"unit\":"); }));
222 }
223
224 // remove the time measurements as they vary from test to test
225 result.erase(std::remove_if (result.begin(),result.end(),
226 [](char c) { return c == '.'; }), result.end());
227 result.erase(std::remove_if (result.begin(), result.end(), &isdigit), result.end());
228 result.erase(std::remove_if (result.begin(),result.end(),
229 [](char c) { return c == '\t'; }), result.end());
230
231 BOOST_CHECK(boost::contains(result, "ArmNN"));
232 BOOST_CHECK(boost::contains(result, "inference_measurements"));
233 BOOST_CHECK(boost::contains(result, "layer_measurements"));
234 BOOST_CHECK_EQUAL(result, testData);
235
236 // ensure no spare parenthesis present in print output
237 BOOST_CHECK(AreParenthesesMatching(result));
238}
239
Aron Virginas-Tar70104002018-10-24 15:33:28 +0100240inline void SetupSoftmaxProfilerWithSpecifiedBackendsAndValidateJsonPrinterResult(
David Beckf0b48452018-10-19 15:20:56 +0100241 const std::vector<armnn::BackendId>& backends)
telsoa01c577f2c2018-08-31 09:22:23 +0100242{
243 // setup the test fixture and obtain JSON Printer result
244 std::string result = SoftmaxProfilerTestSetupHelper(backends);
245
246 std::string backend = "Ref";
247 std::string changeLine31 = "\n},\n\"CopyMemGeneric_Execute\": {";
Nina Drozd2d9dd362018-09-27 11:53:34 +0100248 std::string changeLine39 = "us\"";
telsoa01c577f2c2018-08-31 09:22:23 +0100249 std::string changeLine40;
250 std::string changeLine45;
251
David Beckf0b48452018-10-19 15:20:56 +0100252 if (backends[0] == armnn::Compute::GpuAcc) {
253 backend = "Cl";
254 changeLine31 = ",\n\"OpenClKernelTimer/: softmax_layer_max_shift_exp_sum_quantized_serial GWS[,,]\": {";
255 changeLine39 = R"(us"
telsoa01c577f2c2018-08-31 09:22:23 +0100256},
257"OpenClKernelTimer/: softmax_layer_norm_quantized GWS[,,]": {
258"raw": [
259,
260,
261
262],
263"unit": "us")";
264
David Beckf0b48452018-10-19 15:20:56 +0100265 changeLine40 = R"(
telsoa01c577f2c2018-08-31 09:22:23 +0100266},
267"CopyMemGeneric_Execute": {
268"raw": [
269,
270,
271
272],
Nina Drozd2d9dd362018-09-27 11:53:34 +0100273"unit": "us")";
David Beckf0b48452018-10-19 15:20:56 +0100274 changeLine45 = "}\n";
275 }
276 else if (backends[0] == armnn::Compute::CpuAcc)
277 {
278 backend = "Neon";
279 changeLine31 = ",\n\"NeonKernelTimer/: NEFillBorderKernel\": {";
280 changeLine39 = R"(us"
telsoa01c577f2c2018-08-31 09:22:23 +0100281},
282"NeonKernelTimer/: NELogitsDMaxKernel": {
283"raw": [
284,
285,
286
287],
Nina Drozd69851b52018-09-21 18:42:09 +0100288"unit": "us"
telsoa01c577f2c2018-08-31 09:22:23 +0100289},
290"NeonKernelTimer/: NELogitsDSoftmaxKernel": {
291"raw": [
292,
293,
294
295],
Nina Drozd69851b52018-09-21 18:42:09 +0100296"unit": "us")";
David Beckf0b48452018-10-19 15:20:56 +0100297 changeLine40 = R"(
telsoa01c577f2c2018-08-31 09:22:23 +0100298},
299"CopyMemGeneric_Execute": {
300"raw": [
301,
302,
303
304],
Nina Drozd2d9dd362018-09-27 11:53:34 +0100305"unit": "us")";
David Beckf0b48452018-10-19 15:20:56 +0100306 changeLine45 = "}\n";
telsoa01c577f2c2018-08-31 09:22:23 +0100307 }
David Beckf0b48452018-10-19 15:20:56 +0100308
telsoa01c577f2c2018-08-31 09:22:23 +0100309 std::string testData = R"({
310"ArmNN": {
311"inference_measurements": {
312"raw": [
313,
314,
315
316],
Nina Drozd2d9dd362018-09-27 11:53:34 +0100317"unit": "us",
telsoa01c577f2c2018-08-31 09:22:23 +0100318"layer_measurements": {
319"raw": [
320,
321,
322
323],
Nina Drozd2d9dd362018-09-27 11:53:34 +0100324"unit": "us",
telsoa01c577f2c2018-08-31 09:22:23 +0100325"CopyMemGeneric_Execute": {
326"raw": [
327,
328,
329
330],
Nina Drozd2d9dd362018-09-27 11:53:34 +0100331"unit": "us"
telsoa01c577f2c2018-08-31 09:22:23 +0100332},
333")" + backend + R"(SoftmaxUintWorkload_Execute": {
334"raw": [
335,
336,
337
338],
Nina Drozd2d9dd362018-09-27 11:53:34 +0100339"unit": "us")" + changeLine31 + R"(
telsoa01c577f2c2018-08-31 09:22:23 +0100340"raw": [
341,
342,
343
344],
345"unit": ")" + changeLine39 + R"(
346})" + changeLine40 + R"(
347}
348}
349}
350}
351)" + changeLine45 + R"()";
352
353 // validate the JSON Printer result
354 SoftmaxProfilerTestValidationHelper(result, testData);
355}