Publishing 2019 R1 content
[platform/upstream/dldt.git] / inference-engine / tests / unit / inference_engine_tests / blob_test.cpp
1 // Copyright (C) 2018-2019 Intel Corporation
2 // SPDX-License-Identifier: Apache-2.0
3 //
4
5 #include <ie_blob.h>
6 #include <gtest/gtest.h>
7 #include <random>
8 #include <chrono>
9
10 #include "mock_allocator.hpp"
11
12 #include <cpp/ie_cnn_net_reader.h>
13 #include <gmock/gmock-spec-builders.h>
14
15 #ifdef WIN32
16 #define UNUSED
17 #else
18 #define UNUSED  __attribute__((unused))
19 #endif
20
21 using namespace ::testing;
22 using namespace std;
23 using namespace InferenceEngine;
24
25 class BlobTests: public ::testing::Test {
26 protected:
27     virtual void TearDown() {
28     }
29
30     virtual void SetUp() {
31     }
32
33     shared_ptr<MockAllocator> createMockAllocator() {
34         return shared_ptr<MockAllocator>(new MockAllocator());
35     }
36
37 public:
38
39 };
40
41 struct ScopedTimer
42 {
43     chrono::high_resolution_clock::time_point t0;
44     function<void(int)> cb;
45
46     ScopedTimer(function<void(int)> callback)
47     : t0(chrono::high_resolution_clock::now())
48     , cb(callback)
49     {
50     }
51     ~ScopedTimer(void)
52     {
53         auto  t1 = chrono::high_resolution_clock::now();
54         auto milli = chrono::duration_cast<chrono::microseconds>(t1-t0).count();
55
56         cb((int)milli);
57     }
58 };
59
60 TEST_F(BlobTests, canCreateBlobUsingDefaultAllocator)
61 {
62     SizeVector v = {1,2,3};
63     auto allocator = createMockAllocator();
64
65     EXPECT_CALL(*allocator.get(), alloc(1 * 2 * 3 * sizeof(float))).WillRepeatedly(Return((void*)1));
66     EXPECT_CALL(*allocator.get(), free(_)).Times(1);
67
68     {
69         TBlob<float> blob(Precision::FP32, CHW, v, dynamic_pointer_cast<IAllocator>(allocator));
70         blob.allocate();
71     }
72 }
73
74 TEST_F(BlobTests, secondAllocateWontMemLeak) {
75     SizeVector v = {1,2,3};
76     auto allocator = createMockAllocator();
77
78     EXPECT_CALL(*allocator.get(), alloc(1 * 2 * 3 * sizeof(float))).Times(2).WillRepeatedly(Return((void*)1));
79     EXPECT_CALL(*allocator.get(), free(_)).Times(2).WillRepeatedly(Return(true));
80
81     {
82         TBlob<float> blob(Precision::FP32, CHW, v, dynamic_pointer_cast<IAllocator>(allocator));
83         blob.allocate();
84         blob.allocate();
85     }
86 }
87
88
89 TEST_F(BlobTests, doesNotUnlockIfLockFailed)
90 {
91     SizeVector v = {1,2,3};
92     auto allocator = createMockAllocator();
93
94     EXPECT_CALL(*allocator.get(), alloc(1 * 2 * 3 * sizeof(float))).WillRepeatedly(Return((void*)1));
95     EXPECT_CALL(*allocator.get(), lock((void*)1,LOCK_FOR_WRITE)).Times(1);
96     EXPECT_CALL(*allocator.get(), free(_)).Times(1);
97
98     TBlob<float> blob(Precision::FP32, CHW, v, dynamic_pointer_cast<IAllocator>(allocator));
99     blob.allocate();
100     {
101         float UNUSED *ptr = blob.data();
102     }
103 }
104
105 TEST_F(BlobTests, canAccessDataUsingAllocator)
106 {
107     SizeVector v = {1,2,3};
108     auto allocator = createMockAllocator();
109
110     float data[] = {5.f,6.f,7.f};
111
112     EXPECT_CALL(*allocator.get(), alloc(1 * 2 * 3 * sizeof(float))).WillRepeatedly(Return((void*)1));
113     EXPECT_CALL(*allocator.get(), lock((void*)1, LOCK_FOR_WRITE)).WillRepeatedly(Return(data));
114     EXPECT_CALL(*allocator.get(), unlock((void*)1)).Times(1);
115     EXPECT_CALL(*allocator.get(), free(_)).Times(1);
116
117     TBlob<float> blob(Precision::FP32, CHW, v, dynamic_pointer_cast<IAllocator>(allocator));
118     blob.allocate();
119     {
120         float *ptr = blob.data();
121         ASSERT_EQ(ptr[2] , 7);
122     }
123
124 }
125
126
127 TEST_F(BlobTests, canLockReadOnlyDataForRead)
128 {
129     SizeVector v = {1, 2, 3};
130     auto allocator = createMockAllocator();
131
132     float data[] = {5,6,7};
133
134     EXPECT_CALL(*allocator.get(), alloc(1 * 2 * 3 * sizeof(float))).WillRepeatedly(Return((void*)1));
135     EXPECT_CALL(*allocator.get(), lock(_,LOCK_FOR_READ)).WillRepeatedly(Return(data));
136     EXPECT_CALL(*allocator.get(), free(_)).Times(1);
137     EXPECT_CALL(*allocator.get(), unlock((void*)1)).Times(1);
138
139     TBlob<float> blob(Precision::FP32, CHW, v, dynamic_pointer_cast<IAllocator>(allocator));
140     blob.allocate();
141
142     const float *ptr = blob.readOnly();
143     ASSERT_EQ(ptr[2] , 7);
144 }
145
146 TEST_F(BlobTests, canAccessDataUsingBufferBaseMethod)
147 {
148     SizeVector v = {1, 2, 3};
149     auto allocator = createMockAllocator();
150
151     float data[] = {5,6,7};
152
153     EXPECT_CALL(*allocator.get(), alloc(1 * 2 * 3 * sizeof(float))).WillRepeatedly(Return((void*)1));
154     EXPECT_CALL(*allocator.get(), lock(_,LOCK_FOR_WRITE)).WillRepeatedly(Return(data));
155     EXPECT_CALL(*allocator.get(), unlock((void*)1)).Times(1);
156     EXPECT_CALL(*allocator.get(), free(_)).Times(1);
157
158     TBlob<float> blob(Precision::FP32, CHW, v, dynamic_pointer_cast<IAllocator>(allocator));
159     blob.allocate();
160     auto buffer = blob.buffer();
161     float *ptr = (float * )(void*)buffer;
162     ASSERT_EQ(ptr[2] , 7);
163 }
164
165 TEST_F(BlobTests, canMoveFromTBlobWithSameType)
166 {
167     SizeVector v = {1, 2, 3};
168     auto allocator = createMockAllocator();
169
170     uint8_t data[] = {5,6};
171
172     EXPECT_CALL(*allocator.get(), alloc(1 * 2 * 3 * sizeof(uint8_t))).WillRepeatedly(Return((void*)1));
173     EXPECT_CALL(*allocator.get(), lock(_,LOCK_FOR_WRITE)).WillRepeatedly(Return(data));
174     EXPECT_CALL(*allocator.get(), unlock((void*)1)).Times(1);
175     EXPECT_CALL(*allocator.get(), free(_)).Times(1);
176
177     TBlob<uint8_t > blob(Precision::U8, CHW, v, dynamic_pointer_cast<IAllocator>(allocator));
178     blob.allocate();
179
180     TBlob<uint8_t > newBlob(std::move(blob));
181
182     auto buffer = newBlob.buffer();
183     uint8_t *ptr = (uint8_t * )(void*)buffer;
184     ASSERT_EQ(ptr[0] , data[0]);
185 }
186
187 TEST_F(BlobTests, saveDimsAndSizeAfterMove)
188 {
189     SizeVector v = {1, 2, 3};
190     auto allocator = createMockAllocator();
191
192     TBlob<uint8_t > blob(Precision::U8, CHW, v, dynamic_pointer_cast<IAllocator>(allocator));
193
194     TBlob<uint8_t > newBlob(std::move(blob));
195
196     ASSERT_EQ(newBlob.size(), 1 * 2 * 3);
197     ASSERT_EQ(newBlob.dims()[0], 1);
198     ASSERT_EQ(newBlob.dims()[1], 2);
199     ASSERT_EQ(newBlob.dims()[2], 3);
200 }
201
202
203 TEST_F(BlobTests, canSetAfterFree)
204 {
205     SizeVector v = {1, 3};
206     TBlob<uint8_t> blob(Precision::U8, HW, v);
207     blob.allocate();
208     blob.data()[0] = 1;
209     blob.data()[1] = 2;
210     blob.data()[2] = 3;
211
212     blob.deallocate();
213     ASSERT_NO_THROW(blob.set({1,2,3}));
214 }
215
216 TEST_F(BlobTests, canSetAfterFreeNonAllocated)
217 {
218     SizeVector v = {1, 3};
219     TBlob<uint8_t> blob(Precision::U8, HW, v);
220     blob.deallocate();
221     ASSERT_NO_THROW(blob.set({1,2,3}));
222 }
223
224
225 TEST_F(BlobTests, canCopyBlob)
226 {
227     SizeVector v = {1, 3};
228     TBlob<uint8_t> blob(Precision::U8, HW,v);
229     blob.allocate();
230     blob.data()[0] = 1;
231     blob.data()[1] = 2;
232     blob.data()[2] = 3;
233
234     TBlob<uint8_t> blob2(blob);
235
236     ASSERT_EQ(blob2.dims().size(),  blob.dims().size());
237     ASSERT_EQ(blob2.dims()[0],  blob.dims()[0]);
238     ASSERT_EQ(blob2.dims()[1],  blob.dims()[1]);
239     ASSERT_EQ(blob2.size(),  blob.size());
240     ASSERT_EQ(blob2.data()[0],  blob.data()[0]);
241     ASSERT_EQ(blob2.data()[1],  blob.data()[1]);
242     ASSERT_EQ(blob2.data()[2],  blob.data()[2]);
243 }
244
245 TEST_F(BlobTests, canCompareToNullPtrWithoutDereferencing) {
246     SizeVector v = {1, 2, 3};
247     auto allocator = createMockAllocator();
248
249     TBlob<uint8_t> blob(Precision::U8, CHW, v, dynamic_pointer_cast<IAllocator>(allocator));
250
251     ASSERT_TRUE(blob.readOnly() == nullptr);
252     ASSERT_TRUE(blob.data() == nullptr);
253     ASSERT_TRUE(blob.buffer() == nullptr);
254
255     ASSERT_TRUE(nullptr == blob.readOnly());
256     ASSERT_TRUE(nullptr == blob.data());
257     ASSERT_TRUE(nullptr == blob.buffer());
258 }
259
260 TEST_F(BlobTests, canCreateBlob) {
261     InferenceEngine::SizeVector size = { 1, 1, 1 };
262     InferenceEngine::TBlob<float> blob(Precision::FP32, CHW, size);
263     ASSERT_NE(blob.size(), 0);
264     ASSERT_EQ(blob.buffer(), nullptr);
265 }
266
267 TEST_F(BlobTests, canAllocateBlob) {
268     InferenceEngine::SizeVector size = { 1, 1, 1 };
269     InferenceEngine::TBlob<float> blob(Precision::FP32, CHW, size);
270     blob.allocate();
271     float* buffer = static_cast<float*>(blob.data());
272     ASSERT_NE(buffer, nullptr);
273 }
274
275 TEST_F(BlobTests, canDeallocateBlob) {
276     InferenceEngine::SizeVector size = { 1, 1, 1 };
277     InferenceEngine::TBlob<float> blob(Precision::FP32, CHW, size);
278     blob.allocate();
279     blob.deallocate();
280     ASSERT_EQ(nullptr, blob.data().as<float*>());
281 }
282
283 TEST_F(BlobTests, canCreateBlobWithoutDims) {
284     InferenceEngine::TBlob<float> blob(Precision::FP32, NCHW);
285     ASSERT_EQ(blob.dims().size(), 0);
286 }
287
288 TEST_F(BlobTests, canSetToBlobWithoutDims) {
289     InferenceEngine::TBlob<float> blob(Precision::FP32, C);
290     std::vector<float> data = { 1.0f, 2.0f, 3.0f };
291     blob.set(data);
292     ASSERT_EQ(blob.byteSize(), data.size() * sizeof(float));
293 }
294
295 TEST_F(BlobTests, canReadDataFromConstBlob) {
296     InferenceEngine::TBlob<float> blob(Precision::FP32, CHW, { 1, 1, 1 });
297     blob.set({ 1.0f });
298     InferenceEngine::TBlob<float> const blob2 = blob;
299     const float* buf = blob2.readOnly();
300     ASSERT_NE(buf, nullptr);
301 }
302
303 TEST_F(BlobTests, canMakeSharedBlob) {
304     InferenceEngine::SizeVector size = { 1, 1, 1 };
305     InferenceEngine::TBlob<float>::Ptr blob1 = InferenceEngine::make_shared_blob<float>(Precision::FP32, NCHW);
306     InferenceEngine::TBlob<float>::Ptr blob2 = InferenceEngine::make_shared_blob<float>(Precision::FP32, CHW, size);
307     InferenceEngine::TBlob<float>::Ptr blob3
308         = InferenceEngine::make_shared_blob<float, InferenceEngine::SizeVector >(Precision::FP32, C, { 0 });
309     ASSERT_EQ(blob1->size(), 0);
310     ASSERT_EQ(blob2->size(), 1);
311     ASSERT_EQ(blob3->size(), 0);
312 }
313
314 TEST_F(BlobTests, cannotCreateBlobWithIncorrectPrecision) {
315     InferenceEngine::TensorDesc desc(InferenceEngine::Precision::FP16, {1, 3, 227, 227}, Layout::NCHW);
316     ASSERT_THROW(InferenceEngine::make_shared_blob<float>(desc), InferenceEngine::details::InferenceEngineException);
317 }
318
319 TEST_F(BlobTests, canUseBlobInMoveSemantics) {
320
321     TBlob<float> b(Precision::FP32, C);
322     b.set({1.0f, 2.0f, 3.0f});
323
324     std::vector<float> dump;
325
326     for (const auto & e: b) {
327         dump.push_back(e);
328     }
329
330     ASSERT_EQ(dump.size(), 3);
331
332     ASSERT_EQ(dump[0], 1.0f);
333     ASSERT_EQ(dump[1], 2.0f);
334     ASSERT_EQ(dump[2], 3.0f);
335
336 }
337
338 TEST_F(BlobTests, DISABLED_canUseLockedMemoryAsRvalueReference) {
339
340     std::vector<float> dump;
341     for (auto e: *make_shared_blob(Precision::FP32, C, std::vector<float>({1.0f, 2.0f, 3.0f}))) {
342         dump.push_back(e);
343     }
344
345     ASSERT_EQ(dump.size(), 3);
346
347     ASSERT_EQ(dump[0], 1.0f);
348     ASSERT_EQ(dump[1], 2.0f);
349     ASSERT_EQ(dump[2], 3.0f);
350 }
351
352 TEST_F(BlobTests, canCreateBlobOnExistedMemory) {
353
354     float input[] = {0.1f, 0.2f, 0.3f};
355     {
356         auto  b = make_shared_blob<float>(Precision::FP32, HW, {1, 2}, input);
357         auto i = b->begin();
358         ASSERT_NEAR(*i, 0.1, 0.00001);
359         i++;
360         ASSERT_NEAR(*i, 0.2, 0.00001);
361         i++;
362         ASSERT_EQ(i, b->end());
363
364         ASSERT_EQ(&*b->begin(), input);
365     }
366 }
367
368 TEST_F(BlobTests, preAllocatorWillnotWorkIfPtrNotAlocated) {
369    ASSERT_ANY_THROW(TBlob<float>(Precision::FP32, C, {1}, nullptr));
370 }
371
372 TEST_F(BlobTests, cannotIncreaseSizeOfPreallocated) {
373
374     float input[] = {0.1f, 0.2f, 0.3f};
375     auto  b = make_shared_blob(Precision::FP32, HW, {1, 2}, input);
376     b->Resize({1,3});
377     //since allocator isno't releasing, user have to be carefull that this still use old array
378     ASSERT_EQ(nullptr, b->buffer().as<float*>());
379
380     b->Resize({1,1});
381     ASSERT_NE(nullptr, b->buffer().as<float*>());
382
383     b->Resize({1,2});
384     ASSERT_NE(nullptr, b->buffer().as<float*>());
385 }
386
387
388 TEST_F(BlobTests, canAcceptpreallocatedSize) {
389
390     float input[] = {0.1f, 0.2f, 0.3f};
391     auto  b = make_shared_blob(Precision::FP32, HW, {1, 2}, input, 100);
392     b->Resize({1,101});
393     //since allocator isno't releasing, user have to be carefull that this still use old array
394     ASSERT_EQ(nullptr, b->buffer().as<float*>());
395
396     b->Resize({1,100});
397     ASSERT_NE(nullptr, b->buffer().as<float*>());
398 }
399
400 TEST_F(BlobTests, ifBlobCannotReleaseItWillReuseOldMemory) {
401
402     SizeVector v = {1,2,3};
403     auto allocator = createMockAllocator();
404
405     EXPECT_CALL(*allocator.get(), alloc(1 * 2 * 3 * sizeof(float))).WillOnce(Return((void*)1));
406     EXPECT_CALL(*allocator.get(), alloc(1 * 2 * 4 * sizeof(float))).WillOnce(Return((void*)1));
407     EXPECT_CALL(*allocator.get(), free(_)).WillRepeatedly(Return(false));
408
409     {
410         TBlob<float> blob(Precision::FP32, CHW, v, dynamic_pointer_cast<IAllocator>(allocator));
411         blob.allocate();
412         blob.Resize({1,2,4});
413     }
414 }
415
416 TEST_F(BlobTests, ifBlobCannotReleaseItWillReuseOldMemoryOnlyIfAllocated) {
417
418     SizeVector v = {1,2,3};
419     auto allocator = createMockAllocator();
420
421     EXPECT_CALL(*allocator.get(), free(_)).WillRepeatedly(Return(false));
422
423     {
424         TBlob<float> blob(Precision::FP32, CHW, v, dynamic_pointer_cast<IAllocator>(allocator));
425         blob.Resize({1,2,4});
426     }
427 }
428
429 TEST_F(BlobTests, canModifyDataInRangedFor) {
430
431     SizeVector v = {1,2,3};
432     TBlob<int> blob(Precision::I32, CHW, v);
433     blob.allocate();
434
435     for (auto & data : blob) {
436         data = 5;
437     }
438
439     for(int i=0;i<v.size();i++) {
440         ASSERT_EQ(5, blob.data()[i]) << "Mismatch at" << i;
441     }
442 }
443
444 TEST_F(BlobTests, makeRoiBlobNchw) {
445     // we create main blob with NCHW layout. We will crop ROI from this blob.
446     SizeVector dims = {1, 3, 6, 5};  // RGB picture of size (WxH) = 5x6
447     Blob::Ptr blob = make_shared_blob<uint8_t>(TensorDesc(Precision::U8, dims, NCHW));
448     blob->allocate();
449
450     // create ROI blob based on the already created blob
451     ROI roi = {0, 2, 1, 2, 4};  // cropped picture with: id = 0, (x,y) = (2,1), sizeX (W) = 2, sizeY (H) = 4
452     Blob::Ptr roiBlob = make_shared_blob(blob, roi);
453
454     // check that BlockingDesc is constructed properly for the ROI blob
455     SizeVector refDims = {1, 3, 4, 2};
456     SizeVector refOrder = {0, 1, 2, 3};
457     size_t refOffset = 7;
458     SizeVector refStrides = {90, 30, 5, 1};
459     ASSERT_EQ(roiBlob->getTensorDesc().getBlockingDesc().getBlockDims(), refDims);
460     ASSERT_EQ(roiBlob->getTensorDesc().getBlockingDesc().getOrder(), refOrder);
461     ASSERT_EQ(roiBlob->getTensorDesc().getBlockingDesc().getOffsetPadding(), refOffset);
462     ASSERT_EQ(roiBlob->getTensorDesc().getBlockingDesc().getStrides(), refStrides);
463 }
464
465 TEST_F(BlobTests, makeRoiBlobNhwc) {
466     // we create main blob with NHWC layout. We will crop ROI from this blob.
467     SizeVector dims = {1, 3, 4, 8};  // RGB picture of size (WxH) = 8x4
468     Blob::Ptr blob = make_shared_blob<uint8_t>(TensorDesc(Precision::U8, dims, NHWC));
469     blob->allocate();
470
471     // create ROI blob based on the already created blob
472     ROI roi = {0, 3, 2, 5, 2};  // cropped picture with: id = 0, (x,y) = (3,2), sizeX (W) = 5, sizeY (H) = 2
473     Blob::Ptr roiBlob = make_shared_blob(blob, roi);
474
475     // check that BlockingDesc is constructed properly for the ROI blob
476     SizeVector refDims = {1, 2, 5, 3};
477     SizeVector refOrder = {0, 2, 3, 1};
478     size_t refOffset = 57;
479     SizeVector refStrides = {96, 24, 3, 1};
480     ASSERT_EQ(roiBlob->getTensorDesc().getBlockingDesc().getBlockDims(), refDims);
481     ASSERT_EQ(roiBlob->getTensorDesc().getBlockingDesc().getOrder(), refOrder);
482     ASSERT_EQ(roiBlob->getTensorDesc().getBlockingDesc().getOffsetPadding(), refOffset);
483     ASSERT_EQ(roiBlob->getTensorDesc().getBlockingDesc().getStrides(), refStrides);
484 }
485
486 TEST_F(BlobTests, makeRoiBlobWrongSize) {
487     // we create main blob with NCHW layout. We will crop ROI from this blob.
488     SizeVector dims = {1, 3, 4, 4};  // RGB picture of size (WxH) = 4x4
489     Blob::Ptr blob = make_shared_blob<uint8_t>(TensorDesc(Precision::U8, dims, NCHW));
490     blob->allocate();
491
492     // try to create ROI blob with wrong size
493     ROI roi = {0, 1, 1, 4, 4};  // cropped picture with: id = 0, (x,y) = (1,1), sizeX (W) = 4, sizeY (H) = 4
494     ASSERT_THROW(make_shared_blob(blob, roi), InferenceEngine::details::InferenceEngineException);
495 }