1 /**
2 * Copyright 2023 Huawei Technologies Co., Ltd
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include "minddata/dataset/core/device_tensor_ascend910b.h"
17
18 #include <string>
19
20 #include "minddata/dataset/core/global_context.h"
21 #include "minddata/dataset/core/type_id.h"
22 #include "minddata/dataset/kernels/image/dvpp/acl_adapter.h"
23 #include "minddata/dataset/kernels/image/dvpp/utils/ErrorCode.h"
24 #include "minddata/dataset/kernels/image/image_utils.h"
25 #include "minddata/dataset/util/status.h"
26
27 namespace mindspore {
28 namespace dataset {
DeviceTensorAscend910B(const TensorShape & shape,const DataType & type,device::DeviceContext * device_context,const size_t & stream_id,bool is_hwc)29 DeviceTensorAscend910B::DeviceTensorAscend910B(const TensorShape &shape, const DataType &type,
30 device::DeviceContext *device_context, const size_t &stream_id,
31 bool is_hwc)
32 : device_context_(device_context),
33 stream_id_(stream_id),
34 device_address_(nullptr),
35 tensor_(nullptr),
36 workspace_(nullptr),
37 float_arrays_({}),
38 int_arrays_({}),
39 tensor_shape_(shape),
40 data_type_(type),
41 is_hwc_(is_hwc) {}
42
~DeviceTensorAscend910B()43 DeviceTensorAscend910B::~DeviceTensorAscend910B() {}
44
ValidShape(TensorShape * input_shape,bool is_hwc,std::vector<int> channels={1, 3})45 Status ValidShape(TensorShape *input_shape, bool is_hwc, std::vector<int> channels = {1, 3}) {
46 // change the shape from HWC to 1HWC
47 if (input_shape->Rank() == 1) { // no expand
48 MS_LOG(DEBUG) << "The input is not RGB which is no need convert to 1HWC/1CHW."; // used by Dvpp Decode
49 return Status::OK();
50 }
51
52 const auto kChannelIndexHWC = 2;
53 const auto kChannelIndexNHWC = 3;
54 const auto kDefaultImageChannel = 3;
55 if (is_hwc) {
56 std::string delimiter = ", ";
57 std::ostringstream oss;
58 if (!channels.empty()) {
59 oss << channels[0];
60 for (size_t i = 1; i < channels.size(); ++i) {
61 oss << delimiter << channels[i];
62 }
63 }
64 std::string result = oss.str();
65 // change the shape from HWC to 1HWC
66 if (input_shape->Rank() == kMinImageRank) { // expand HW to 1HW1
67 *input_shape = input_shape->AppendDim(1);
68 *input_shape = input_shape->PrependDim(1);
69 } else if (input_shape->Rank() == kDefaultImageRank) { // expand HWC to 1HWC
70 if (std::find(channels.begin(), channels.end(), input_shape->AsVector()[kChannelIndexHWC]) == channels.end()) {
71 RETURN_STATUS_UNEXPECTED("The channel of the input tensor of shape [H,W,C] is not " + result +
72 ", but got: " + std::to_string(input_shape->AsVector()[kChannelIndexHWC]));
73 }
74 *input_shape = input_shape->PrependDim(1);
75 } else if (input_shape->Rank() == kDefaultImageRank + 1) { // NHWC
76 if (std::find(channels.begin(), channels.end(), input_shape->AsVector()[kChannelIndexNHWC]) == channels.end()) {
77 RETURN_STATUS_UNEXPECTED("The channel of the input tensor of shape [N,H,W,C] is not " + result +
78 ", but got: " + std::to_string(input_shape->AsVector()[kChannelIndexNHWC]));
79 }
80 if (input_shape->AsVector()[0] != 1) {
81 RETURN_STATUS_UNEXPECTED("The input tensor NHWC should be 1HWC or HWC.");
82 }
83 } else {
84 RETURN_STATUS_UNEXPECTED("The input tensor is not of shape [H,W], [H,W,C] or [N,H,W,C].");
85 }
86 } else {
87 // change the shape from CHW to 1CHW
88 if (input_shape->Rank() == kMinImageRank) { // expand HW to 11HW
89 *input_shape = input_shape->PrependDim(1);
90 *input_shape = input_shape->PrependDim(1);
91 } else if (input_shape->Rank() == kDefaultImageRank) { // expand CHW to 1CHW
92 if (input_shape->AsVector()[0] != 1 && input_shape->AsVector()[0] != kDefaultImageChannel) {
93 RETURN_STATUS_UNEXPECTED("The channel of the input tensor of shape [C,H,W] is not 1 or 3, but got: " +
94 std::to_string(input_shape->AsVector()[0]));
95 }
96 *input_shape = input_shape->PrependDim(1);
97 } else if (input_shape->Rank() == kDefaultImageRank + 1) { // NCHW
98 if (input_shape->AsVector()[1] != 1 && input_shape->AsVector()[1] != kDefaultImageChannel) {
99 RETURN_STATUS_UNEXPECTED("The channel of the input tensor of shape [N,C,H,W] is not 1 or 3, but got: " +
100 std::to_string(input_shape->AsVector()[1]));
101 }
102 if (input_shape->AsVector()[0] != 1) {
103 RETURN_STATUS_UNEXPECTED("The input tensor NCHW should be 1CHW or CHW.");
104 }
105 } else {
106 RETURN_STATUS_UNEXPECTED("The input tensor is not of shape [H,W], [C,H,W] or [N,C,H,W].");
107 }
108 }
109 return Status::OK();
110 }
111
112 // create device_tensor by empty
CreateDeviceTensor(const TensorShape & shape,const DataType & type,device::DeviceContext * device_context,const size_t & stream_id,std::shared_ptr<DeviceTensorAscend910B> * out,bool is_hwc,std::vector<int> channels)113 Status DeviceTensorAscend910B::CreateDeviceTensor(const TensorShape &shape, const DataType &type,
114 device::DeviceContext *device_context, const size_t &stream_id,
115 std::shared_ptr<DeviceTensorAscend910B> *out, bool is_hwc,
116 std::vector<int> channels) {
117 RETURN_UNEXPECTED_IF_NULL(device_context);
118 RETURN_UNEXPECTED_IF_NULL(out);
119
120 TensorShape input_shape(shape);
121 RETURN_IF_NOT_OK(ValidShape(&input_shape, is_hwc, channels));
122
123 *out = std::make_shared<DeviceTensorAscend910B>(input_shape, type, device_context, stream_id, is_hwc);
124
125 // create new device address for data copy
126 void *device_address = device_context->device_res_manager_->AllocateMemory((*out)->GetShape().NumOfElements() *
127 (*out)->GetType().SizeInBytes());
128 if (device_address == nullptr) {
129 RETURN_STATUS_UNEXPECTED("Allocate dynamic workspace memory failed");
130 }
131
132 (*out)->SetDeviceAddress(device_address);
133
134 // create the stride
135 std::vector<int64_t> strides((*out)->GetShape().Rank(), 1);
136 for (int64_t i = (*out)->GetShape().Rank() - 2; i >= 0; i--) {
137 strides[i] = (*out)->GetShape()[i + 1] * strides[i + 1];
138 }
139
140 // create aclTensor, here we use void* hold it.
141 void *device_tensor = nullptr;
142 auto ret = AclAdapter::GetInstance().CreateAclTensor((*out)->GetShape().AsVector().data(), (*out)->GetShape().Rank(),
143 DETypeToMSType((*out)->GetType()), strides.data(), 0,
144 (*out)->GetShape().AsVector().data(), (*out)->GetShape().Rank(),
145 (*out)->GetDeviceAddress(), is_hwc, &device_tensor);
146 if (ret != APP_ERR_OK) {
147 std::string error = "Create acl tensor failed.";
148 RETURN_STATUS_UNEXPECTED(error);
149 }
150 CHECK_FAIL_RETURN_UNEXPECTED(device_tensor != nullptr, "Create device tensor failed.");
151
152 (*out)->SetDeviceTensor(device_tensor);
153
154 return Status::OK();
155 }
156
157 // create device_tensor by host tensor
CreateDeviceTensor(std::shared_ptr<Tensor> tensor,device::DeviceContext * device_context,const size_t & stream_id,std::shared_ptr<DeviceTensorAscend910B> * out,bool is_hwc,std::vector<int> channels)158 Status DeviceTensorAscend910B::CreateDeviceTensor(std::shared_ptr<Tensor> tensor, device::DeviceContext *device_context,
159 const size_t &stream_id, std::shared_ptr<DeviceTensorAscend910B> *out,
160 bool is_hwc, std::vector<int> channels) {
161 RETURN_UNEXPECTED_IF_NULL(tensor);
162 RETURN_UNEXPECTED_IF_NULL(device_context);
163 RETURN_UNEXPECTED_IF_NULL(out);
164
165 RETURN_IF_NOT_OK(DeviceTensorAscend910B::CreateDeviceTensor(tensor->shape(), tensor->type(), device_context,
166 stream_id, out, is_hwc, channels));
167
168 CHECK_FAIL_RETURN_UNEXPECTED(
169 tensor->SizeInBytes() == (*out)->GetShape().NumOfElements() * (*out)->GetType().SizeInBytes(),
170 "The device SizeInBytes is not equal the input tensor.");
171
172 // copy the host data to device
173 (void)(*out)->GetDeviceContext()->device_res_manager_->SwapIn(
174 reinterpret_cast<void *>(tensor->GetMutableBuffer()), (*out)->GetDeviceAddress(), tensor->SizeInBytes(), nullptr);
175
176 return Status::OK();
177 }
178
ToHostTensor(std::shared_ptr<Tensor> * host_tensor)179 Status DeviceTensorAscend910B::ToHostTensor(std::shared_ptr<Tensor> *host_tensor) {
180 CHECK_FAIL_RETURN_UNEXPECTED(host_tensor != nullptr, "The host tensor is nullptr pointer.");
181 CHECK_FAIL_RETURN_UNEXPECTED(device_address_ != nullptr, "The device tensor is nullptr pointer.");
182
183 if (!device_context_->device_res_manager_->SyncStream(stream_id_)) {
184 std::string err_msg = "SyncStream stream id: " + std::to_string(stream_id_) + " failed.";
185 RETURN_STATUS_UNEXPECTED(err_msg);
186 }
187
188 RETURN_IF_NOT_OK(Tensor::CreateEmpty(GetShape(), GetType(), host_tensor));
189
190 // copy the device data to host
191 (void)device_context_->device_res_manager_->SwapOut(device_address_,
192 reinterpret_cast<void *>((*host_tensor)->GetMutableBuffer()),
193 (*host_tensor)->SizeInBytes(), nullptr);
194
195 // release the device memory
196 (void)device_context_->device_res_manager_->FreeMemory(device_address_);
197
198 (*host_tensor)->Squeeze();
199
200 return Status::OK();
201 }
202
AddWorkSpace(void * workspace)203 bool DeviceTensorAscend910B::AddWorkSpace(void *workspace) {
204 if (workspace == nullptr) {
205 MS_LOG(ERROR) << "The input workspace is nullptr.";
206 return false;
207 }
208 if (workspace_ != nullptr) {
209 MS_LOG(ERROR) << "The workspace_ is not nullptr, please don't re-add workspace to the current DeviceTensor.";
210 return false;
211 }
212 workspace_ = workspace;
213 return true;
214 }
215
AddMaintenFloatArrayMemory(void * float_array)216 bool DeviceTensorAscend910B::AddMaintenFloatArrayMemory(void *float_array) {
217 if (float_array == nullptr) {
218 MS_LOG(ERROR) << "The input float_array is nullptr.";
219 return false;
220 }
221 float_arrays_.push_back(float_array);
222 return true;
223 }
224
AddMaintenIntArrayMemory(void * int_array)225 bool DeviceTensorAscend910B::AddMaintenIntArrayMemory(void *int_array) {
226 if (int_array == nullptr) {
227 MS_LOG(ERROR) << "The input int_array is nullptr.";
228 return false;
229 }
230 int_arrays_.push_back(int_array);
231 return true;
232 }
233
ReleaseDeviceMemory()234 bool DeviceTensorAscend910B::ReleaseDeviceMemory() {
235 // release the device memory of the data
236 if (device_address_ != nullptr) {
237 device_context_->device_res_manager_->FreeMemory(device_address_);
238 device_address_ = nullptr;
239 }
240
241 // release the acl_tensor
242 if (tensor_ != nullptr) {
243 auto ret = AclAdapter::GetInstance().DestroyTensor(tensor_);
244 if (ret != APP_ERR_OK) {
245 MS_LOG(ERROR) << "Destroy acl tensor failed.";
246 return false;
247 }
248 tensor_ = nullptr;
249 }
250
251 // release the device memory of the workspace
252 if (workspace_ != nullptr) {
253 device_context_->device_res_manager_->FreeMemory(workspace_);
254 workspace_ = nullptr;
255 }
256
257 // release the float arrays
258 for (auto &float_array : float_arrays_) {
259 if (float_array != nullptr) {
260 auto ret = AclAdapter::GetInstance().DestroyFloatArray(float_array);
261 if (ret != APP_ERR_OK) {
262 MS_LOG(ERROR) << "Destroy float array failed.";
263 return false;
264 }
265 }
266 float_array = nullptr;
267 }
268
269 // release the int arrays
270 for (auto &int_array : int_arrays_) {
271 if (int_array != nullptr) {
272 auto ret = AclAdapter::GetInstance().DestroyIntArray(int_array);
273 if (ret != APP_ERR_OK) {
274 MS_LOG(ERROR) << "Destroy int array failed.";
275 return false;
276 }
277 }
278 int_array = nullptr;
279 }
280
281 return true;
282 }
283 } // namespace dataset
284 } // namespace mindspore
285