Release 18.03
[platform/upstream/armnn.git] / src / armnn / test / RuntimeTests.cpp
1 //
2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // See LICENSE file in the project root for full license information.
4 //
5 #include <boost/test/unit_test.hpp>
6
7 #include "armnn/TypesUtils.hpp"
8
9 #include "armnn/IRuntime.hpp"
10 #include "armnn/INetwork.hpp"
11 #include "armnn/Descriptors.hpp"
12 #include "Runtime.hpp"
13
14 #ifdef WITH_VALGRIND
15 #include "valgrind/memcheck.h"
16 #endif
17
18 #include <boost/core/ignore_unused.hpp>
19
20 namespace armnn
21 {
22
23 void RuntimeLoadedNetworksReserve(armnn::Runtime* runtime)
24 {
25     runtime->m_LoadedNetworks.reserve(1);
26 }
27
28 }
29
30 BOOST_AUTO_TEST_SUITE(Runtime)
31
32 BOOST_AUTO_TEST_CASE(RuntimeUnloadNetwork)
33 {
34     // build 2 mock-networks and load them into the runtime
35     armnn::IRuntimePtr runtime(armnn::IRuntime::Create(armnn::Compute::CpuRef));
36
37     // mock network 1
38     armnn::NetworkId networkIdentifier1 = 1;
39     armnn::INetworkPtr mockNetwork1(armnn::INetwork::Create());
40     mockNetwork1->AddInputLayer(0, "test layer");
41     runtime->LoadNetwork(networkIdentifier1, Optimize(*mockNetwork1, runtime->GetDeviceSpec()));
42
43     // mock network 2
44     armnn::NetworkId networkIdentifier2 = 2;
45     armnn::INetworkPtr mockNetwork2(armnn::INetwork::Create());
46     mockNetwork2->AddInputLayer(0, "test layer");
47     runtime->LoadNetwork(networkIdentifier2, Optimize(*mockNetwork2, runtime->GetDeviceSpec()));
48
49     // unload one by its networkID
50     BOOST_TEST(runtime->UnloadNetwork(networkIdentifier1) == armnn::Status::Success);
51
52     BOOST_TEST(runtime->UnloadNetwork(networkIdentifier1) == armnn::Status::Failure);
53 }
54
55 #if defined(ARMCOMPUTECL_ENABLED) && defined(WITH_VALGRIND)
56 BOOST_AUTO_TEST_CASE(RuntimeMemoryUsage)
57 {
58     // From documentation:
59
60     // This means that no pointer to the block can be found. The block is classified as "lost",
61     // because the programmer could not possibly have freed it at program exit, since no pointer to it exists.
62     unsigned long leakedBefore = 0;
63     unsigned long leakedAfter = 0;
64
65     // A start-pointer or chain of start-pointers to the block is found. Since the block is still pointed at,
66     // the programmer could, at least in principle, have freed it before program exit.
67     // We want to test this in case memory is not freed as early as it could have been
68     unsigned long reachableBefore = 0;
69     unsigned long reachableAfter = 0;
70
71     // needed as out params but we don't test them
72     unsigned long dubious = 0;
73     unsigned long suppressed = 0;
74
75     // ensure that runtime is large enough before checking for memory leaks
76     // otherwise when loading the network it will automatically reserve memory that won't be released until destruction
77     armnn::NetworkId networkIdentifier;
78     armnn::Runtime runtime(armnn::Compute::GpuAcc);
79     armnn::RuntimeLoadedNetworksReserve(&runtime);
80
81     // check for leaks before we load the network and record them so that we can see the delta after unloading
82     VALGRIND_DO_QUICK_LEAK_CHECK;
83     VALGRIND_COUNT_LEAKS(leakedBefore, dubious, reachableBefore, suppressed);
84
85     // build a mock-network and load it into the runtime
86     {
87         armnn::TensorInfo inputTensorInfo(armnn::TensorShape({ 7, 7 }), armnn::DataType::Float32);
88         armnn::TensorInfo outputTensorInfo(armnn::TensorShape({ 7, 7 }), armnn::DataType::Float32);
89
90         armnn::INetworkPtr mockNetwork(armnn::INetwork::Create());
91
92         armnn::IConnectableLayer* input = mockNetwork->AddInputLayer(0, "input");
93         armnn::IConnectableLayer* layer = mockNetwork->AddActivationLayer(armnn::ActivationDescriptor(), "test");
94         armnn::IConnectableLayer* output = mockNetwork->AddOutputLayer(0, "output");
95
96         input->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
97         layer->GetOutputSlot(0).Connect(output->GetInputSlot(0));
98
99         // set the tensors in the network
100         input->GetOutputSlot(0).SetTensorInfo(inputTensorInfo);
101         layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
102
103         // optimize the network
104         armnn::IOptimizedNetworkPtr optNet = Optimize(*mockNetwork, runtime.GetDeviceSpec());
105
106         runtime.LoadNetwork(networkIdentifier, std::move(optNet));
107     }
108
109     runtime.UnloadNetwork(networkIdentifier);
110
111     VALGRIND_DO_ADDED_LEAK_CHECK;
112     VALGRIND_COUNT_LEAKS(leakedAfter, dubious, reachableAfter, suppressed);
113
114     // if we're not running under Valgrind, these vars will have been initialised to 0, so this will always pass
115     BOOST_TEST(leakedBefore == leakedAfter);
116
117     // Add resonable threshold after and before running valgrind with the ACL clear cache function.
118     BOOST_TEST(static_cast<long>(reachableAfter) - static_cast<long>(reachableBefore) < 1024);
119
120     // these are needed because VALGRIND_COUNT_LEAKS is a macro that assigns to the parameters
121     // so they are assigned to, but still considered unused, causing a warning
122     boost::ignore_unused(dubious);
123     boost::ignore_unused(suppressed);
124 }
125 #endif
126
127 #ifdef WITH_VALGRIND
128 // run with the following command to get all the amazing output (in the devenv/build folder) :)
129 // valgrind --leak-check=full --show-leak-kinds=all --log-file=Valgrind_Memcheck_Leak_Report.txt armnn/test/UnitTests
130 BOOST_AUTO_TEST_CASE(RuntimeMemoryLeak)
131 {
132     // From documentation:
133
134     // This means that no pointer to the block can be found. The block is classified as "lost",
135     // because the programmer could not possibly have freed it at program exit, since no pointer to it exists.
136     unsigned long leakedBefore = 0;
137     unsigned long leakedAfter = 0;
138
139     // A start-pointer or chain of start-pointers to the block is found. Since the block is still pointed at,
140     // the programmer could, at least in principle, have freed it before program exit.
141     // We want to test this in case memory is not freed as early as it could have been
142     unsigned long reachableBefore = 0;
143     unsigned long reachableAfter = 0;
144
145     // needed as out params but we don't test them
146     unsigned long dubious = 0;
147     unsigned long suppressed = 0;
148
149     armnn::NetworkId networkIdentifier1 = 1;
150
151     // ensure that runtime is large enough before checking for memory leaks
152     // otherwise when loading the network it will automatically reserve memory that won't be released until destruction
153     armnn::Runtime runtime(armnn::Compute::CpuRef);
154     armnn::RuntimeLoadedNetworksReserve(&runtime);
155
156     // check for leaks before we load the network and record them so that we can see the delta after unloading
157     VALGRIND_DO_QUICK_LEAK_CHECK;
158     VALGRIND_COUNT_LEAKS(leakedBefore, dubious, reachableBefore, suppressed);
159
160     // build a mock-network and load it into the runtime
161     {
162         unsigned int inputShape[] = {1, 7, 1, 1};
163         armnn::TensorInfo inputTensorInfo(4, inputShape, armnn::DataType::Float32);
164
165         std::unique_ptr<armnn::Network> mockNetwork1 = std::make_unique<armnn::Network>();
166         mockNetwork1->AddInputLayer(0, "test layer");
167
168         armnn::DeviceSpec device;
169         device.DefaultComputeDevice = armnn::Compute::CpuRef;
170
171         runtime.LoadNetwork(networkIdentifier1, Optimize(*mockNetwork1, device));
172     }
173
174     runtime.UnloadNetwork(networkIdentifier1);
175
176     VALGRIND_DO_ADDED_LEAK_CHECK;
177     VALGRIND_COUNT_LEAKS(leakedAfter, dubious, reachableAfter, suppressed);
178
179     // if we're not running under Valgrind, these vars will have been initialised to 0, so this will always pass
180     BOOST_TEST(leakedBefore == leakedAfter);
181
182     #if defined(ARMCOMPUTECL_ENABLED)
183         // reachableBefore == reachableAfter should hold, but on OpenCL with Android we are still
184         // not entirely able to control the memory in the OpenCL driver. Testing is showing that
185         // after this test (which clears all OpenCL memory) we are clearing a little bit more than
186         // we expect, probably depending on the order in which other tests are run.
187         BOOST_TEST(reachableBefore - reachableAfter <= 24);
188     #else
189         BOOST_TEST(reachableBefore == reachableAfter);
190     #endif
191
192     BOOST_TEST(reachableBefore >= reachableAfter);
193
194     // these are needed because VALGRIND_COUNT_LEAKS is a macro that assigns to the parameters
195     // so they are assigned to, but still considered unused, causing a warning
196     boost::ignore_unused(dubious);
197     boost::ignore_unused(suppressed);
198 }
199 #endif
200
201 BOOST_AUTO_TEST_SUITE_END()