1 /**
2 * Copyright 2021-2022 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
17 #include "plugin/device/cpu/kernel/pyfunc/py_func_cpu_kernel.h"
18
19 #include <memory>
20 #include <vector>
21 #include "Eigen/Core"
22 #include "abstract/utils.h"
23 #include "plugin/device/cpu/hal/device/cpu_common.h"
24 #include "include/common/utils/python_adapter.h"
25 #include "plugin/factory/ms_factory.h"
26 #include "utils/ms_utils_secure.h"
27
28 namespace mindspore {
29 namespace kernel {
30 namespace {
RawMemoryToScalar(const void * data,const TypeId & type)31 py::object RawMemoryToScalar(const void *data, const TypeId &type) {
32 switch (type) {
33 case kNumberTypeBool:
34 return py::bool_(*reinterpret_cast<const bool *>(data));
35 case kNumberTypeInt16:
36 return py::int_(*reinterpret_cast<const int16_t *>(data));
37 case kNumberTypeUInt16:
38 return py::int_(*reinterpret_cast<const uint16_t *>(data));
39 case kNumberTypeInt8:
40 return py::int_(*reinterpret_cast<const int8_t *>(data));
41 case kNumberTypeUInt8:
42 return py::int_(*reinterpret_cast<const uint8_t *>(data));
43 case kNumberTypeInt32:
44 return py::int_(*reinterpret_cast<const int32_t *>(data));
45 case kNumberTypeUInt32:
46 return py::int_(*reinterpret_cast<const uint32_t *>(data));
47 case kNumberTypeInt64:
48 return py::int_(*reinterpret_cast<const int64_t *>(data));
49 case kNumberTypeUInt64:
50 return py::int_(*reinterpret_cast<const uint64_t *>(data));
51 case kNumberTypeFloat16: {
52 const Eigen::half_impl::__half_raw data_half(*reinterpret_cast<const uint16_t *>(data));
53 return py::float_(Eigen::half_impl::half_to_float(data_half));
54 }
55 case kNumberTypeFloat32:
56 return py::float_(*reinterpret_cast<const float *>(data));
57 case kNumberTypeFloat64:
58 return py::float_(*reinterpret_cast<const double *>(data));
59 default:
60 MS_LOG(EXCEPTION) << "Type: " << type << " not supported.";
61 }
62 }
63
ScalarToRawMemory(const py::object & obj,const TypeId & type,KernelTensor * address)64 void ScalarToRawMemory(const py::object &obj, const TypeId &type, KernelTensor *address) {
65 MS_EXCEPTION_IF_NULL(address);
66 switch (type) {
67 case kNumberTypeBool: {
68 bool data = py::cast<bool>(obj);
69 CHECK_RET_WITH_EXCEPT(memcpy_s(address->device_ptr(), address->size(), &data, sizeof(bool)), EOK,
70 "memcpy failed.");
71 return;
72 }
73 // ref: pybind11-src/include/pybind11/pytypes.h
74 // py::int_ convert py::object to `long`, `unsigned long`, `long long`, `unsigned long long` with Python API
75 // according to typename T, and then convert to target data type with C style cast.
76 case kNumberTypeInt8: {
77 int8_t data = py::cast<int8_t>(obj);
78 CHECK_RET_WITH_EXCEPT(memcpy_s(address->device_ptr(), address->size(), &data, sizeof(int8_t)), EOK,
79 "memcpy failed.");
80 return;
81 }
82 case kNumberTypeUInt8: {
83 uint8_t data = py::cast<uint8_t>(obj);
84 CHECK_RET_WITH_EXCEPT(memcpy_s(address->device_ptr(), address->size(), &data, sizeof(uint8_t)), EOK,
85 "memcpy failed.");
86 return;
87 }
88 case kNumberTypeInt16: {
89 int16_t data = py::cast<int16_t>(obj);
90 CHECK_RET_WITH_EXCEPT(memcpy_s(address->device_ptr(), address->size(), &data, sizeof(int16_t)), EOK,
91 "memcpy failed.");
92 return;
93 }
94 case kNumberTypeUInt16: {
95 uint8_t data = py::cast<uint8_t>(obj);
96 CHECK_RET_WITH_EXCEPT(memcpy_s(address->device_ptr(), address->size(), &data, sizeof(uint8_t)), EOK,
97 "memcpy failed.");
98 return;
99 }
100 case kNumberTypeInt32: {
101 int32_t data = py::cast<int32_t>(obj);
102 CHECK_RET_WITH_EXCEPT(memcpy_s(address->device_ptr(), address->size(), &data, sizeof(int32_t)), EOK,
103 "memcpy failed.");
104 return;
105 }
106 case kNumberTypeUInt32: {
107 uint32_t data = py::cast<uint32_t>(obj);
108 CHECK_RET_WITH_EXCEPT(memcpy_s(address->device_ptr(), address->size(), &data, sizeof(uint32_t)), EOK,
109 "memcpy failed.");
110 return;
111 }
112 case kNumberTypeInt64: {
113 int64_t data = py::cast<int64_t>(obj);
114 CHECK_RET_WITH_EXCEPT(memcpy_s(address->device_ptr(), address->size(), &data, sizeof(int64_t)), EOK,
115 "memcpy failed.");
116 return;
117 }
118 case kNumberTypeUInt64: {
119 uint64_t data = py::cast<uint64_t>(obj);
120 CHECK_RET_WITH_EXCEPT(memcpy_s(address->device_ptr(), address->size(), &data, sizeof(uint64_t)), EOK,
121 "memcpy failed.");
122 return;
123 }
124 case kNumberTypeFloat16: {
125 float data = py::cast<float>(obj);
126 Eigen::half_impl::__half_raw data_half = Eigen::half_impl::float_to_half_rtne(data);
127 CHECK_RET_WITH_EXCEPT(memcpy_s(address->device_ptr(), address->size(), &data_half.x, sizeof(data_half.x)), EOK,
128 "memcpy failed.");
129 return;
130 }
131 case kNumberTypeFloat32: {
132 float data = py::cast<float>(obj);
133 CHECK_RET_WITH_EXCEPT(memcpy_s(address->device_ptr(), address->size(), &data, sizeof(float)), EOK,
134 "memcpy failed.");
135 return;
136 }
137 case kNumberTypeFloat64: {
138 double data = py::cast<double>(obj);
139 CHECK_RET_WITH_EXCEPT(memcpy_s(address->device_ptr(), address->size(), &data, sizeof(double)), EOK,
140 "memcpy failed.");
141 return;
142 }
143 default:
144 MS_LOG(EXCEPTION) << "Type: " << type << " not supported.";
145 }
146 }
147
ArrayToRawMemory(const py::array & array,KernelTensor * address)148 void ArrayToRawMemory(const py::array &array, KernelTensor *address) {
149 MS_EXCEPTION_IF_NULL(address);
150 if (static_cast<unsigned int>(array.flags()) &
151 static_cast<unsigned int>(pybind11::detail::npy_api::NPY_ARRAY_C_CONTIGUOUS_)) {
152 const py::buffer_info &buf_info = array.request();
153 CHECK_RET_WITH_EXCEPT(
154 common::huge_memcpy(reinterpret_cast<uint8_t *>(address->device_ptr()), address->size(),
155 reinterpret_cast<uint8_t *>(buf_info.ptr), LongToSize(buf_info.size * buf_info.itemsize)),
156 EOK, "memcpy failed.");
157 } else {
158 // Transform numpy array to row major buffer.
159 Py_buffer pybuf;
160 if (PyObject_GetBuffer(array.ptr(), &pybuf, PyBUF_ANY_CONTIGUOUS) != 0) {
161 MS_LOG(EXCEPTION) << "Failed to get buffer from the input!";
162 }
163
164 auto buffer = std::make_unique<char[]>(LongToSize(pybuf.len));
165 if (PyBuffer_ToContiguous(buffer.get(), &pybuf, pybuf.len, 'C')) {
166 PyBuffer_Release(&pybuf);
167 MS_LOG(EXCEPTION) << "Can't copy numpy.ndarray to a contiguous buffer.";
168 }
169 PyBuffer_Release(&pybuf);
170 CHECK_RET_WITH_EXCEPT(common::huge_memcpy(reinterpret_cast<uint8_t *>(address->device_ptr()), address->size(),
171 reinterpret_cast<uint8_t *>(buffer.get()), LongToSize(pybuf.len)),
172 EOK, "memcpy failed.");
173 }
174 }
175
ObjectToRawMemory(const py::object & object,const PythonOjectType & object_type,const TypeId & data_type,KernelTensor * address)176 void ObjectToRawMemory(const py::object &object, const PythonOjectType &object_type, const TypeId &data_type,
177 KernelTensor *address) {
178 switch (object_type) {
179 case PythonOjectType::kScalar:
180 return ScalarToRawMemory(object, data_type, address);
181 case PythonOjectType::kNumpyArray:
182 return ArrayToRawMemory(object.cast<py::array>(), address);
183 default:
184 MS_LOG(EXCEPTION) << "python object not supported. type: " << object_type;
185 }
186 }
187
RawMemoryToPyObjects(const std::vector<KernelTensor * > & inputs,const PyFuncArgumentInfo & input_infos,const std::vector<tensor::TensorPtr> & input_tensors)188 py::tuple RawMemoryToPyObjects(const std::vector<KernelTensor *> &inputs, const PyFuncArgumentInfo &input_infos,
189 const std::vector<tensor::TensorPtr> &input_tensors) {
190 py::tuple result(inputs.size());
191 for (size_t i = 0; i < inputs.size(); i++) {
192 switch (input_infos.object_types[i]) {
193 case PythonOjectType::kScalar:
194 MS_EXCEPTION_IF_NULL(inputs[i]);
195 result[i] = RawMemoryToScalar(inputs[i]->device_ptr(), input_infos.dtypes[i]);
196 break;
197 case PythonOjectType::kNumpyArray: {
198 const tensor::TensorPtr &tensor = input_tensors[i];
199 MS_EXCEPTION_IF_NULL(tensor);
200 CHECK_RET_WITH_EXCEPT(
201 common::huge_memcpy(reinterpret_cast<uint8_t *>(tensor->data_c()), tensor->Size(),
202 reinterpret_cast<uint8_t *>(inputs[i]->device_ptr()), inputs[i]->size()),
203 EOK, "memcpy failed.");
204 result[i] = python_adapter::PyAdapterCallback::TensorToNumpy(*tensor);
205 break;
206 }
207 default:
208 MS_LOG(EXCEPTION) << "Python args not support. Index: " << i << ", type" << input_infos.object_types[i];
209 }
210 }
211 return result;
212 }
213
PyObjectToRawMemorys(const py::object & object,const PyFuncArgumentInfo & output_infos,const std::vector<KernelTensor * > & outputs)214 void PyObjectToRawMemorys(const py::object &object, const PyFuncArgumentInfo &output_infos,
215 const std::vector<KernelTensor *> &outputs) {
216 // Single output.
217 if (!py::isinstance<py::tuple>(object)) {
218 if (outputs.size() != 1) {
219 MS_LOG(EXCEPTION) << "The output num is 1, with " << outputs.size() << " expect.";
220 }
221 return ObjectToRawMemory(object, output_infos.object_types[0], output_infos.dtypes[0], outputs[0]);
222 }
223
224 // Multiply outputs.
225 auto result_tuple = object.cast<py::tuple>();
226 if (result_tuple.size() != outputs.size()) {
227 MS_LOG(EXCEPTION) << "The output num is: " << result_tuple.size() << ", with " << outputs.size() << " expect.";
228 }
229 if (output_infos.object_types.size() != outputs.size() || output_infos.dtypes.size() != outputs.size()) {
230 MS_LOG(EXCEPTION) << "The output info size is: " << output_infos.object_types.size() << " and "
231 << output_infos.dtypes.size() << ", with " << outputs.size() << " expect.";
232 }
233 for (size_t i = 0; i < outputs.size(); i++) {
234 ObjectToRawMemory(result_tuple[i], output_infos.object_types[i], output_infos.dtypes[i], outputs[i]);
235 }
236 }
237 } // namespace
238
Resize(const std::vector<KernelTensor * > & inputs,const std::vector<KernelTensor * > & outputs)239 int PyFuncCpuKernelMod::Resize(const std::vector<KernelTensor *> &inputs, const std::vector<KernelTensor *> &outputs) {
240 if (auto ret = KernelMod::Resize(inputs, outputs); ret != KRET_OK) {
241 return ret;
242 }
243 func_id_ = GetValue<int64_t>(primitive_->GetAttr("fn_id"));
244 fake_output_ = GetValue<bool>(primitive_->GetAttr("fake_output"));
245 single_scalar_output_ = GetValue<bool>(primitive_->GetAttr("single_scalar_output"));
246 BuildFuncInfo(primitive_, inputs, outputs);
247 return KRET_OK;
248 }
249
Launch(const std::vector<KernelTensor * > & inputs,const std::vector<KernelTensor * > &,const std::vector<KernelTensor * > & outputs)250 bool PyFuncCpuKernelMod::Launch(const std::vector<KernelTensor *> &inputs, const std::vector<KernelTensor *> &,
251 const std::vector<KernelTensor *> &outputs) {
252 if (!init_) {
253 py_func_ = GetPythonFunc();
254 init_ = true;
255 }
256
257 return ExecuteKernel(inputs, outputs);
258 }
259
GetTypeInfo(const PrimitivePtr & primitive,const std::vector<KernelTensor * > & inputs,const std::string & arg_name,std::vector<TypeId> * types)260 void GetTypeInfo(const PrimitivePtr &primitive, const std::vector<KernelTensor *> &inputs, const std::string &arg_name,
261 std::vector<TypeId> *types) {
262 if (primitive->HasAttr(arg_name)) {
263 const auto &type_ptrs = GetValue<std::vector<TypePtr>>(primitive->GetAttr(arg_name));
264 (void)std::for_each(type_ptrs.begin(), type_ptrs.end(), [&types](auto p) {
265 MS_EXCEPTION_IF_NULL(p);
266 (void)types->emplace_back(p->type_id());
267 });
268 } else {
269 for (size_t i = 0; i < inputs.size(); ++i) {
270 types->emplace_back(inputs[i]->dtype_id());
271 }
272 }
273 }
274
BuildFuncInfo(const PrimitivePtr & primitive,const std::vector<KernelTensor * > & inputs,const std::vector<KernelTensor * > & outputs)275 void PyFuncCpuKernelMod::BuildFuncInfo(const PrimitivePtr &primitive, const std::vector<KernelTensor *> &inputs,
276 const std::vector<KernelTensor *> &outputs) {
277 std::vector<TypeId> in_types;
278 std::vector<TypeId> out_types;
279 std::vector<std::vector<int64_t>> in_shapes;
280 std::vector<std::vector<int64_t>> out_shapes;
281
282 GetTypeInfo(primitive, inputs, "in_types", &in_types);
283 GetTypeInfo(primitive, outputs, "out_types", &out_types);
284
285 if (primitive->HasAttr("in_shapes")) {
286 in_shapes = GetValue<std::vector<std::vector<int64_t>>>(primitive->GetAttr("in_shapes"));
287 } else {
288 for (size_t i = 0; i < inputs.size(); i++) {
289 const auto &in_shape = inputs[i]->GetShapeVector();
290 (void)in_shapes.emplace_back(in_shape);
291 }
292 }
293
294 if (primitive->HasAttr("out_shapes")) {
295 out_shapes = GetValue<std::vector<std::vector<int64_t>>>(primitive->GetAttr("out_shapes"));
296 } else {
297 for (size_t i = 0; i < outputs.size(); i++) {
298 const auto &out_shape = outputs[i]->GetShapeVector();
299 (void)out_shapes.emplace_back(out_shape);
300 }
301 }
302
303 if (in_shapes.size() != in_types.size()) {
304 MS_LOG(EXCEPTION) << "Input shapes'size is " << in_shapes.size() << ", while input types' size is "
305 << in_types.size();
306 }
307 if (out_shapes.size() != out_types.size()) {
308 MS_LOG(EXCEPTION) << "Output shapes'size is " << out_shapes.size() << ", while output types' size is "
309 << out_types.size();
310 }
311
312 input_infos_.dtypes = in_types;
313 input_infos_.shapes = in_shapes;
314 for (size_t i = 0; i < in_shapes.size(); i++) {
315 auto tensor = std::make_shared<tensor::Tensor>(in_types[i], in_shapes[i]);
316 input_tensors_.push_back(tensor);
317
318 const auto &object_type = in_shapes[i].empty() ? PythonOjectType::kScalar : PythonOjectType::kNumpyArray;
319 (void)input_infos_.object_types.emplace_back(object_type);
320 }
321
322 output_infos_.dtypes = out_types;
323 output_infos_.shapes = out_shapes;
324 if (single_scalar_output_) {
325 (void)output_infos_.object_types.emplace_back(PythonOjectType::kScalar);
326 } else {
327 for (size_t j = 0; j < out_shapes.size(); j++) {
328 const auto &object_type = out_shapes[j].empty() ? PythonOjectType::kScalar : PythonOjectType::kNumpyArray;
329 (void)output_infos_.object_types.emplace_back(object_type);
330 }
331 }
332 }
333
ExecuteKernel(const std::vector<KernelTensor * > & inputs,const std::vector<KernelTensor * > & outputs)334 bool PyFuncCpuKernelMod::ExecuteKernel(const std::vector<KernelTensor *> &inputs,
335 const std::vector<KernelTensor *> &outputs) {
336 if (Py_IsInitialized() != true) {
337 MS_LOG(ERROR) << "Py_IsInitialized failed.";
338 return false;
339 }
340
341 py::gil_scoped_acquire gil_acquire;
342 py::object result;
343 if (inputs.size()) {
344 py::tuple args = RawMemoryToPyObjects(inputs, input_infos_, input_tensors_);
345 result = py_func_(*args);
346 } else {
347 result = py_func_();
348 }
349
350 if (fake_output_) {
351 if (result.is_none()) {
352 return true;
353 } else {
354 MS_LOG(ERROR) << "This CustomPyfunc must have no outputs, but got 1";
355 return false;
356 }
357 }
358
359 PyObjectToRawMemorys(result, output_infos_, outputs);
360
361 return true;
362 }
363
GetPythonFunc() const364 py::function PyFuncCpuKernelMod::GetPythonFunc() const {
365 py::gil_scoped_acquire gil_acquire;
366 static const std::string &module_name = "mindspore.ops.operations._pyfunc_registry";
367 static const std::string &entrance = "get_pyfunc";
368 py::module module = py::module::import(module_name.c_str());
369 py::object get_pyfunc_obj = module.attr(entrance.c_str());
370 if (get_pyfunc_obj.is_none()) {
371 MS_LOG(EXCEPTION) << "Cannot find a python function named " << entrance << "in module" << module_name;
372 }
373
374 py::function get_pyfunc = get_pyfunc_obj.cast<py::function>();
375 py::object py_func_obj = get_pyfunc(py::int_(func_id_));
376 if (py_func_obj.is_none()) {
377 MS_LOG(EXCEPTION) << "Cannot find python func with id: " << func_id_;
378 }
379
380 return py_func_obj.cast<py::function>();
381 }
382
383 MS_KERNEL_FACTORY_REG(NativeCpuKernelMod, PyFunc, PyFuncCpuKernelMod);
384 } // namespace kernel
385 } // namespace mindspore
386