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