2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // See LICENSE file in the project root for full license information.
5 #include <boost/test/unit_test.hpp>
7 #include "armnn/TypesUtils.hpp"
9 #include "armnn/IRuntime.hpp"
10 #include "armnn/INetwork.hpp"
11 #include "armnn/Descriptors.hpp"
12 #include "Runtime.hpp"
15 #include "valgrind/memcheck.h"
18 #include <boost/core/ignore_unused.hpp>
23 void RuntimeLoadedNetworksReserve(armnn::Runtime* runtime)
25 runtime->m_LoadedNetworks.reserve(1);
30 BOOST_AUTO_TEST_SUITE(Runtime)
32 BOOST_AUTO_TEST_CASE(RuntimeUnloadNetwork)
34 // build 2 mock-networks and load them into the runtime
35 armnn::IRuntimePtr runtime(armnn::IRuntime::Create(armnn::Compute::CpuRef));
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()));
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()));
49 // unload one by its networkID
50 BOOST_TEST(runtime->UnloadNetwork(networkIdentifier1) == armnn::Status::Success);
52 BOOST_TEST(runtime->UnloadNetwork(networkIdentifier1) == armnn::Status::Failure);
55 #if defined(ARMCOMPUTECL_ENABLED) && defined(WITH_VALGRIND)
56 BOOST_AUTO_TEST_CASE(RuntimeMemoryUsage)
58 // From documentation:
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;
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;
71 // needed as out params but we don't test them
72 unsigned long dubious = 0;
73 unsigned long suppressed = 0;
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);
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);
85 // build a mock-network and load it into the runtime
87 armnn::TensorInfo inputTensorInfo(armnn::TensorShape({ 7, 7 }), armnn::DataType::Float32);
88 armnn::TensorInfo outputTensorInfo(armnn::TensorShape({ 7, 7 }), armnn::DataType::Float32);
90 armnn::INetworkPtr mockNetwork(armnn::INetwork::Create());
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");
96 input->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
97 layer->GetOutputSlot(0).Connect(output->GetInputSlot(0));
99 // set the tensors in the network
100 input->GetOutputSlot(0).SetTensorInfo(inputTensorInfo);
101 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
103 // optimize the network
104 armnn::IOptimizedNetworkPtr optNet = Optimize(*mockNetwork, runtime.GetDeviceSpec());
106 runtime.LoadNetwork(networkIdentifier, std::move(optNet));
109 runtime.UnloadNetwork(networkIdentifier);
111 VALGRIND_DO_ADDED_LEAK_CHECK;
112 VALGRIND_COUNT_LEAKS(leakedAfter, dubious, reachableAfter, suppressed);
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);
117 // Add resonable threshold after and before running valgrind with the ACL clear cache function.
118 BOOST_TEST(reachableAfter - reachableBefore < 30000);
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);
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)
132 // From documentation:
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;
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;
145 // needed as out params but we don't test them
146 unsigned long dubious = 0;
147 unsigned long suppressed = 0;
149 armnn::NetworkId networkIdentifier1 = 1;
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);
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);
160 // build a mock-network and load it into the runtime
162 unsigned int inputShape[] = {1, 7, 1, 1};
163 armnn::TensorInfo inputTensorInfo(4, inputShape, armnn::DataType::Float32);
165 std::unique_ptr<armnn::Network> mockNetwork1 = std::make_unique<armnn::Network>();
166 mockNetwork1->AddInputLayer(0, "test layer");
168 armnn::DeviceSpec device;
169 device.DefaultComputeDevice = armnn::Compute::CpuRef;
171 runtime.LoadNetwork(networkIdentifier1, Optimize(*mockNetwork1, device));
174 runtime.UnloadNetwork(networkIdentifier1);
176 VALGRIND_DO_ADDED_LEAK_CHECK;
177 VALGRIND_COUNT_LEAKS(leakedAfter, dubious, reachableAfter, suppressed);
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 BOOST_TEST(reachableBefore == reachableAfter);
183 // these are needed because VALGRIND_COUNT_LEAKS is a macro that assigns to the parameters
184 // so they are assigned to, but still considered unused, causing a warning
185 boost::ignore_unused(dubious);
186 boost::ignore_unused(suppressed);
190 BOOST_AUTO_TEST_SUITE_END()