| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| /*! |
| * \file test_mkldnn.h |
| * \brief helper functions to test mkldnn. |
| * \author Alex Zai |
| */ |
| |
| #ifndef TEST_MKLDNN_H_ |
| #define TEST_MKLDNN_H_ |
| |
| #if MXNET_USE_MKLDNN == 1 |
| |
| #include <set> |
| #include <string> |
| #include <vector> |
| #include "../../../3rdparty/mkldnn/include/mkldnn_types.h" |
| #include "../../../3rdparty/googletest/googletest/include/gtest/gtest.h" |
| #include "../../../src/operator/nn/mkldnn/mkldnn_base-inl.h" |
| |
| using namespace mxnet; |
| |
| inline static mkldnn::memory::primitive_desc GetMemPD(const mxnet::TShape s, int dtype, |
| mkldnn::memory::format format) { |
| mkldnn::memory::dims dims(s.ndim()); |
| for (size_t i = 0; i < dims.size(); i++) |
| dims[i] = s[i]; |
| mkldnn::memory::desc desc{dims, get_mkldnn_type(dtype), format}; |
| return mkldnn::memory::primitive_desc(desc, CpuEngine::Get()->get_engine()); |
| } |
| |
| inline static mkldnn::memory::primitive_desc GetExpandedMemPD( |
| mkldnn::memory::primitive_desc pd, float scale, int dim = 0) { |
| CHECK(dim < pd.desc().data.ndims) << "dimension cannot be larger than total dimensions of input"; |
| mxnet::TShape s(pd.desc().data.ndims, -1); |
| for (size_t i = 0; i < pd.desc().data.ndims; i++) |
| s[i] = pd.desc().data.dims[i]; |
| s[dim] = static_cast<int>(s[dim] * scale); |
| return GetMemPD(s, mshadow::DataType<mshadow::default_real_t>::kFlag, |
| static_cast<mkldnn::memory::format>(pd.desc().data.format)); |
| } |
| |
| struct TestArrayShapes { |
| std::vector<mxnet::TShape> shapes; |
| std::vector<mkldnn::memory::primitive_desc> pds; |
| }; |
| |
| // Init arrays with the default layout. |
| inline static void InitDefaultArray(NDArray *arr, bool is_rand = false) { |
| const TBlob &blob = arr->data(); |
| mshadow::default_real_t *data = blob.dptr<mshadow::default_real_t>(); |
| int size = blob.Size(); |
| |
| for (int i = 0; i < size; i++) |
| if (is_rand) { |
| data[i] = (std::rand() % 100) - 50; |
| } else { |
| data[i] = i % 100 - 50; |
| } |
| } |
| |
| |
| // Init arrays with the specified layout. |
| inline static void InitMKLDNNArray(NDArray *arr, const mkldnn::memory::primitive_desc &pd, |
| bool is_rand = false) { |
| InitDefaultArray(arr, is_rand); |
| arr->MKLDNNDataReorderAsync(pd); |
| arr->WaitToRead(); |
| } |
| |
| inline static bool IsSameShape(mkldnn::memory::primitive_desc pd, mxnet::TShape shape) { |
| if (pd.desc().data.ndims != shape.ndim()) return false; |
| for (size_t i = 0; i < shape.ndim(); i++) |
| if (pd.desc().data.dims[i] != shape[i]) return false; |
| return true; |
| } |
| |
| // This function gets special MKLDNN formats without knowing the specific |
| // hardware configuration. Certainly, it potentially misses some format if |
| // it's specific for certain array shapes. It covers at least one special format |
| // for each of the formats: nchw, oihw, goihw. |
| // To test the logic of the code in NDArray, these formats should be enough. |
| inline static std::vector<mkldnn::memory::format> GetMKLDNNFormat(size_t num_dims, int dtype) { |
| if (num_dims == 4) { |
| mkldnn::memory::dims data_dims{1, 3, 224, 224}; |
| mkldnn::memory::desc data_md{data_dims, get_mkldnn_type(dtype), |
| mkldnn::memory::format::any}; |
| mkldnn::memory::dims weight_dims{96, 3, 11, 11}; |
| mkldnn::memory::desc weight_md{weight_dims, get_mkldnn_type(dtype), |
| mkldnn::memory::format::any}; |
| mkldnn::memory::dims output_dims{1, 96, 54, 54}; |
| mkldnn::memory::desc out_md{output_dims, get_mkldnn_type(dtype), |
| mkldnn::memory::format::any}; |
| mkldnn::memory::dims strides{4, 4}; |
| mkldnn::memory::dims padding{0, 0}; |
| |
| mkldnn::convolution_forward::desc desc(mkldnn::prop_kind::forward_training, |
| mkldnn::algorithm::convolution_direct, |
| data_md, weight_md, out_md, strides, |
| padding, padding, mkldnn::padding_kind::zero); |
| mkldnn::convolution_forward::primitive_desc pd(desc, CpuEngine::Get()->get_engine()); |
| while (pd.dst_primitive_desc().get_size() != GetMemDescSize(out_md) || |
| pd.src_primitive_desc().get_size() != GetMemDescSize(data_md) || |
| pd.weights_primitive_desc().get_size() != GetMemDescSize(weight_md)) { |
| CHECK(pd.next_impl()) << "No implementation"; |
| } |
| |
| std::vector<mkldnn::memory::format> ret(1); |
| ret[0] = static_cast<mkldnn::memory::format>(pd.dst_primitive_desc().desc().data.format); |
| printf("format: %d \n", ret[0]); |
| return ret; |
| } else if (num_dims == 5) { |
| mkldnn::memory::dims data_dims{1, 32, 112, 112}; |
| mkldnn::memory::desc data_md{data_dims, get_mkldnn_type(dtype), |
| mkldnn::memory::format::any}; |
| mkldnn::memory::dims weight_dims{32, 1, 1, 3, 3}; |
| mkldnn::memory::desc weight_md{weight_dims, get_mkldnn_type(dtype), |
| mkldnn::memory::format::any}; |
| mkldnn::memory::dims output_dims{1, 32, 112, 112}; |
| mkldnn::memory::desc out_md{output_dims, get_mkldnn_type(dtype), |
| mkldnn::memory::format::any}; |
| mkldnn::memory::dims strides{1, 1}; |
| mkldnn::memory::dims padding{1, 1}; |
| |
| mkldnn::convolution_forward::desc desc(mkldnn::prop_kind::forward_training, |
| mkldnn::algorithm::convolution_direct, |
| data_md, weight_md, out_md, strides, |
| padding, padding, mkldnn::padding_kind::zero); |
| mkldnn::convolution_forward::primitive_desc pd(desc, CpuEngine::Get()->get_engine()); |
| while (pd.dst_primitive_desc().get_size() != GetMemDescSize(out_md) || |
| pd.src_primitive_desc().get_size() != GetMemDescSize(data_md) || |
| pd.weights_primitive_desc().get_size() != GetMemDescSize(weight_md)) { |
| CHECK(pd.next_impl()) << "No implementation"; |
| } |
| |
| std::vector<mkldnn::memory::format> ret(1); |
| ret[0] = static_cast<mkldnn::memory::format>(pd.weights_primitive_desc().desc().data.format); |
| printf("format: %d\n", ret[0]); |
| return ret; |
| } else { |
| return std::vector<mkldnn::memory::format>(); |
| } |
| } |
| |
| inline static TestArrayShapes GetTestArrayShapes(bool spatial_data_format = false) { |
| int dtype = mshadow::DataType<mshadow::default_real_t>::kFlag; |
| mxnet::ShapeVector shapes; |
| std::vector<mkldnn::memory::primitive_desc> pds; |
| { |
| // 1D |
| mxnet::TShape s(1, -1); |
| s[0] = 279936; |
| shapes.push_back(s); |
| pds.push_back(GetMemPD(s, dtype, mkldnn::memory::format::x)); |
| s[0] = 34848; |
| shapes.push_back(s); |
| pds.push_back(GetMemPD(s, dtype, mkldnn::memory::format::x)); |
| } |
| { |
| // 2D |
| mxnet::TShape s(2, -1); |
| s[0] = 96; |
| s[1] = 2916; |
| shapes.push_back(s); |
| pds.push_back(GetMemPD(s, dtype, mkldnn::memory::format::nc)); |
| s[0] = 96; |
| s[1] = 363; |
| shapes.push_back(s); |
| pds.push_back(GetMemPD(s, dtype, mkldnn::memory::format::nc)); |
| } |
| { |
| // 4D |
| mxnet::TShape s1(4, -1); |
| s1[0] = 10; s1[1] = 96; s1[2] = 54; s1[3] = 54; |
| shapes.push_back(s1); |
| pds.push_back(GetMemPD(s1, dtype, mkldnn::memory::format::nchw)); |
| |
| mxnet::TShape s2(4, -1); |
| s2[0] = 96; s2[1] = 3; s2[2] = 11; s2[3] = 11; |
| shapes.push_back(s2); |
| pds.push_back(GetMemPD(s2, dtype, mkldnn::memory::format::oihw)); |
| |
| std::vector<mkldnn::memory::format> formats = GetMKLDNNFormat(4, dtype); |
| if (!spatial_data_format) { |
| pds.push_back(GetMemPD(s1, dtype, formats[0])); |
| } |
| } |
| { |
| // 5D |
| mxnet::TShape s(5, -1); |
| s[0] = 96; s[1] = 1; s[2] = 3; s[3] = 11; s[4] = 11; |
| shapes.push_back(s); |
| pds.push_back(GetMemPD(s, dtype, mkldnn::memory::format::goihw)); |
| |
| std::vector<mkldnn::memory::format> formats = GetMKLDNNFormat(5, dtype); |
| if (!spatial_data_format) { |
| pds.push_back(GetMemPD(s, dtype, formats[0])); |
| } |
| } |
| |
| TestArrayShapes ret; |
| ret.shapes = shapes; |
| ret.pds = pds; |
| return ret; |
| } |
| |
| struct NDArrayAttrs { |
| NDArray arr; |
| std::string desc; |
| NDArrayAttrs(NDArray arr, std::string desc) : arr(arr), desc(desc) {} |
| }; |
| |
| struct OpAttrs { |
| nnvm::NodeAttrs attrs; |
| std::vector<DispatchMode> dispatches; |
| std::set<OpReqType> requests; |
| std::unordered_set<int> accept_dims; |
| int num_inputs; |
| int num_outputs; |
| int input_types; |
| int output_types; |
| }; |
| |
| enum ArrayTypes { |
| Normal = 1, |
| MKLDNN = 2, |
| MKLDNNDiffShape = 4, |
| MKLDNNDiffDim = 8, |
| NormalReshaped = 16, |
| MKLDNNReshaped = 32, |
| MKLDNNReshapedDiffShape = 64, |
| MKLDNNReshapedDiffDim = 128, |
| NormalReused = 256, |
| MKLDNNReused = 512, |
| MKLDNNReusedDiffDim = 1024, |
| NormalReshapedReused = 2048, |
| NormalReusedDiffDtype = 4096, |
| All = 8191, |
| }; |
| |
| |
| inline NDArray CreateKernelNDArray(mxnet::TShape kernel, int num_filters, mxnet::TShape input, |
| bool is_deconv = false) { |
| CHECK_EQ(kernel.ndim(), 2) << "mkldnn only supports 2d filters on 4d inputs"; |
| mxnet::TShape target_shape(4, -1); |
| target_shape[0] = is_deconv ? input[1] : num_filters; |
| target_shape[1] = is_deconv ? num_filters : input[1]; |
| target_shape[2] = kernel[0]; |
| target_shape[3] = kernel[1]; |
| int dtype = mshadow::DataType<mshadow::default_real_t>::kFlag; |
| NDArray arr(target_shape, Context()); |
| auto pd = GetMemPD(target_shape, dtype, mkldnn::memory::format::nchw); |
| InitMKLDNNArray(&arr, pd); |
| return arr; |
| } |
| |
| inline NDArray CreateBiasNDArray(mxnet::TShape target_shape) { |
| int dtype = mshadow::DataType<mshadow::default_real_t>::kFlag; |
| NDArray arr(target_shape, Context()); |
| auto pd = GetMemPD(target_shape, dtype, mkldnn::memory::format::x); |
| InitMKLDNNArray(&arr, pd); |
| return arr; |
| } |
| |
| inline int CalculateWidthConvOutput(int width, int kernel, int padding, int stride) { |
| return (width - kernel + 2 * padding) / stride + 1; |
| } |
| |
| inline int CalculateWidthDeconvOutput(int width, int kernel, int padding, int stride) { |
| return stride * (width - 1) + kernel - 2 * padding; |
| } |
| |
| inline std::string CreateShapeString(int value, int dim) { |
| std::stringstream ss; |
| ss << "("; |
| for (int i = 0; i < dim; i++) { |
| ss << value; |
| if (i != dim - 1) ss << ","; |
| } |
| ss << ")"; |
| return ss.str(); |
| } |
| |
| inline void PrintVerifyMsg(const NDArrayAttrs &arr1, const NDArrayAttrs &arr2) { |
| mxnet::TShape t1 = arr1.arr.shape(); |
| mxnet::TShape t2 = arr2.arr.shape(); |
| std::stringstream ss; |
| std::cout << "Verifying: " << arr1.desc.c_str() << " " << |
| t1 << " with " << arr2.desc.c_str() << " " << t2 << "\n"; |
| } |
| |
| /* |
| * We want to get a few types of NDArrays for testing: |
| * 1. Normal NDArray |
| * 2. Normal NDArray with MKLDNN layout (output from an MKLDNN operator) |
| * 3. Normal NDArray with MKLDNN layout whose MKLDNN memory may have different |
| * dimensions from the NDArray (result of MKLDNNDataReorderAsync). However, this |
| * type of NDArrays only exists for weight arrays. I don't think we should |
| * pass them to all operators. |
| * In the inference mode, the MKLDNN memory in the weight array will be |
| * reordered to 5 dimensions. |
| * 4. Reshaped/sliced NDArray |
| * 5. Reshaped/sliced NDArray with MKLDNN layout (reshape/slice from Normal NDArray |
| * with MKLDNN layout) |
| * 6. Reshaped/sliced NDArray with MKLDNN layout whose MKLDNN memory may have |
| * different dimensions from the NDArray (result of MKLDNNDataReorderAsync). |
| * However, this type of NDArrays only exists for weight arrays. I don't think |
| * we should pass them to all operators. |
| * In the inference mode, the MKLDNN memory in the weight array will be |
| * reordered to 5 dimensions. |
| * |
| * num_inputs / dim arguments used to scale shape (used for concat backwards to enlarge input shapes) |
| */ |
| inline std::vector<NDArrayAttrs> GetTestInputArrays( |
| int types = ArrayTypes::All, bool rand = false, |
| std::vector<float> scale = {1}, bool spatial_data_format = false) { |
| TestArrayShapes tas = GetTestArrayShapes(spatial_data_format); |
| std::vector<mxnet::TShape> shapes = tas.shapes; |
| std::vector<mkldnn::memory::primitive_desc> pds = tas.pds; |
| |
| std::vector<NDArrayAttrs> in_arrs; |
| std::string desc; |
| |
| int slice_amount = scale[0]; |
| for (auto shape : shapes) { |
| if (scale.size() > shape.ndim()) |
| continue; |
| |
| for (size_t dim = 0; dim < scale.size(); ++dim) |
| shape[dim] = static_cast<int>(round(shape[dim] * scale[dim])); |
| |
| // Type 1. |
| NDArray arr(shape, Context()); |
| if (types & ArrayTypes::Normal) { |
| InitDefaultArray(&arr, rand); |
| in_arrs.emplace_back(arr, "Normal NDArray"); |
| } |
| |
| // Type 4 |
| arr = NDArray(shape, Context()); |
| if (types & ArrayTypes::NormalReshaped) { |
| InitDefaultArray(&arr, rand); |
| in_arrs.emplace_back(arr.Slice(slice_amount, arr.shape()[0] - slice_amount), |
| "Reshaped Normal NDArray"); |
| } |
| |
| |
| for (auto pd : pds) { |
| for (size_t dim = 0; dim < scale.size(); ++dim) { |
| // preserve if matching layout else just expand on 0 dim |
| if (shape.ndim() == pd.desc().data.ndims) |
| pd = GetExpandedMemPD(pd, scale[dim], dim); |
| else |
| pd = GetExpandedMemPD(pd, scale[dim]); |
| } |
| |
| if (shape.Size() != pd.get_size() / sizeof(mshadow::default_real_t)) |
| continue; |
| |
| // Type 2, 3. |
| arr = NDArray(shape, Context()); |
| if (shape.ndim() == pd.desc().data.ndims && IsSameShape(pd, shape) |
| && types & ArrayTypes::MKLDNN) { |
| desc = "MKLDNN NDArray"; |
| InitMKLDNNArray(&arr, pd, rand); |
| in_arrs.emplace_back(arr, desc); |
| } else if (shape.ndim() == pd.desc().data.ndims && !IsSameShape(pd, shape) |
| && types & ArrayTypes::MKLDNNDiffShape) { |
| desc = "MKLDNN NDArray with different shape"; |
| InitMKLDNNArray(&arr, pd, rand); |
| in_arrs.emplace_back(arr, desc); |
| } else if (shape.ndim() != pd.desc().data.ndims && types & ArrayTypes::MKLDNNDiffDim) { |
| std::stringstream ss; |
| ss << "MKLDNN NDArray with different dim " << |
| shape.ndim() << "/" << pd.desc().data.ndims; |
| desc = ss.str(); |
| InitMKLDNNArray(&arr, pd, rand); |
| in_arrs.emplace_back(arr, desc); |
| } |
| |
| |
| // Type 5, 6. |
| arr = NDArray(shape, Context()); |
| if (shape.ndim() == pd.desc().data.ndims && IsSameShape(pd, shape) |
| && types & ArrayTypes::MKLDNNReshaped) { |
| desc = "Reshaped MKLDNN NDArray"; |
| InitMKLDNNArray(&arr, pd, rand); |
| in_arrs.emplace_back(arr.Slice(slice_amount, arr.shape()[0] - slice_amount), desc); |
| } else if (shape.ndim() == pd.desc().data.ndims && !IsSameShape(pd, shape) |
| && types & ArrayTypes::MKLDNNReshapedDiffShape) { |
| desc = "Reshaped MKLDNN NDArray with different shape"; |
| InitMKLDNNArray(&arr, pd, rand); |
| in_arrs.emplace_back(arr.Slice(slice_amount, arr.shape()[0] - slice_amount), desc); |
| } else if (shape.ndim() != pd.desc().data.ndims |
| && types & ArrayTypes::MKLDNNReshapedDiffDim) { |
| std::stringstream ss; |
| ss << "MKLDNN NDArray with different dim " << |
| shape.ndim() << "/" << pd.desc().data.ndims; |
| desc = ss.str(); |
| InitMKLDNNArray(&arr, pd, rand); |
| in_arrs.emplace_back(arr.Slice(slice_amount, arr.shape()[0] - slice_amount), desc); |
| } |
| } |
| } |
| return in_arrs; |
| } |
| |
| /* |
| * We want to get a few types of NDArrays for testing: |
| * 1. Normal NDArray |
| * 2. Normal NDArray with MKLDNN layout (output from an MKLDNN operator) |
| * 3. Normal NDArray with MKLDNN layout whose MKLDNN memory may have different |
| * dimensions from the NDArray (result of MKLDNNDataReorderAsync). However, this |
| * type of NDArrays only exists for weight arrays. I don't think we should |
| * pass them to all operators. |
| * In the inference mode, the MKLDNN memory in the weight array will be |
| * reordered to 5 dimensions. |
| * 4. Reshaped/sliced NDArray |
| * 5. Reused NDArray (this is created by the MXNet executor). This type of |
| * NDArrays can only be used as output arrays. |
| * 6. Reused NDArray converted from an array with a different data type. |
| * 7. Reused reshaped/sliced NDArray. |
| * 8. Reused NDArray with MKLDNN layout. |
| * 9. Reused NDArray with MKLDNN layout of different dimensions. |
| * |
| * Optional num_inputs / dim args can be passed to modify input shape (used for Concat test) |
| */ |
| inline std::vector<NDArrayAttrs> GetTestOutputArrays( |
| const mxnet::TShape &shp, |
| const std::vector<mkldnn::memory::primitive_desc> &pds, |
| std::vector<float>scale = {1}, bool rand = true, int types = ArrayTypes::All) { |
| mxnet::TShape shape = shp; |
| |
| for (int dim = 0; dim < scale.size(); dim++) |
| shape[dim] = static_cast<int>(shape[dim] * scale[dim]); |
| |
| std::vector<NDArrayAttrs> in_arrs; |
| std::string desc; |
| // Type 1. |
| NDArray arr(shape, Context()); |
| |
| if (types & ArrayTypes::Normal) { |
| in_arrs.emplace_back(arr, "Normal NDArray"); |
| InitDefaultArray(&in_arrs.back().arr, rand); |
| } |
| |
| mxnet::TShape tmp_shape = shape; |
| if (types & ArrayTypes::NormalReshaped) { |
| // Type 4. |
| tmp_shape[0] = shape[0] * 2; |
| NDArray arr0(tmp_shape, Context()); |
| InitDefaultArray(&arr0, rand); |
| in_arrs.emplace_back(arr0.Slice(1, shape[0] + 1), "Reshaped NDArray"); |
| } |
| |
| mxnet::TShape s(1, -1); |
| if (types & ArrayTypes::NormalReused) { |
| // Type 5. |
| // Get a reused version. |
| s[0] = shape.Size(); |
| NDArray arr1(s, Context()); |
| arr1 = arr1.AsArray(shape, arr1.dtype()); |
| InitDefaultArray(&arr1, rand); |
| in_arrs.emplace_back(arr1, "Reused NDArray"); |
| } |
| |
| if (types & ArrayTypes::NormalReusedDiffDtype) { |
| // Type 6. |
| s[0] = shape.Size() * GetTypeSize(mshadow::default_type_flag); |
| NDArray arr2(s, Context(), true, mshadow::kUint8); |
| arr2 = arr2.AsArray(shape, mshadow::default_type_flag); |
| InitDefaultArray(&arr2, rand); |
| in_arrs.emplace_back(arr2, "Reused NDArray with diff data type"); |
| } |
| |
| if (types & ArrayTypes::NormalReshapedReused) { |
| // Type 7 |
| s[0] = shape.Size() * GetTypeSize(mshadow::default_type_flag) * 2; |
| NDArray arr3(s, Context(), true, mshadow::kUint8); |
| tmp_shape[0] = shape[0] * 2; |
| arr3 = arr3.AsArray(tmp_shape, mshadow::default_type_flag); |
| InitDefaultArray(&arr3, rand); |
| in_arrs.emplace_back(arr3.Slice(1, shape[0] + 1), "Reused+Reshaped NDArray"); |
| } |
| |
| for (auto pd : pds) { |
| if (shape.Size() != pd.get_size() / sizeof(mshadow::default_real_t)) |
| continue; |
| |
| if (scale.size() > pd.desc().data.ndims) |
| continue; |
| |
| for (int dim = 0; dim < scale.size(); dim++) |
| pd = GetExpandedMemPD(pd, scale[dim]); |
| |
| // Type 2, 3. |
| arr = NDArray(shape, Context()); |
| desc = "MKLDNN NDArray"; |
| if (shape.ndim() != pd.desc().data.ndims) { |
| std::stringstream ss; |
| ss << "MKLDNN NDArray with different memory layout " |
| << shape.ndim() << "/" << pd.desc().data.ndims; |
| desc = ss.str(); |
| } |
| |
| if ((types & ArrayTypes::MKLDNN && shape.ndim() == pd.desc().data.ndims) || |
| (types & ArrayTypes::MKLDNNDiffDim && shape.ndim() != pd.desc().data.ndims)) { |
| in_arrs.emplace_back(arr, desc); |
| InitMKLDNNArray(&in_arrs.back().arr, pd, rand); |
| } |
| |
| // Type 8, 9. |
| // Get a reused version. |
| mxnet::TShape s(1, -1); |
| s[0] = shape.Size(); |
| NDArray arr = NDArray(s, Context()); |
| arr = arr.AsArray(shape, arr.dtype()); |
| InitMKLDNNArray(&arr, pd, rand); |
| desc = "Reused MKLDNN NDArray"; |
| if (shape.ndim() != pd.desc().data.ndims) { |
| std::stringstream ss; |
| ss << "Reused MKLDNN NDArray with different memory layout " |
| << shape.ndim() << "/" << pd.desc().data.ndims; |
| desc = ss.str(); |
| } |
| |
| if ((types & ArrayTypes::MKLDNNReused && shape.ndim() == pd.desc().data.ndims) || |
| (types & ArrayTypes::MKLDNNReusedDiffDim && shape.ndim() != pd.desc().data.ndims)) { |
| in_arrs.emplace_back(arr, desc); |
| } |
| } |
| return in_arrs; |
| } |
| |
| /* |
| * Determines axis ndarrays are concatenated by |
| * Used to verify concat/concat backwards operator |
| */ |
| inline int GetDim(mxnet::TShape input_shape, mxnet::TShape output_shape) { |
| CHECK(input_shape.Size() != output_shape.Size()); |
| for (size_t i = 0; i < input_shape.ndim(); i++) { |
| if (input_shape[i] != output_shape[i]) |
| return i; |
| } |
| return -1; |
| } |
| |
| /* |
| * Calculates the size of continuous block of array inside larger concatenated array |
| * Used to verify concat/concat backwards operator |
| */ |
| inline int GetBlockSize(mxnet::TShape shape, int dim) { |
| int block_size = 1; |
| for (int i = shape.ndim() - 1; i >= dim; i--) |
| block_size *= shape[i]; |
| return block_size; |
| } |
| |
| inline int CalculateWidthPoolOutput(int width, int kernel, int padding, int stride) { |
| return (width - kernel + 2 * padding) / stride + 1; |
| } |
| |
| using VerifyFunc = std::function<void (const std::vector<NDArray *> &in_arrs, |
| const std::vector<NDArray *> &out_arrs)>; |
| |
| inline void VerifyAddRequest(const std::vector<NDArray*> &in_arrs, |
| const std::vector<NDArray*> &original_outputs, |
| const std::vector<NDArray*> &new_outputs, |
| VerifyFunc verify_fn) { |
| CHECK(original_outputs.size() == new_outputs.size()); |
| std::vector<NDArray*> tmp_outputs; |
| NDArray tmp; |
| for (size_t i = 0; i < new_outputs.size(); i++) { |
| tmp = new_outputs[i]->Reorder2Default() - original_outputs[i]->Reorder2Default(); |
| tmp_outputs.push_back(&tmp); |
| } |
| Engine::Get()->WaitForAll(); |
| verify_fn(in_arrs, tmp_outputs); |
| } |
| |
| inline void VerifyCopyResult(const std::vector<NDArray *> &in_arrs, |
| const std::vector<NDArray *> &out_arrs) { |
| NDArray tmp1 = in_arrs[0]->Reorder2Default(); |
| NDArray tmp2 = out_arrs[0]->Reorder2Default(); |
| EXPECT_EQ(tmp1.shape().Size(), tmp2.shape().Size()); |
| TBlob d1 = tmp1.data(); |
| TBlob d2 = tmp2.data(); |
| EXPECT_EQ(memcmp(d1.dptr_, d2.dptr_, |
| tmp1.shape().Size() * sizeof(mshadow::default_real_t)), 0); |
| } |
| |
| inline void VerifySumResult(const std::vector<NDArray *> &in_arrs, |
| const std::vector<NDArray *> &out_arrs) { |
| NDArray in1 = in_arrs[0]->Reorder2Default(); |
| NDArray in2 = in_arrs[1]->Reorder2Default(); |
| NDArray out = out_arrs[0]->Reorder2Default(); |
| EXPECT_EQ(in1.shape().Size(), in2.shape().Size()); |
| EXPECT_EQ(in1.shape().Size(), out.shape().Size()); |
| |
| mshadow::default_real_t *d1 = in1.data().dptr<mshadow::default_real_t>(); |
| mshadow::default_real_t *d2 = in2.data().dptr<mshadow::default_real_t>(); |
| mshadow::default_real_t *o = out.data().dptr<mshadow::default_real_t>(); |
| for (size_t i = 0; i < in1.shape().Size(); i++) |
| ASSERT_EQ(d1[i] + d2[i], o[i]); |
| } |
| |
| #endif // MXNET_USE_MKLDNN |
| #endif // TEST_MKLDNN_H_ |