• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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