From c264563700658ec1793455e4a229a788e198c6c1 Mon Sep 17 00:00:00 2001 From: Alexander Zai Date: Thu, 30 Aug 2018 05:23:55 -0700 Subject: [PATCH 01/13] move mkldnn helper funcs to diff file --- .../operator/{mkldnn.cc => mkldnn_test.cc} | 505 +---------------- tests/cpp/operator/mkldnn_utils.h | 524 ++++++++++++++++++ 2 files changed, 530 insertions(+), 499 deletions(-) rename tests/cpp/operator/{mkldnn.cc => mkldnn_test.cc} (70%) create mode 100644 tests/cpp/operator/mkldnn_utils.h diff --git a/tests/cpp/operator/mkldnn.cc b/tests/cpp/operator/mkldnn_test.cc similarity index 70% rename from tests/cpp/operator/mkldnn.cc rename to tests/cpp/operator/mkldnn_test.cc index 14578bec5610..f6c1a3db9afb 100644 --- a/tests/cpp/operator/mkldnn.cc +++ b/tests/cpp/operator/mkldnn_test.cc @@ -18,7 +18,7 @@ */ /*! - * \file mkldnn.cc + * \file mkldnn_test.cc * \brief test functions in mkldnn. * \author Da Zheng */ @@ -35,6 +35,7 @@ #include "../../src/operator/nn/mkldnn/mkldnn_ops-inl.h" #include "../../src/operator/nn/mkldnn/mkldnn_pooling-inl.h" #include "../../src/operator/nn/pooling-inl.h" +#include "./mkldnn_utils.h" using namespace mxnet; @@ -95,31 +96,9 @@ TEST(MKLDNN_UTIL_FUNC, MemFormat) { CHECK_EQ(mkldnn_oihw, 15); } -// Init arrays with the default layout. -static void InitDefaultArray(NDArray *arr, bool is_rand = false) { - const TBlob &blob = arr->data(); - mshadow::default_real_t *data = blob.dptr(); - 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; - } -} - using VerifyFunc = std::function &in_arrs, const std::vector &out_arrs)>; -// Init arrays with the specified layout. -static void InitMKLDNNArray(NDArray *arr, const mkldnn::memory::primitive_desc &pd, - bool is_rand = false) { - InitDefaultArray(arr, is_rand); - arr->MKLDNNDataReorderAsync(pd); - arr->WaitToRead(); -} - static void VerifyDefMem(const mkldnn::memory &mem) { mkldnn::memory::primitive_desc pd = mem.get_primitive_desc(); mshadow::default_real_t *data @@ -153,153 +132,6 @@ static void VerifyMem(const mkldnn::memory &mem) { } } -static bool IsSameShape(mkldnn::memory::primitive_desc pd, 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; -} - -static mkldnn::memory::primitive_desc GetMemPD(const 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()); -} - -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"; - nnvm::TShape s(pd.desc().data.ndims); - for (size_t i = 0; i < pd.desc().data.ndims; i++) - s[i] = pd.desc().data.dims[i]; - s[dim] = static_cast(s[dim] * scale); - return GetMemPD(s, mshadow::DataType::kFlag, - static_cast(pd.desc().data.format)); -} - -// 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. -static std::vector 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()); - std::vector ret(2); - ret[0] = static_cast(pd.dst_primitive_desc().desc().data.format); - ret[1] = static_cast(pd.weights_primitive_desc().desc().data.format); - printf("format: %d, %d\n", ret[0], ret[1]); - 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()); - std::vector ret(1); - ret[0] = static_cast(pd.weights_primitive_desc().desc().data.format); - printf("format: %d\n", ret[0]); - return ret; - } else { - return std::vector(); - } -} - -struct TestArrayShapes { - std::vector shapes; - std::vector pds; -}; - -static TestArrayShapes GetTestArrayShapes() { - int dtype = mshadow::DataType::kFlag; - std::vector shapes; - std::vector pds; - { - // 1D - TShape s(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 - TShape s(2); - 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 - TShape s1(4); - 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)); - - TShape s2(4); - 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 formats = GetMKLDNNFormat(4, dtype); - pds.push_back(GetMemPD(s1, dtype, formats[0])); - pds.push_back(GetMemPD(s2, dtype, formats[1])); - } - { - // 5D - TShape s(5); - 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 formats = GetMKLDNNFormat(5, dtype); - pds.push_back(GetMemPD(s, dtype, formats[0])); - } - - TestArrayShapes ret; - ret.shapes = shapes; - ret.pds = pds; - return ret; -} - TEST(MKLDNN_NDArray, GetDataReorder) { TestArrayShapes tas = GetTestArrayShapes(); std::vector shapes = tas.shapes; @@ -363,39 +195,6 @@ TEST(MKLDNN_NDArray, GetDataReorder) { } } -struct NDArrayAttrs { - NDArray arr; - std::string desc; - NDArrayAttrs(NDArray arr, std::string desc) : arr(arr), desc(desc) {} -}; - -struct OpAttrs { - nnvm::NodeAttrs attrs; - std::vector dispatches; - std::set requests; - 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, -}; - OpAttrs GetCopyOp() { OpAttrs attrs; attrs.attrs.op = Op::Get("_copy"); @@ -512,18 +311,6 @@ OpAttrs GetConcatBackwardsOp(int num_args, int dim) { return attrs; } -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(); -} - - OpAttrs GetPoolingOp(int kernel, int dim, int stride, int pad) { OpAttrs attrs; attrs.attrs.op = Op::Get("Pooling"); @@ -542,23 +329,14 @@ OpAttrs GetPoolingBackwardsOp(int kernel, int dim, int stride, int pad) { attrs.attrs.op = Op::Get("_backward_Pooling"); attrs.num_inputs = dim == 2 ? 5 : 3; attrs.num_outputs = 1; - attrs.attrs.dict.insert({"kernel" , CreateShapeString(kernel, dim)}); - attrs.attrs.dict.insert({"stride" , CreateShapeString(stride, dim)}); - attrs.attrs.dict.insert({"pad" , CreateShapeString(pad, dim)}); - attrs.attrs.dict.insert({"pool_type" , "max"}); + attrs.attrs.dict.insert({"kernel", CreateShapeString(kernel, dim)}); + attrs.attrs.dict.insert({"stride", CreateShapeString(stride, dim)}); + attrs.attrs.dict.insert({"pad", CreateShapeString(pad, dim)}); + attrs.attrs.dict.insert({"pool_type", "max"}); attrs.attrs.op->attr_parser(&attrs.attrs); return attrs; } - -void PrintVerifyMsg(const NDArrayAttrs &arr1, const NDArrayAttrs &arr2) { - TShape t1 = arr1.arr.shape(); - TShape t2 = arr2.arr.shape(); - std::stringstream ss; - std::cout << "Verifying: " << arr1.desc.c_str() << " " << - t1 << " with " << arr2.desc.c_str() << " " << t2 << "\n"; -} - OpAttrs GetLRNOp() { OpAttrs attrs; attrs.attrs.op = Op::Get("LRN"); @@ -591,249 +369,6 @@ OpAttrs GetLRNBackwardsOp() { return attrs; } -/* - * 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) - */ -std::vector GetTestInputArrays( - int types = ArrayTypes::All, bool rand = false, - int num_inputs = 1, int dim = 0) { - TestArrayShapes tas = GetTestArrayShapes(); - std::vector shapes = tas.shapes; - std::vector pds = tas.pds; - - std::vector in_arrs; - std::string desc; - - int slice_amount = 1; - if (dim == 0) - slice_amount = num_inputs; - for (auto shape : shapes) { - if (dim >= shape.ndim()) - continue; - shape[dim] = shape[dim] * num_inputs; - - // 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) { - if (num_inputs > 1) { - // preserve if matching layout else just expand on 0 dim - if (shape.ndim() == pd.desc().data.ndims) - pd = GetExpandedMemPD(pd, num_inputs, dim); - else - pd = GetExpandedMemPD(pd, num_inputs); - } - - 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) - */ -std::vector GetTestOutputArrays( - const TShape &shp, - const std::vector &pds, - std::vectorscale = {1}, bool rand = true, int types = ArrayTypes::All) { - TShape shape = shp; - - for (int dim = 0; dim < scale.size(); dim++) - shape[dim] = static_cast(shape[dim] * scale[dim]); - - std::vector 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); - } - - 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"); - } - - nnvm::TShape s(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. - nnvm::TShape s(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; -} - TEST(MKLDNN_NDArray, GetTestInputArraysConcat) { auto in_arrs = GetTestInputArrays(); for (int dim = 0; dim < 5; dim++) { @@ -964,30 +499,6 @@ void VerifySumBackwardsResult(const std::vector &in_arrs, } } -/* - * Determines axis ndarrays are concatenated by - * Used to verify concat/concat backwards operator - */ -int GetDim(TShape input_shape, 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 - */ -int GetBlockSize(TShape shape, int dim) { - int block_size = 1; - for (int i = shape.ndim() - 1; i >= dim; i--) - block_size *= shape[i]; - return block_size; -} - void VerifyConcatResult(const std::vector &in_arrs, const std::vector &out_arrs) { int num_inputs = in_arrs.size(); @@ -1306,10 +817,6 @@ void TestOpEx(const OpAttrs &forward_attrs, const OpAttrs &backwards_attrs) { } } -int CalculateWidthPoolOutput(int width, int kernel, int padding, int stride) { - return (width - kernel + 2 * padding) / stride + 1; -} - void TestPoolingOp(const OpAttrs &forward_attrs, const OpAttrs &backwards_attrs) { std::vector inputs(forward_attrs.num_inputs); std::vector outputs(forward_attrs.num_outputs); diff --git a/tests/cpp/operator/mkldnn_utils.h b/tests/cpp/operator/mkldnn_utils.h new file mode 100644 index 000000000000..0931270b5fe2 --- /dev/null +++ b/tests/cpp/operator/mkldnn_utils.h @@ -0,0 +1,524 @@ +/* + * 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 mkldnn_utils.cc + * \brief helper functions to test mkldnn. + * \author Alex Zai + */ + +#if MXNET_USE_MKLDNN == 1 + +#include "mxnet/imperative.h" +#include "../../src/operator/nn/mkldnn/mkldnn_base-inl.h" + +struct TestArrayShapes { + std::vector shapes; + std::vector pds; +}; + +static TestArrayShapes GetTestArrayShapes() { + int dtype = mshadow::DataType::kFlag; + std::vector shapes; + std::vector pds; + { + // 1D + TShape s(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 + TShape s(2); + 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 + TShape s1(4); + 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)); + + TShape s2(4); + 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 formats = GetMKLDNNFormat(4, dtype); + pds.push_back(GetMemPD(s1, dtype, formats[0])); + pds.push_back(GetMemPD(s2, dtype, formats[1])); + } + { + // 5D + TShape s(5); + 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 formats = GetMKLDNNFormat(5, dtype); + pds.push_back(GetMemPD(s, dtype, formats[0])); + } + + TestArrayShapes ret; + ret.shapes = shapes; + ret.pds = pds; + return ret; +} + +// Init arrays with the default layout. +static void InitDefaultArray(NDArray *arr, bool is_rand = false) { + const TBlob &blob = arr->data(); + mshadow::default_real_t *data = blob.dptr(); + 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. +static void InitMKLDNNArray(NDArray *arr, const mkldnn::memory::primitive_desc &pd, + bool is_rand = false) { + InitDefaultArray(arr, is_rand); + arr->MKLDNNDataReorderAsync(pd); + arr->WaitToRead(); +} + +static bool IsSameShape(mkldnn::memory::primitive_desc pd, 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; +} + +static mkldnn::memory::primitive_desc GetMemPD(const 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()); +} + +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"; + nnvm::TShape s(pd.desc().data.ndims); + for (size_t i = 0; i < pd.desc().data.ndims; i++) + s[i] = pd.desc().data.dims[i]; + s[dim] = static_cast(s[dim] * scale); + return GetMemPD(s, mshadow::DataType::kFlag, + static_cast(pd.desc().data.format)); +} + +// 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. +static std::vector 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()); + std::vector ret(2); + ret[0] = static_cast(pd.dst_primitive_desc().desc().data.format); + ret[1] = static_cast(pd.weights_primitive_desc().desc().data.format); + printf("format: %d, %d\n", ret[0], ret[1]); + 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()); + std::vector ret(1); + ret[0] = static_cast(pd.weights_primitive_desc().desc().data.format); + printf("format: %d\n", ret[0]); + return ret; + } else { + return std::vector(); + } +} + +struct NDArrayAttrs { + NDArray arr; + std::string desc; + NDArrayAttrs(NDArray arr, std::string desc) : arr(arr), desc(desc) {} +}; + +struct OpAttrs { + nnvm::NodeAttrs attrs; + std::vector dispatches; + std::set requests; + 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, +}; + +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(); +} + +void PrintVerifyMsg(const NDArrayAttrs &arr1, const NDArrayAttrs &arr2) { + TShape t1 = arr1.arr.shape(); + 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) + */ +std::vector GetTestInputArrays( + int types = ArrayTypes::All, bool rand = false, + int num_inputs = 1, int dim = 0) { + TestArrayShapes tas = GetTestArrayShapes(); + std::vector shapes = tas.shapes; + std::vector pds = tas.pds; + + std::vector in_arrs; + std::string desc; + + int slice_amount = 1; + if (dim == 0) + slice_amount = num_inputs; + for (auto shape : shapes) { + if (dim >= shape.ndim()) + continue; + shape[dim] = shape[dim] * num_inputs; + + // 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) { + if (num_inputs > 1) { + // preserve if matching layout else just expand on 0 dim + if (shape.ndim() == pd.desc().data.ndims) + pd = GetExpandedMemPD(pd, num_inputs, dim); + else + pd = GetExpandedMemPD(pd, num_inputs); + } + + 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) + */ +std::vector GetTestOutputArrays( + const TShape &shp, + const std::vector &pds, + std::vectorscale = {1}, bool rand = true, int types = ArrayTypes::All) { + TShape shape = shp; + + for (int dim = 0; dim < scale.size(); dim++) + shape[dim] = static_cast(shape[dim] * scale[dim]); + + std::vector 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); + } + + 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"); + } + + nnvm::TShape s(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. + nnvm::TShape s(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 + */ +int GetDim(TShape input_shape, 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 + */ +int GetBlockSize(TShape shape, int dim) { + int block_size = 1; + for (int i = shape.ndim() - 1; i >= dim; i--) + block_size *= shape[i]; + return block_size; +} + +int CalculateWidthPoolOutput(int width, int kernel, int padding, int stride) { + return (width - kernel + 2 * padding) / stride + 1; +} + +#endif \ No newline at end of file From 2d6d395045d6652d8a5ae9b7fa1f4c917b8aa660 Mon Sep 17 00:00:00 2001 From: Alexander Zai Date: Thu, 30 Aug 2018 07:33:43 -0700 Subject: [PATCH 02/13] create test file to test helper functions --- tests/cpp/operator/mkldnn_operator_test.cc | 753 ++++++++++++++++++ tests/cpp/operator/mkldnn_test.cc | 865 ++------------------- tests/cpp/operator/mkldnn_utils.h | 213 +++-- 3 files changed, 942 insertions(+), 889 deletions(-) create mode 100644 tests/cpp/operator/mkldnn_operator_test.cc diff --git a/tests/cpp/operator/mkldnn_operator_test.cc b/tests/cpp/operator/mkldnn_operator_test.cc new file mode 100644 index 000000000000..2dac49d5a39e --- /dev/null +++ b/tests/cpp/operator/mkldnn_operator_test.cc @@ -0,0 +1,753 @@ +/* + * 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 mkldnn_test.cc + * \brief test functions in mkldnn. + * \author Alex Zai + */ + +#if MXNET_USE_MKLDNN == 1 + +#include +#include +#include +#include +#include "gtest/gtest.h" +#include "mxnet/imperative.h" +#include "../../src/operator/nn/mkldnn/mkldnn_base-inl.h" +#include "../../src/operator/nn/mkldnn/mkldnn_ops-inl.h" +#include "../../src/operator/nn/mkldnn/mkldnn_pooling-inl.h" +#include "../../src/operator/nn/pooling-inl.h" +#include "./mkldnn_utils.h" + +using namespace mxnet; + +OpAttrs GetCopyOp() { + OpAttrs attrs; + attrs.attrs.op = Op::Get("_copy"); + attrs.num_inputs = 1; + attrs.num_outputs = 1; + attrs.dispatches.resize(2); + attrs.dispatches[0] = DispatchMode::kFCompute; + attrs.dispatches[1] = DispatchMode::kFComputeEx; + attrs.requests.insert(OpReqType::kWriteTo); + attrs.requests.insert(OpReqType::kWriteInplace); + attrs.requests.insert(OpReqType::kAddTo); + return attrs; +} + +OpAttrs GetCopyBackwardsOp() { + OpAttrs attrs; + attrs.attrs.op = Op::Get("_backward_copy"); + attrs.num_inputs = 1; + attrs.num_outputs = 1; + attrs.dispatches.resize(2); + attrs.dispatches[0] = DispatchMode::kFCompute; + attrs.dispatches[1] = DispatchMode::kFComputeEx; + attrs.requests.insert(OpReqType::kWriteTo); + attrs.requests.insert(OpReqType::kWriteInplace); + attrs.requests.insert(OpReqType::kAddTo); + return attrs; +} + +OpAttrs GetReluOp() { + OpAttrs attrs; + attrs.attrs.op = Op::Get("Activation"); + attrs.attrs.dict.insert({"act_type", "relu"}); + attrs.attrs.op->attr_parser(&attrs.attrs); + attrs.num_inputs = 1; + attrs.num_outputs = 1; + attrs.dispatches.resize(2); + attrs.dispatches[0] = DispatchMode::kFCompute; + attrs.dispatches[1] = DispatchMode::kFComputeEx; + attrs.requests.insert(OpReqType::kWriteTo); + attrs.requests.insert(OpReqType::kWriteInplace); + attrs.requests.insert(OpReqType::kAddTo); + return attrs; +} + +OpAttrs GetReluBackwardsOp() { + OpAttrs attrs; + attrs.attrs.op = Op::Get("_backward_Activation"); + attrs.attrs.dict.insert({"act_type", "relu"}); + attrs.attrs.op->attr_parser(&attrs.attrs); + attrs.num_inputs = 2; + attrs.num_outputs = 1; + attrs.dispatches.resize(2); + attrs.dispatches[0] = DispatchMode::kFCompute; + attrs.dispatches[1] = DispatchMode::kFComputeEx; + attrs.requests.insert(OpReqType::kWriteTo); + attrs.requests.insert(OpReqType::kWriteInplace); + attrs.requests.insert(OpReqType::kAddTo); + return attrs; +} + +OpAttrs GetSumOp() { + OpAttrs attrs; + attrs.attrs.op = Op::Get("elemwise_add"); + attrs.num_inputs = 2; + attrs.num_outputs = 1; + attrs.dispatches.resize(2); + attrs.dispatches[0] = DispatchMode::kFCompute; + attrs.dispatches[1] = DispatchMode::kFComputeEx; + attrs.requests.insert(OpReqType::kWriteTo); + attrs.requests.insert(OpReqType::kWriteInplace); + attrs.requests.insert(OpReqType::kAddTo); + return attrs; +} + +OpAttrs GetSumBackwardsOp() { + OpAttrs attrs; + attrs.attrs.op = Op::Get("_backward_add"); + attrs.num_inputs = 1; + attrs.num_outputs = 2; + attrs.dispatches.resize(2); + attrs.dispatches[0] = DispatchMode::kFCompute; + attrs.dispatches[1] = DispatchMode::kFComputeEx; + attrs.requests.insert(OpReqType::kWriteTo); + attrs.requests.insert(OpReqType::kWriteInplace); + attrs.requests.insert(OpReqType::kAddTo); + return attrs; +} + +OpAttrs GetConcatOp(int num_args, int dim) { + OpAttrs attrs; + attrs.attrs.op = Op::Get("concat"); + attrs.num_inputs = num_args; + attrs.num_outputs = 1; + attrs.attrs.dict.insert({"num_args" , std::to_string(num_args)}); + attrs.attrs.dict.insert({"dim" , std::to_string(dim)}); + attrs.attrs.op->attr_parser(&attrs.attrs); + attrs.dispatches.resize(2); + attrs.dispatches[0] = DispatchMode::kFCompute; + attrs.dispatches[1] = DispatchMode::kFComputeEx; + return attrs; +} + +OpAttrs GetConcatBackwardsOp(int num_args, int dim) { + OpAttrs attrs; + attrs.attrs.op = Op::Get("_backward_Concat"); + attrs.num_inputs = 2; + attrs.num_outputs = num_args; + attrs.attrs.dict.insert({"num_args" , std::to_string(num_args)}); + attrs.attrs.dict.insert({"dim" , std::to_string(dim)}); + attrs.attrs.op->attr_parser(&attrs.attrs); + attrs.dispatches.resize(2); + attrs.dispatches[0] = DispatchMode::kFCompute; + attrs.dispatches[1] = DispatchMode::kFComputeEx; + return attrs; +} + +OpAttrs GetPoolingOp(int kernel, int dim, int stride, int pad) { + OpAttrs attrs; + attrs.attrs.op = Op::Get("Pooling"); + attrs.num_inputs = 1; + attrs.num_outputs = dim == 2 ? 2 : 1; + attrs.attrs.dict.insert({"kernel" , CreateShapeString(kernel, dim)}); + attrs.attrs.dict.insert({"stride" , CreateShapeString(stride, dim)}); + attrs.attrs.dict.insert({"pad" , CreateShapeString(pad, dim)}); + attrs.attrs.dict.insert({"pool_type" , "max"}); + attrs.attrs.op->attr_parser(&attrs.attrs); + return attrs; +} + +OpAttrs GetPoolingBackwardsOp(int kernel, int dim, int stride, int pad) { + OpAttrs attrs; + attrs.attrs.op = Op::Get("_backward_Pooling"); + attrs.num_inputs = dim == 2 ? 5 : 3; + attrs.num_outputs = 1; + attrs.attrs.dict.insert({"kernel", CreateShapeString(kernel, dim)}); + attrs.attrs.dict.insert({"stride", CreateShapeString(stride, dim)}); + attrs.attrs.dict.insert({"pad", CreateShapeString(pad, dim)}); + attrs.attrs.dict.insert({"pool_type", "max"}); + attrs.attrs.op->attr_parser(&attrs.attrs); + return attrs; +} + +OpAttrs GetLRNOp() { + OpAttrs attrs; + attrs.attrs.op = Op::Get("LRN"); + attrs.num_inputs = 1; + attrs.num_outputs = 2; + attrs.attrs.dict.insert({"nsize" , "3"}); + attrs.attrs.op->attr_parser(&attrs.attrs); + attrs.dispatches.resize(2); + attrs.requests.insert(OpReqType::kWriteTo); + attrs.input_types = ArrayTypes::Normal | + ArrayTypes::MKLDNN | + ArrayTypes::NormalReshaped | + ArrayTypes::MKLDNNReshaped; + attrs.output_types = ArrayTypes::Normal | + ArrayTypes::MKLDNN | + ArrayTypes::NormalReshaped | + ArrayTypes::MKLDNNReshaped; + return attrs; +} + +OpAttrs GetLRNBackwardsOp() { + OpAttrs attrs; + attrs.attrs.op = Op::Get("_backward_LRN"); + attrs.num_inputs = 3; + attrs.num_outputs = 1; + attrs.attrs.dict.insert({"nsize" , "3"}); + attrs.attrs.op->attr_parser(&attrs.attrs); + attrs.dispatches.resize(2); + attrs.requests.insert(OpReqType::kWriteTo); + return attrs; +} + +void AssertEqual(const std::vector &in_arrs, + const std::vector &out_arrs) { + NDArray tmp1 = in_arrs[0]->Reorder2Default(); + NDArray tmp2 = out_arrs[0]->Reorder2Default(); + EXPECT_EQ(tmp1.shape().Size(), tmp2.shape().Size()); + TBlob blob1 = tmp1.data(); + TBlob blob2 = tmp2.data(); + mshadow::default_real_t *d1 = static_cast(blob1.dptr_); + mshadow::default_real_t *d2 = static_cast(blob2.dptr_); + for (int i = 0; i < tmp1.shape().Size(); i++) + ASSERT_FLOAT_EQ(d1[i], d2[i]); +} + +void VerifyActResult(const std::vector &in_arrs, + const std::vector &out_arrs) { + NDArray tmp1 = in_arrs[0]->Reorder2Default(); + NDArray tmp2 = out_arrs[0]->Reorder2Default(); + TBlob blob1 = tmp1.data(); + TBlob blob2 = tmp2.data(); + mshadow::default_real_t *d1 = static_cast(blob1.dptr_); + mshadow::default_real_t *d2 = static_cast(blob2.dptr_); + EXPECT_EQ(tmp1.shape().Size(), tmp2.shape().Size()); + for (size_t i = 0; i < tmp1.shape().Size(); i++) { + EXPECT_EQ(std::fmax(d1[i], 0), d2[i]); + } +} + +void VerifyActBackwardsResult(const std::vector &in_arrs, + const std::vector &out_arrs) { + NDArray tmp1 = in_arrs[0]->Reorder2Default(); // out grads + NDArray tmp2 = in_arrs[1]->Reorder2Default(); // input + NDArray tmp3 = out_arrs[0]->Reorder2Default(); // input grads + TBlob blob1 = tmp1.data(); + TBlob blob2 = tmp2.data(); + TBlob blob3 = tmp3.data(); + mshadow::default_real_t *d1 = static_cast(blob1.dptr_); + mshadow::default_real_t *d2 = static_cast(blob2.dptr_); + mshadow::default_real_t *d3 = static_cast(blob3.dptr_); + EXPECT_EQ(tmp1.shape().Size(), tmp2.shape().Size()); + for (size_t i = 0; i < tmp1.shape().Size(); i++) { + ASSERT_EQ(d2[i] > 0 ? d1[i] : 0, d3[i]); + } +} + +void VerifySumBackwardsResult(const std::vector &in_arrs, + const std::vector &out_arrs) { + NDArray out_grads = in_arrs[0]->Reorder2Default(); // out grads + NDArray input_grads1 = out_arrs[0]->Reorder2Default(); // input grads + NDArray input_grads2 = out_arrs[1]->Reorder2Default(); // input grads + mshadow::default_real_t *og = out_grads.data().dptr(); + mshadow::default_real_t *ig1 = input_grads1.data().dptr(); + mshadow::default_real_t *ig2 = input_grads2.data().dptr(); + for (size_t i = 0; i < out_grads.shape().Size(); i++) { + ASSERT_EQ(og[i], ig1[i]); + ASSERT_EQ(og[i], ig2[i]); + } +} + +void VerifyConcatResult(const std::vector &in_arrs, + const std::vector &out_arrs) { + int num_inputs = in_arrs.size(); + int input_size = in_arrs[0]->shape().Size(); + TShape input_shape = in_arrs[0]->shape(); + NDArray output = out_arrs[0]->Reorder2Default(); + size_t total_size = output.shape().Size(); + EXPECT_EQ(input_size * num_inputs, total_size); + mshadow::default_real_t *out_data = output.data().dptr(); + + int dim = GetDim(input_shape, output.shape()); + int block_size = GetBlockSize(input_shape, dim); + int num_blocks = input_size / block_size; + for (size_t input_num = 0; input_num < num_inputs; input_num++) { + NDArray tmp = in_arrs[input_num]->Reorder2Default(); + mshadow::default_real_t* data = tmp.data().dptr(); + for (size_t block_num = 0; block_num < num_blocks; block_num++) { + for (size_t i = 0; i < block_size; i++) + ASSERT_EQ(data[block_num * block_size + i], + out_data[(block_num * num_inputs + input_num) * block_size + i]); + } + } +} + +void VerifyConcatBackwardsResult(const std::vector &in_arrs, + const std::vector &out_arrs) { + // in_arrs is larger array, out_arr is ammler + int num_inputs = out_arrs.size(); + int input_size = out_arrs[0]->shape().Size(); + TShape input_shape = out_arrs[0]->shape(); + NDArray output = in_arrs[0]->Reorder2Default(); + size_t total_size = output.shape().Size(); + EXPECT_EQ(input_size * num_inputs, total_size); + mshadow::default_real_t *out_data = output.data().dptr(); + + int dim = GetDim(input_shape, output.shape()); + int block_size = GetBlockSize(input_shape, dim); + int num_blocks = input_size / block_size; + for (size_t input_num = 0; input_num < num_inputs; input_num++) { + NDArray tmp = out_arrs[input_num]->Reorder2Default(); + mshadow::default_real_t* data = tmp.data().dptr(); + for (size_t block_num = 0; block_num < num_blocks; block_num++) { + for (size_t i = 0; i < block_size; i++) + ASSERT_EQ(data[block_num * block_size + i], + out_data[(block_num * num_inputs + input_num) * block_size + i]); + } + } +} + +TEST(MKLDNN_NDArray, CopyFrom) { + TestArrayShapes tas = GetTestArrayShapes(); + std::vector pds = tas.pds; + + std::vector in_arrs = GetTestInputArrays(); + for (auto &in_arr : in_arrs) { + if (in_arr.arr.IsMKLDNNData() && in_arr.arr.IsView()) + continue; + std::vector out_arrs = GetTestOutputArrays(in_arr.arr.shape(), pds); + for (auto &out_arr : out_arrs) { + const mkldnn::memory *mem = in_arr.arr.GetMKLDNNData(); + out_arr.arr.CopyFrom(*mem); + MKLDNNStream::Get()->Submit(); + std::vector inputs(1); + inputs[0] = &in_arr.arr; + VerifyCopyResult(inputs, {&out_arr.arr}); + } + } +} + +void TestOp(const OpAttrs &attrs, VerifyFunc verify_fn) { + std::vector inputs(attrs.num_inputs); + std::vector outputs(attrs.num_outputs); + std::vector req(attrs.num_outputs); + std::vector in_arrs; + std::vector> out_arrs(attrs.num_outputs); + std::vector dispatches = attrs.dispatches; + + TestArrayShapes tas = GetTestArrayShapes(); + std::vector pds = tas.pds; + + if (attrs.requests.find(OpReqType::kWriteTo) != attrs.requests.end()) { + std::vector in_arrs = GetTestInputArrays(); + for (auto &in_arr : in_arrs) { + for (auto &dispatch : dispatches) { + std::vector> out_arrs(attrs.num_outputs); + for (int i = 0; i < attrs.num_outputs; i++) + out_arrs[i] = GetTestOutputArrays(in_arr.arr.shape(), pds); + for (int i = 0; i < attrs.num_inputs; i++) + inputs[i] = &in_arr.arr; + for (size_t output_i = 0; output_i < out_arrs[0].size(); output_i++) { + for (int i = 0; i < attrs.num_outputs; i++) { + req[i] = kWriteTo; + outputs[i] = &out_arrs[i][output_i].arr; + } + PrintVerifyMsg(in_arr, out_arrs[0][output_i]); + Imperative::Get()->InvokeOp(Context(), attrs.attrs, inputs, + outputs, req, dispatch, mxnet::OpStatePtr()); + Engine::Get()->WaitForAll(); + verify_fn(inputs, outputs); + } + } + } + } + + if (attrs.requests.find(OpReqType::kWriteInplace) != attrs.requests.end()) { + for (auto &dispatch : dispatches) { + in_arrs = GetTestInputArrays(); + for (auto &arr : in_arrs) { + // If the array is a view, we shouldn't write data to it. + if (arr.arr.IsView()) + continue; + NDArrayAttrs orig(arr.arr.Copy(arr.arr.ctx()), "InPlace Copy"); + for (int i = 0; i < attrs.num_inputs; i++) + inputs[i] = &arr.arr; + for (int i = 0; i < attrs.num_outputs; i++) { + req[i] = kWriteInplace; + outputs[i] = &arr.arr; + } + PrintVerifyMsg(orig, arr); + Imperative::Get()->InvokeOp(Context(), attrs.attrs, inputs, outputs, req, + dispatch, mxnet::OpStatePtr()); + Engine::Get()->WaitForAll(); + std::vector orig_inputs(attrs.num_inputs); + for (int i = 0; i < attrs.num_inputs; i++) + orig_inputs[i] = &orig.arr; + verify_fn(orig_inputs, outputs); + } + } + } + + if (attrs.requests.find(OpReqType::kAddTo) != attrs.requests.end()) { + std::vector original_outputs(attrs.num_outputs); + in_arrs = GetTestInputArrays(); + for (auto &in_arr : in_arrs) { + for (auto &dispatch : dispatches) { + for (int i = 0; i < attrs.num_outputs; i++) + out_arrs[i] = GetTestOutputArrays(in_arr.arr.shape(), pds); + for (size_t i = 0; i < attrs.num_inputs; i++) + inputs[i] = &in_arr.arr; + for (size_t output_i = 0; output_i < out_arrs[0].size(); output_i++) { + NDArray tmp; + for (size_t i = 0; i < attrs.num_outputs; i++) { + auto out_arr = out_arrs[i][output_i]; + tmp = out_arr.arr.Copy(out_arr.arr.ctx()); + original_outputs[i] = &tmp; + outputs[i] = &out_arrs[i][output_i].arr; + req[i] = kAddTo; + } + PrintVerifyMsg(in_arr, out_arrs[0][output_i]); + Imperative::Get()->InvokeOp(Context(), attrs.attrs, inputs, + outputs, req, dispatch, mxnet::OpStatePtr()); + Engine::Get()->WaitForAll(); + VerifyAddRequest(inputs, original_outputs, outputs, verify_fn); + } + } + } + } +} + +void TestConcatOp(const OpAttrs &attrs, VerifyFunc verify_fn, + bool backwards = false) { + std::vector inputs(attrs.num_inputs); + std::vector outputs(attrs.num_outputs); + std::vector req(attrs.num_outputs); + std::vector dispatches = attrs.dispatches; + + TestArrayShapes tas = GetTestArrayShapes(); + std::vector pds = tas.pds; + + std::vector in_arrs = GetTestInputArrays(); + + // concat backwards uses scaled up inputs + if (backwards) { + std::string str_dim = const_cast(attrs).attrs.dict["dim"]; + int dim = std::stoi(str_dim); + in_arrs = GetTestInputArrays(ArrayTypes::All, false, attrs.num_outputs, dim); + } + + for (auto &in_arr : in_arrs) { + for (auto &dispatch : dispatches) { + std::vector> out_arrs(attrs.num_outputs); + + std::string str_dim = const_cast(attrs).attrs.dict["dim"]; + int dim = std::stoi(str_dim); + if (dim >= in_arr.arr.shape().ndim()) + continue; + float scale = backwards ? 1 / static_cast(attrs.num_outputs) : + static_cast(attrs.num_inputs); + + std::vector scale_vector(in_arr.arr.shape().ndim()); + for (int i = 0; i < in_arr.arr.shape().ndim(); i++) + scale_vector[i] = 1; + scale_vector[dim] = scale; + for (int i = 0; i < attrs.num_outputs; i++) + out_arrs[i] = GetTestOutputArrays(in_arr.arr.shape(), pds, scale_vector); + + for (int i = 0; i < attrs.num_inputs; i++) + inputs[i] = &in_arr.arr; + + for (size_t output_i = 0; output_i < out_arrs[0].size(); output_i++) { + for (int i = 0; i < attrs.num_outputs; i++) { + req[i] = kWriteTo; + outputs[i] = &out_arrs[i][output_i].arr; + } + PrintVerifyMsg(in_arr, out_arrs[0][output_i]); + Imperative::Get()->InvokeOp(Context(), attrs.attrs, inputs, + outputs, req, dispatch, mxnet::OpStatePtr()); + Engine::Get()->WaitForAll(); + verify_fn(inputs, outputs); + } + } + } +} + +// compares output of fcompute with fcomputex +void TestOpEx(const OpAttrs &forward_attrs, const OpAttrs &backwards_attrs) { + std::vector inputs(forward_attrs.num_inputs); + std::vector outputs(forward_attrs.num_outputs); + std::vector ex_outputs(forward_attrs.num_outputs); + + std::vector backwards_input(backwards_attrs.num_inputs); + std::vector backwards_outputs(backwards_attrs.num_outputs); + std::vector backwards_ex_outputs(backwards_attrs.num_outputs); + + + std::vector req(forward_attrs.num_outputs); + std::vector back_req(backwards_attrs.num_outputs); + + TestArrayShapes tas = GetTestArrayShapes(); + std::vector pds = tas.pds; + + std::vector in_arrs = GetTestInputArrays(forward_attrs.input_types, true); + std::vector> out_arrs(forward_attrs.num_outputs); + std::vector> ex_out_arrs(forward_attrs.num_outputs); + + if (forward_attrs.requests.find(OpReqType::kWriteTo) != forward_attrs.requests.end()) { + for (int i1 = 0; i1 < in_arrs.size(); i1++) { + auto in_arr = in_arrs[i1]; + + // TODO(alex): (MXNET-845) Remove when MKLDNN supports other dims + if (in_arr.arr.shape().ndim() != 4) + continue; + + for (int i = 0; i < forward_attrs.num_outputs; i++) { + out_arrs[i] = + GetTestOutputArrays(in_arr.arr.shape(), pds, {1}, forward_attrs.output_types); + ex_out_arrs[i] = + GetTestOutputArrays(in_arr.arr.shape(), pds, {1}, forward_attrs.output_types); + } + + for (int i = 0; i < forward_attrs.num_inputs; i++) + inputs[i] = &in_arr.arr; + + for (size_t output_i = 0; output_i < out_arrs[0].size(); output_i++) { + if (out_arrs[0][output_i].arr.IsMKLDNNData()) + continue; + + for (int i = 0; i < forward_attrs.num_outputs; i++) { + req[i] = kWriteTo; + outputs[i] = &out_arrs[i][output_i].arr; + ex_outputs[i] = &ex_out_arrs[i][output_i].arr; + } + Imperative::Get()->set_is_training(true); + + PrintVerifyMsg(in_arr, out_arrs[0][output_i]); + Imperative::Get()->InvokeOp( + Context(), forward_attrs.attrs, inputs, outputs, req, + DispatchMode::kFCompute, mxnet::OpStatePtr()); + Imperative::Get()->InvokeOp( + Context(), forward_attrs.attrs, inputs, ex_outputs, req, + DispatchMode::kFComputeEx, mxnet::OpStatePtr()); + Engine::Get()->WaitForAll(); + AssertEqual(outputs, ex_outputs); + + // backwards test performed same time since output needed + backwards_input[0] = outputs[0]; // output grad + backwards_input[1] = inputs[0]; // input + backwards_input[2] = outputs[1]; // out norm + + auto tmp_output = GetTestInputArrays(forward_attrs.input_types, true)[i1]; + backwards_outputs[0] = &tmp_output.arr; + + auto tmp_output2 = GetTestInputArrays(forward_attrs.input_types, true)[i1]; + backwards_ex_outputs[0] = &tmp_output2.arr; + + for (int i = 0; i < backwards_attrs.num_outputs; i++) + back_req[i] = kWriteTo; + + std::cout << "Backwards: "; + PrintVerifyMsg(out_arrs[0][output_i], tmp_output); + Imperative::Get()->InvokeOp( + Context(), backwards_attrs.attrs, backwards_input, backwards_outputs, + back_req, DispatchMode::kFCompute, mxnet::OpStatePtr()); + Imperative::Get()->InvokeOp( + Context(), backwards_attrs.attrs, backwards_input, backwards_ex_outputs, + back_req, DispatchMode::kFComputeEx, mxnet::OpStatePtr()); + Engine::Get()->WaitForAll(); + AssertEqual(backwards_outputs, backwards_ex_outputs); + } + } + } +} + +void TestPoolingOp(const OpAttrs &forward_attrs, const OpAttrs &backwards_attrs) { + std::vector inputs(forward_attrs.num_inputs); + std::vector outputs(forward_attrs.num_outputs); + std::vector ex_outputs(forward_attrs.num_outputs); + + std::vector backwards_input(backwards_attrs.num_inputs); + std::vector backwards_outputs(backwards_attrs.num_outputs); + std::vector backwards_ex_outputs(backwards_attrs.num_outputs); + + + std::vector req(forward_attrs.num_outputs); + std::vector back_req(backwards_attrs.num_outputs); + std::vector dispatches = forward_attrs.dispatches; + + TestArrayShapes tas = GetTestArrayShapes(); + std::vector pds = tas.pds; + + mxnet::op::PoolingParam param; + param.Init(forward_attrs.attrs.dict); + TShape kernel = param.kernel; + TShape padding = param.pad; + TShape stride = param.stride; + + std::vector in_arrs = GetTestInputArrays(); + std::vector> out_arrs(forward_attrs.num_outputs); + std::vector> ex_out_arrs(forward_attrs.num_outputs); + + for (int i1 = 0; i1 < in_arrs.size(); i1++) { + auto in_arr = in_arrs[i1]; + + // can only pool only 3D and 4D inputs + TShape input_shape = in_arr.arr.shape(); + if (input_shape.ndim() != kernel.ndim() + 2) + continue; + // cannot pool if ndarray and mkldnn memory have different ndim + if (in_arr.arr.IsView() || in_arr.arr.GetMKLDNNData()->get_primitive_desc().desc().data.ndims + != in_arr.arr.shape().ndim()) + continue; + std::vector scale_vector(in_arr.arr.shape().ndim()); + for (int i = 0; i < in_arr.arr.shape().ndim(); i++) { + if (i < 2) + scale_vector[i] = 1; + else + scale_vector[i] = CalculateWidthPoolOutput( + input_shape[i], kernel[i-2], padding[i-2], stride[i-2]) / + static_cast(input_shape[i]); + } + for (int i = 0; i < forward_attrs.num_outputs; i++) { + out_arrs[i] = GetTestOutputArrays(in_arr.arr.shape(), pds, scale_vector); + ex_out_arrs[i] = GetTestOutputArrays(in_arr.arr.shape(), pds, scale_vector); + } + + for (int i = 0; i < forward_attrs.num_inputs; i++) + inputs[i] = &in_arr.arr; + + for (size_t output_i = 0; output_i < out_arrs[0].size(); output_i++) { + for (int i = 0; i < forward_attrs.num_outputs; i++) { + req[i] = kWriteTo; + outputs[i] = &out_arrs[i][output_i].arr; + ex_outputs[i] = &ex_out_arrs[i][output_i].arr; + } + Imperative::Get()->set_is_training(true); + + PrintVerifyMsg(in_arr, out_arrs[0][output_i]); + Imperative::Get()->InvokeOp(Context(), forward_attrs.attrs, inputs, + outputs, req, DispatchMode::kFCompute, mxnet::OpStatePtr()); + Imperative::Get()->InvokeOp(Context(), forward_attrs.attrs, inputs, + ex_outputs, req, DispatchMode::kFComputeEx, mxnet::OpStatePtr()); + Engine::Get()->WaitForAll(); + VerifyCopyResult(outputs, ex_outputs); + + + // backwards test performed same time since output needed + if (backwards_attrs.num_inputs == 3) { + backwards_input[0] = outputs[0]; // output grad + backwards_input[1] = inputs[0]; // input + backwards_input[2] = outputs[0]; // output + } else if (backwards_attrs.num_inputs == 5) { + backwards_input[0] = outputs[0]; // output grad + backwards_input[1] = outputs[0]; // workspace grad + backwards_input[2] = inputs[0]; // input + backwards_input[3] = outputs[0]; // output + backwards_input[4] = ex_outputs[1]; // workspace + } + + // needs copies of inputs since they be reused in next iteration + // cannot use Copy method since we need to maintain MKLDNN format + auto tmp_output = GetTestInputArrays()[i1]; + auto tmp_output2 = GetTestInputArrays()[i1]; + backwards_outputs[0] = &tmp_output.arr; + backwards_ex_outputs[0] = &tmp_output2.arr; + back_req[0] = kWriteTo; + std::cout << "Backwards: "; + PrintVerifyMsg(out_arrs[0][output_i], tmp_output); + Imperative::Get()->InvokeOp( + Context(), backwards_attrs.attrs, backwards_input, backwards_outputs, + back_req, DispatchMode::kFCompute, mxnet::OpStatePtr()); + Imperative::Get()->InvokeOp( + Context(), backwards_attrs.attrs, backwards_input, backwards_ex_outputs, + back_req, DispatchMode::kFComputeEx, mxnet::OpStatePtr()); + Engine::Get()->WaitForAll(); + VerifyCopyResult(backwards_outputs, backwards_ex_outputs); + } + } +} + +TEST(IMPERATIVE, CopyOp) { + OpAttrs attrs = GetCopyOp(); + TestOp(attrs, VerifyCopyResult); +} + +TEST(IMPERATIVE, CopyBackwardsOp) { + OpAttrs attrs = GetCopyBackwardsOp(); + TestOp(attrs, VerifyCopyResult); +} + +TEST(IMPERATIVE, ActOp) { + OpAttrs attrs = GetReluOp(); + TestOp(attrs, VerifyActResult); +} + +TEST(IMPERATIVE, ActBackwardsOp) { + OpAttrs attrs = GetReluBackwardsOp(); + TestOp(attrs, VerifyActBackwardsResult); +} + +TEST(IMPERATIVE, SumOp) { + OpAttrs attrs = GetSumOp(); + TestOp(attrs, VerifySumResult); +} + +TEST(IMPERATIVE, SumBackwardsOp) { + OpAttrs attrs = GetSumBackwardsOp(); + TestOp(attrs, VerifySumBackwardsResult); +} + +TEST(IMPERATIVE, ConcatOp) { + for (int num_inputs = 2; num_inputs < 4; num_inputs++) { + for (int dim = 0; dim < 5; dim++) { + OpAttrs attrs = GetConcatOp(num_inputs, dim); + TestConcatOp(attrs, VerifyConcatResult); + } + } +} + +TEST(IMPERATIVE, ConcatBackwardsOp) { + for (int num_inputs = 2; num_inputs < 4; num_inputs++) { + for (int dim = 0; dim < 5; dim++) { + OpAttrs attrs = GetConcatBackwardsOp(num_inputs, dim); + TestConcatOp(attrs, VerifyConcatBackwardsResult, true); + } + } +} + +TEST(IMPERATIVE, LRNOp) { + OpAttrs forward_attrs = GetLRNOp(); + OpAttrs backwards_attrs = GetLRNBackwardsOp(); + TestOpEx(forward_attrs, backwards_attrs); +} + +TEST(IMPERATIVE, PoolingOp) { + for (int dim = 2; dim < 4; dim++) { + for (int kernel = 1; kernel < 4; kernel++) { + for (int stride = 1; stride < 3; stride++) { + for (int pad = 0; pad < 2; pad++) { + if (kernel / 2. < pad) + continue; + OpAttrs forward_attrs = GetPoolingOp(kernel, dim, stride, pad); + OpAttrs backwards_attrs = GetPoolingBackwardsOp(kernel, dim, stride, pad); + TestPoolingOp(forward_attrs, backwards_attrs); + } + } + } + } +} + +#endif diff --git a/tests/cpp/operator/mkldnn_test.cc b/tests/cpp/operator/mkldnn_test.cc index f6c1a3db9afb..07ca262adc13 100644 --- a/tests/cpp/operator/mkldnn_test.cc +++ b/tests/cpp/operator/mkldnn_test.cc @@ -89,16 +89,6 @@ TEST(MKLDNN_UTIL_FUNC, AlignMem) { #endif } -TEST(MKLDNN_UTIL_FUNC, MemFormat) { - // Check whether the number of format is correct. - CHECK_EQ(mkldnn_format_last, 67); - CHECK_EQ(mkldnn_nchw, 5); - CHECK_EQ(mkldnn_oihw, 15); -} - -using VerifyFunc = std::function &in_arrs, - const std::vector &out_arrs)>; - static void VerifyDefMem(const mkldnn::memory &mem) { mkldnn::memory::primitive_desc pd = mem.get_primitive_desc(); mshadow::default_real_t *data @@ -110,6 +100,13 @@ static void VerifyDefMem(const mkldnn::memory &mem) { EXPECT_EQ(num_same, size); } +TEST(MKLDNN_UTIL_FUNC, MemFormat) { + // Check whether the number of format is correct. + CHECK_EQ(mkldnn_format_last, 67); + CHECK_EQ(mkldnn_nchw, 5); + CHECK_EQ(mkldnn_oihw, 15); +} + static void VerifyMem(const mkldnn::memory &mem) { mkldnn::memory::primitive_desc pd = mem.get_primitive_desc(); @@ -195,804 +192,6 @@ TEST(MKLDNN_NDArray, GetDataReorder) { } } -OpAttrs GetCopyOp() { - OpAttrs attrs; - attrs.attrs.op = Op::Get("_copy"); - attrs.num_inputs = 1; - attrs.num_outputs = 1; - attrs.dispatches.resize(2); - attrs.dispatches[0] = DispatchMode::kFCompute; - attrs.dispatches[1] = DispatchMode::kFComputeEx; - attrs.requests.insert(OpReqType::kWriteTo); - attrs.requests.insert(OpReqType::kWriteInplace); - attrs.requests.insert(OpReqType::kAddTo); - return attrs; -} - -OpAttrs GetCopyBackwardsOp() { - OpAttrs attrs; - attrs.attrs.op = Op::Get("_backward_copy"); - attrs.num_inputs = 1; - attrs.num_outputs = 1; - attrs.dispatches.resize(2); - attrs.dispatches[0] = DispatchMode::kFCompute; - attrs.dispatches[1] = DispatchMode::kFComputeEx; - attrs.requests.insert(OpReqType::kWriteTo); - attrs.requests.insert(OpReqType::kWriteInplace); - attrs.requests.insert(OpReqType::kAddTo); - return attrs; -} - -OpAttrs GetReluOp() { - OpAttrs attrs; - attrs.attrs.op = Op::Get("Activation"); - attrs.attrs.dict.insert({"act_type", "relu"}); - attrs.attrs.op->attr_parser(&attrs.attrs); - attrs.num_inputs = 1; - attrs.num_outputs = 1; - attrs.dispatches.resize(2); - attrs.dispatches[0] = DispatchMode::kFCompute; - attrs.dispatches[1] = DispatchMode::kFComputeEx; - attrs.requests.insert(OpReqType::kWriteTo); - attrs.requests.insert(OpReqType::kWriteInplace); - attrs.requests.insert(OpReqType::kAddTo); - return attrs; -} - -OpAttrs GetReluBackwardsOp() { - OpAttrs attrs; - attrs.attrs.op = Op::Get("_backward_Activation"); - attrs.attrs.dict.insert({"act_type", "relu"}); - attrs.attrs.op->attr_parser(&attrs.attrs); - attrs.num_inputs = 2; - attrs.num_outputs = 1; - attrs.dispatches.resize(2); - attrs.dispatches[0] = DispatchMode::kFCompute; - attrs.dispatches[1] = DispatchMode::kFComputeEx; - attrs.requests.insert(OpReqType::kWriteTo); - attrs.requests.insert(OpReqType::kWriteInplace); - attrs.requests.insert(OpReqType::kAddTo); - return attrs; -} - -OpAttrs GetSumOp() { - OpAttrs attrs; - attrs.attrs.op = Op::Get("elemwise_add"); - attrs.num_inputs = 2; - attrs.num_outputs = 1; - attrs.dispatches.resize(2); - attrs.dispatches[0] = DispatchMode::kFCompute; - attrs.dispatches[1] = DispatchMode::kFComputeEx; - attrs.requests.insert(OpReqType::kWriteTo); - attrs.requests.insert(OpReqType::kWriteInplace); - attrs.requests.insert(OpReqType::kAddTo); - return attrs; -} - -OpAttrs GetSumBackwardsOp() { - OpAttrs attrs; - attrs.attrs.op = Op::Get("_backward_add"); - attrs.num_inputs = 1; - attrs.num_outputs = 2; - attrs.dispatches.resize(2); - attrs.dispatches[0] = DispatchMode::kFCompute; - attrs.dispatches[1] = DispatchMode::kFComputeEx; - attrs.requests.insert(OpReqType::kWriteTo); - attrs.requests.insert(OpReqType::kWriteInplace); - attrs.requests.insert(OpReqType::kAddTo); - return attrs; -} - -OpAttrs GetConcatOp(int num_args, int dim) { - OpAttrs attrs; - attrs.attrs.op = Op::Get("concat"); - attrs.num_inputs = num_args; - attrs.num_outputs = 1; - attrs.attrs.dict.insert({"num_args" , std::to_string(num_args)}); - attrs.attrs.dict.insert({"dim" , std::to_string(dim)}); - attrs.attrs.op->attr_parser(&attrs.attrs); - attrs.dispatches.resize(2); - attrs.dispatches[0] = DispatchMode::kFCompute; - attrs.dispatches[1] = DispatchMode::kFComputeEx; - return attrs; -} - -OpAttrs GetConcatBackwardsOp(int num_args, int dim) { - OpAttrs attrs; - attrs.attrs.op = Op::Get("_backward_Concat"); - attrs.num_inputs = 2; - attrs.num_outputs = num_args; - attrs.attrs.dict.insert({"num_args" , std::to_string(num_args)}); - attrs.attrs.dict.insert({"dim" , std::to_string(dim)}); - attrs.attrs.op->attr_parser(&attrs.attrs); - attrs.dispatches.resize(2); - attrs.dispatches[0] = DispatchMode::kFCompute; - attrs.dispatches[1] = DispatchMode::kFComputeEx; - return attrs; -} - -OpAttrs GetPoolingOp(int kernel, int dim, int stride, int pad) { - OpAttrs attrs; - attrs.attrs.op = Op::Get("Pooling"); - attrs.num_inputs = 1; - attrs.num_outputs = dim == 2 ? 2 : 1; - attrs.attrs.dict.insert({"kernel" , CreateShapeString(kernel, dim)}); - attrs.attrs.dict.insert({"stride" , CreateShapeString(stride, dim)}); - attrs.attrs.dict.insert({"pad" , CreateShapeString(pad, dim)}); - attrs.attrs.dict.insert({"pool_type" , "max"}); - attrs.attrs.op->attr_parser(&attrs.attrs); - return attrs; -} - -OpAttrs GetPoolingBackwardsOp(int kernel, int dim, int stride, int pad) { - OpAttrs attrs; - attrs.attrs.op = Op::Get("_backward_Pooling"); - attrs.num_inputs = dim == 2 ? 5 : 3; - attrs.num_outputs = 1; - attrs.attrs.dict.insert({"kernel", CreateShapeString(kernel, dim)}); - attrs.attrs.dict.insert({"stride", CreateShapeString(stride, dim)}); - attrs.attrs.dict.insert({"pad", CreateShapeString(pad, dim)}); - attrs.attrs.dict.insert({"pool_type", "max"}); - attrs.attrs.op->attr_parser(&attrs.attrs); - return attrs; -} - -OpAttrs GetLRNOp() { - OpAttrs attrs; - attrs.attrs.op = Op::Get("LRN"); - attrs.num_inputs = 1; - attrs.num_outputs = 2; - attrs.attrs.dict.insert({"nsize" , "3"}); - attrs.attrs.op->attr_parser(&attrs.attrs); - attrs.dispatches.resize(2); - attrs.requests.insert(OpReqType::kWriteTo); - attrs.input_types = ArrayTypes::Normal | - ArrayTypes::MKLDNN | - ArrayTypes::NormalReshaped | - ArrayTypes::MKLDNNReshaped; - attrs.output_types = ArrayTypes::Normal | - ArrayTypes::MKLDNN | - ArrayTypes::NormalReshaped | - ArrayTypes::MKLDNNReshaped; - return attrs; -} - -OpAttrs GetLRNBackwardsOp() { - OpAttrs attrs; - attrs.attrs.op = Op::Get("_backward_LRN"); - attrs.num_inputs = 3; - attrs.num_outputs = 1; - attrs.attrs.dict.insert({"nsize" , "3"}); - attrs.attrs.op->attr_parser(&attrs.attrs); - attrs.dispatches.resize(2); - attrs.requests.insert(OpReqType::kWriteTo); - return attrs; -} - -TEST(MKLDNN_NDArray, GetTestInputArraysConcat) { - auto in_arrs = GetTestInputArrays(); - for (int dim = 0; dim < 5; dim++) { - for (int num_inputs = 2; num_inputs < 5; num_inputs++) { - std::vector expanded_arrs = GetTestInputArrays( - ArrayTypes::All, false, num_inputs, dim); - int i = 0; - for (auto &arr : in_arrs) { - if (dim >= arr.arr.shape().ndim()) - continue; - auto ex_arr = expanded_arrs[i]; - PrintVerifyMsg(arr, ex_arr); - EXPECT_EQ(arr.arr.shape().Size() * num_inputs, ex_arr.arr.shape().Size()); - EXPECT_EQ(arr.arr.shape()[dim] * num_inputs, ex_arr.arr.shape()[dim]); - i++; - } - } - } -} - -TEST(MKLDNN_NDArray, GetTestOutputArraysConcat) { - auto shapes_pds = GetTestArrayShapes(); - std::vector shapes; shapes = shapes_pds.shapes; - std::vector pds = shapes_pds.pds; - for (auto &shape : shapes) { - for (int dim = 0; dim < 5; dim++) { - for (int num_inputs = 2; num_inputs < 5; num_inputs++) { - if (shape.ndim() <= dim) - continue; - std::cout << "Extending " << shape << " dim " << - dim << " and " << num_inputs << "num_inputs\n"; - std::vector scale_vector(shape.ndim()); - for (int i = 0; i < shape.ndim(); i++) - scale_vector[i] = 1; - scale_vector[dim] = num_inputs; - auto output_arrs = GetTestOutputArrays(shape, pds, scale_vector); - for (auto &out_arr : output_arrs) { - auto out_shape = out_arr.arr.shape(); - EXPECT_EQ(shape.Size() * num_inputs, out_arr.arr.shape().Size()); - EXPECT_EQ(shape[dim] * num_inputs, out_arr.arr.shape()[dim]); - } - } - } - } -} - -void VerifyCopyResult(const std::vector &in_arrs, - const std::vector &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); -} - -void AssertEqual(const std::vector &in_arrs, - const std::vector &out_arrs) { - NDArray tmp1 = in_arrs[0]->Reorder2Default(); - NDArray tmp2 = out_arrs[0]->Reorder2Default(); - EXPECT_EQ(tmp1.shape().Size(), tmp2.shape().Size()); - TBlob blob1 = tmp1.data(); - TBlob blob2 = tmp2.data(); - mshadow::default_real_t *d1 = static_cast(blob1.dptr_); - mshadow::default_real_t *d2 = static_cast(blob2.dptr_); - for (int i = 0; i < tmp1.shape().Size(); i++) - ASSERT_FLOAT_EQ(d1[i], d2[i]); -} - -void VerifyActResult(const std::vector &in_arrs, - const std::vector &out_arrs) { - NDArray tmp1 = in_arrs[0]->Reorder2Default(); - NDArray tmp2 = out_arrs[0]->Reorder2Default(); - TBlob blob1 = tmp1.data(); - TBlob blob2 = tmp2.data(); - mshadow::default_real_t *d1 = static_cast(blob1.dptr_); - mshadow::default_real_t *d2 = static_cast(blob2.dptr_); - EXPECT_EQ(tmp1.shape().Size(), tmp2.shape().Size()); - for (size_t i = 0; i < tmp1.shape().Size(); i++) { - EXPECT_EQ(std::fmax(d1[i], 0), d2[i]); - } -} - -void VerifySumResult(const std::vector &in_arrs, - const std::vector &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 *d2 = in2.data().dptr(); - mshadow::default_real_t *o = out.data().dptr(); - for (size_t i = 0; i < in1.shape().Size(); i++) - ASSERT_EQ(d1[i] + d2[i], o[i]); -} - -void VerifyActBackwardsResult(const std::vector &in_arrs, - const std::vector &out_arrs) { - NDArray tmp1 = in_arrs[0]->Reorder2Default(); // out grads - NDArray tmp2 = in_arrs[1]->Reorder2Default(); // input - NDArray tmp3 = out_arrs[0]->Reorder2Default(); // input grads - TBlob blob1 = tmp1.data(); - TBlob blob2 = tmp2.data(); - TBlob blob3 = tmp3.data(); - mshadow::default_real_t *d1 = static_cast(blob1.dptr_); - mshadow::default_real_t *d2 = static_cast(blob2.dptr_); - mshadow::default_real_t *d3 = static_cast(blob3.dptr_); - EXPECT_EQ(tmp1.shape().Size(), tmp2.shape().Size()); - for (size_t i = 0; i < tmp1.shape().Size(); i++) { - ASSERT_EQ(d2[i] > 0 ? d1[i] : 0, d3[i]); - } -} - -void VerifySumBackwardsResult(const std::vector &in_arrs, - const std::vector &out_arrs) { - NDArray out_grads = in_arrs[0]->Reorder2Default(); // out grads - NDArray input_grads1 = out_arrs[0]->Reorder2Default(); // input grads - NDArray input_grads2 = out_arrs[1]->Reorder2Default(); // input grads - mshadow::default_real_t *og = out_grads.data().dptr(); - mshadow::default_real_t *ig1 = input_grads1.data().dptr(); - mshadow::default_real_t *ig2 = input_grads2.data().dptr(); - for (size_t i = 0; i < out_grads.shape().Size(); i++) { - ASSERT_EQ(og[i], ig1[i]); - ASSERT_EQ(og[i], ig2[i]); - } -} - -void VerifyConcatResult(const std::vector &in_arrs, - const std::vector &out_arrs) { - int num_inputs = in_arrs.size(); - int input_size = in_arrs[0]->shape().Size(); - TShape input_shape = in_arrs[0]->shape(); - NDArray output = out_arrs[0]->Reorder2Default(); - size_t total_size = output.shape().Size(); - EXPECT_EQ(input_size * num_inputs, total_size); - mshadow::default_real_t *out_data = output.data().dptr(); - - int dim = GetDim(input_shape, output.shape()); - int block_size = GetBlockSize(input_shape, dim); - int num_blocks = input_size / block_size; - for (size_t input_num = 0; input_num < num_inputs; input_num++) { - NDArray tmp = in_arrs[input_num]->Reorder2Default(); - mshadow::default_real_t* data = tmp.data().dptr(); - for (size_t block_num = 0; block_num < num_blocks; block_num++) { - for (size_t i = 0; i < block_size; i++) - ASSERT_EQ(data[block_num * block_size + i], - out_data[(block_num * num_inputs + input_num) * block_size + i]); - } - } -} - -void VerifyAddRequest(const std::vector &in_arrs, - const std::vector &original_outputs, - const std::vector &new_outputs, - VerifyFunc verify_fn) { - CHECK(original_outputs.size() == new_outputs.size()); - std::vector 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); -} - -void VerifyConcatBackwardsResult(const std::vector &in_arrs, - const std::vector &out_arrs) { - // in_arrs is larger array, out_arr is ammler - int num_inputs = out_arrs.size(); - int input_size = out_arrs[0]->shape().Size(); - TShape input_shape = out_arrs[0]->shape(); - NDArray output = in_arrs[0]->Reorder2Default(); - size_t total_size = output.shape().Size(); - EXPECT_EQ(input_size * num_inputs, total_size); - mshadow::default_real_t *out_data = output.data().dptr(); - - int dim = GetDim(input_shape, output.shape()); - int block_size = GetBlockSize(input_shape, dim); - int num_blocks = input_size / block_size; - for (size_t input_num = 0; input_num < num_inputs; input_num++) { - NDArray tmp = out_arrs[input_num]->Reorder2Default(); - mshadow::default_real_t* data = tmp.data().dptr(); - for (size_t block_num = 0; block_num < num_blocks; block_num++) { - for (size_t i = 0; i < block_size; i++) - ASSERT_EQ(data[block_num * block_size + i], - out_data[(block_num * num_inputs + input_num) * block_size + i]); - } - } -} - -TEST(MKLDNN_NDArray, CopyFrom) { - TestArrayShapes tas = GetTestArrayShapes(); - std::vector pds = tas.pds; - - std::vector in_arrs = GetTestInputArrays(); - for (auto &in_arr : in_arrs) { - if (in_arr.arr.IsMKLDNNData() && in_arr.arr.IsView()) - continue; - std::vector out_arrs = GetTestOutputArrays(in_arr.arr.shape(), pds); - for (auto &out_arr : out_arrs) { - const mkldnn::memory *mem = in_arr.arr.GetMKLDNNData(); - out_arr.arr.CopyFrom(*mem); - MKLDNNStream::Get()->Submit(); - std::vector inputs(1); - inputs[0] = &in_arr.arr; - VerifyCopyResult(inputs, {&out_arr.arr}); - } - } -} - -void TestOp(const OpAttrs &attrs, VerifyFunc verify_fn) { - std::vector inputs(attrs.num_inputs); - std::vector outputs(attrs.num_outputs); - std::vector req(attrs.num_outputs); - std::vector in_arrs; - std::vector> out_arrs(attrs.num_outputs); - std::vector dispatches = attrs.dispatches; - - TestArrayShapes tas = GetTestArrayShapes(); - std::vector pds = tas.pds; - - if (attrs.requests.find(OpReqType::kWriteTo) != attrs.requests.end()) { - std::vector in_arrs = GetTestInputArrays(); - for (auto &in_arr : in_arrs) { - for (auto &dispatch : dispatches) { - std::vector> out_arrs(attrs.num_outputs); - for (int i = 0; i < attrs.num_outputs; i++) - out_arrs[i] = GetTestOutputArrays(in_arr.arr.shape(), pds); - for (int i = 0; i < attrs.num_inputs; i++) - inputs[i] = &in_arr.arr; - for (size_t output_i = 0; output_i < out_arrs[0].size(); output_i++) { - for (int i = 0; i < attrs.num_outputs; i++) { - req[i] = kWriteTo; - outputs[i] = &out_arrs[i][output_i].arr; - } - PrintVerifyMsg(in_arr, out_arrs[0][output_i]); - Imperative::Get()->InvokeOp(Context(), attrs.attrs, inputs, - outputs, req, dispatch, mxnet::OpStatePtr()); - Engine::Get()->WaitForAll(); - verify_fn(inputs, outputs); - } - } - } - } - - if (attrs.requests.find(OpReqType::kWriteInplace) != attrs.requests.end()) { - for (auto &dispatch : dispatches) { - in_arrs = GetTestInputArrays(); - for (auto &arr : in_arrs) { - // If the array is a view, we shouldn't write data to it. - if (arr.arr.IsView()) - continue; - NDArrayAttrs orig(arr.arr.Copy(arr.arr.ctx()), "InPlace Copy"); - for (int i = 0; i < attrs.num_inputs; i++) - inputs[i] = &arr.arr; - for (int i = 0; i < attrs.num_outputs; i++) { - req[i] = kWriteInplace; - outputs[i] = &arr.arr; - } - PrintVerifyMsg(orig, arr); - Imperative::Get()->InvokeOp(Context(), attrs.attrs, inputs, outputs, req, - dispatch, mxnet::OpStatePtr()); - Engine::Get()->WaitForAll(); - std::vector orig_inputs(attrs.num_inputs); - for (int i = 0; i < attrs.num_inputs; i++) - orig_inputs[i] = &orig.arr; - verify_fn(orig_inputs, outputs); - } - } - } - - if (attrs.requests.find(OpReqType::kAddTo) != attrs.requests.end()) { - std::vector original_outputs(attrs.num_outputs); - in_arrs = GetTestInputArrays(); - for (auto &in_arr : in_arrs) { - for (auto &dispatch : dispatches) { - for (int i = 0; i < attrs.num_outputs; i++) - out_arrs[i] = GetTestOutputArrays(in_arr.arr.shape(), pds); - for (size_t i = 0; i < attrs.num_inputs; i++) - inputs[i] = &in_arr.arr; - for (size_t output_i = 0; output_i < out_arrs[0].size(); output_i++) { - NDArray tmp; - for (size_t i = 0; i < attrs.num_outputs; i++) { - auto out_arr = out_arrs[i][output_i]; - tmp = out_arr.arr.Copy(out_arr.arr.ctx()); - original_outputs[i] = &tmp; - outputs[i] = &out_arrs[i][output_i].arr; - req[i] = kAddTo; - } - PrintVerifyMsg(in_arr, out_arrs[0][output_i]); - Imperative::Get()->InvokeOp(Context(), attrs.attrs, inputs, - outputs, req, dispatch, mxnet::OpStatePtr()); - Engine::Get()->WaitForAll(); - VerifyAddRequest(inputs, original_outputs, outputs, verify_fn); - } - } - } - } -} - -void TestConcatOp(const OpAttrs &attrs, VerifyFunc verify_fn, - bool backwards = false) { - std::vector inputs(attrs.num_inputs); - std::vector outputs(attrs.num_outputs); - std::vector req(attrs.num_outputs); - std::vector dispatches = attrs.dispatches; - - TestArrayShapes tas = GetTestArrayShapes(); - std::vector pds = tas.pds; - - std::vector in_arrs = GetTestInputArrays(); - - // concat backwards uses scaled up inputs - if (backwards) { - std::string str_dim = const_cast(attrs).attrs.dict["dim"]; - int dim = std::stoi(str_dim); - in_arrs = GetTestInputArrays(ArrayTypes::All, false, attrs.num_outputs, dim); - } - - for (auto &in_arr : in_arrs) { - for (auto &dispatch : dispatches) { - std::vector> out_arrs(attrs.num_outputs); - - std::string str_dim = const_cast(attrs).attrs.dict["dim"]; - int dim = std::stoi(str_dim); - if (dim >= in_arr.arr.shape().ndim()) - continue; - float scale = backwards ? 1 / static_cast(attrs.num_outputs) : - static_cast(attrs.num_inputs); - - std::vector scale_vector(in_arr.arr.shape().ndim()); - for (int i = 0; i < in_arr.arr.shape().ndim(); i++) - scale_vector[i] = 1; - scale_vector[dim] = scale; - for (int i = 0; i < attrs.num_outputs; i++) - out_arrs[i] = GetTestOutputArrays(in_arr.arr.shape(), pds, scale_vector); - - for (int i = 0; i < attrs.num_inputs; i++) - inputs[i] = &in_arr.arr; - - for (size_t output_i = 0; output_i < out_arrs[0].size(); output_i++) { - for (int i = 0; i < attrs.num_outputs; i++) { - req[i] = kWriteTo; - outputs[i] = &out_arrs[i][output_i].arr; - } - PrintVerifyMsg(in_arr, out_arrs[0][output_i]); - Imperative::Get()->InvokeOp(Context(), attrs.attrs, inputs, - outputs, req, dispatch, mxnet::OpStatePtr()); - Engine::Get()->WaitForAll(); - verify_fn(inputs, outputs); - } - } - } -} - -// compares output of fcompute with fcomputex -void TestOpEx(const OpAttrs &forward_attrs, const OpAttrs &backwards_attrs) { - std::vector inputs(forward_attrs.num_inputs); - std::vector outputs(forward_attrs.num_outputs); - std::vector ex_outputs(forward_attrs.num_outputs); - - std::vector backwards_input(backwards_attrs.num_inputs); - std::vector backwards_outputs(backwards_attrs.num_outputs); - std::vector backwards_ex_outputs(backwards_attrs.num_outputs); - - - std::vector req(forward_attrs.num_outputs); - std::vector back_req(backwards_attrs.num_outputs); - - TestArrayShapes tas = GetTestArrayShapes(); - std::vector pds = tas.pds; - - std::vector in_arrs = GetTestInputArrays(forward_attrs.input_types, true); - std::vector> out_arrs(forward_attrs.num_outputs); - std::vector> ex_out_arrs(forward_attrs.num_outputs); - - if (forward_attrs.requests.find(OpReqType::kWriteTo) != forward_attrs.requests.end()) { - for (int i1 = 0; i1 < in_arrs.size(); i1++) { - auto in_arr = in_arrs[i1]; - - // TODO(alex): (MXNET-845) Remove when MKLDNN supports other dims - if (in_arr.arr.shape().ndim() != 4) - continue; - - for (int i = 0; i < forward_attrs.num_outputs; i++) { - out_arrs[i] = - GetTestOutputArrays(in_arr.arr.shape(), pds, {1}, forward_attrs.output_types); - ex_out_arrs[i] = - GetTestOutputArrays(in_arr.arr.shape(), pds, {1}, forward_attrs.output_types); - } - - for (int i = 0; i < forward_attrs.num_inputs; i++) - inputs[i] = &in_arr.arr; - - for (size_t output_i = 0; output_i < out_arrs[0].size(); output_i++) { - if (out_arrs[0][output_i].arr.IsMKLDNNData()) - continue; - - for (int i = 0; i < forward_attrs.num_outputs; i++) { - req[i] = kWriteTo; - outputs[i] = &out_arrs[i][output_i].arr; - ex_outputs[i] = &ex_out_arrs[i][output_i].arr; - } - Imperative::Get()->set_is_training(true); - - PrintVerifyMsg(in_arr, out_arrs[0][output_i]); - Imperative::Get()->InvokeOp( - Context(), forward_attrs.attrs, inputs, outputs, req, - DispatchMode::kFCompute, mxnet::OpStatePtr()); - Imperative::Get()->InvokeOp( - Context(), forward_attrs.attrs, inputs, ex_outputs, req, - DispatchMode::kFComputeEx, mxnet::OpStatePtr()); - Engine::Get()->WaitForAll(); - AssertEqual(outputs, ex_outputs); - - // backwards test performed same time since output needed - backwards_input[0] = outputs[0]; // output grad - backwards_input[1] = inputs[0]; // input - backwards_input[2] = outputs[1]; // out norm - - auto tmp_output = GetTestInputArrays(forward_attrs.input_types, true)[i1]; - backwards_outputs[0] = &tmp_output.arr; - - auto tmp_output2 = GetTestInputArrays(forward_attrs.input_types, true)[i1]; - backwards_ex_outputs[0] = &tmp_output2.arr; - - for (int i = 0; i < backwards_attrs.num_outputs; i++) - back_req[i] = kWriteTo; - - std::cout << "Backwards: "; - PrintVerifyMsg(out_arrs[0][output_i], tmp_output); - Imperative::Get()->InvokeOp( - Context(), backwards_attrs.attrs, backwards_input, backwards_outputs, - back_req, DispatchMode::kFCompute, mxnet::OpStatePtr()); - Imperative::Get()->InvokeOp( - Context(), backwards_attrs.attrs, backwards_input, backwards_ex_outputs, - back_req, DispatchMode::kFComputeEx, mxnet::OpStatePtr()); - Engine::Get()->WaitForAll(); - AssertEqual(backwards_outputs, backwards_ex_outputs); - } - } - } -} - -void TestPoolingOp(const OpAttrs &forward_attrs, const OpAttrs &backwards_attrs) { - std::vector inputs(forward_attrs.num_inputs); - std::vector outputs(forward_attrs.num_outputs); - std::vector ex_outputs(forward_attrs.num_outputs); - - std::vector backwards_input(backwards_attrs.num_inputs); - std::vector backwards_outputs(backwards_attrs.num_outputs); - std::vector backwards_ex_outputs(backwards_attrs.num_outputs); - - - std::vector req(forward_attrs.num_outputs); - std::vector back_req(backwards_attrs.num_outputs); - std::vector dispatches = forward_attrs.dispatches; - - TestArrayShapes tas = GetTestArrayShapes(); - std::vector pds = tas.pds; - - mxnet::op::PoolingParam param; - param.Init(forward_attrs.attrs.dict); - TShape kernel = param.kernel; - TShape padding = param.pad; - TShape stride = param.stride; - - std::vector in_arrs = GetTestInputArrays(); - std::vector> out_arrs(forward_attrs.num_outputs); - std::vector> ex_out_arrs(forward_attrs.num_outputs); - - for (int i1 = 0; i1 < in_arrs.size(); i1++) { - auto in_arr = in_arrs[i1]; - - // can only pool only 3D and 4D inputs - TShape input_shape = in_arr.arr.shape(); - if (input_shape.ndim() != kernel.ndim() + 2) - continue; - // cannot pool if ndarray and mkldnn memory have different ndim - if (in_arr.arr.IsView() || in_arr.arr.GetMKLDNNData()->get_primitive_desc().desc().data.ndims - != in_arr.arr.shape().ndim()) - continue; - std::vector scale_vector(in_arr.arr.shape().ndim()); - for (int i = 0; i < in_arr.arr.shape().ndim(); i++) { - if (i < 2) - scale_vector[i] = 1; - else - scale_vector[i] = CalculateWidthPoolOutput( - input_shape[i], kernel[i-2], padding[i-2], stride[i-2]) / - static_cast(input_shape[i]); - } - for (int i = 0; i < forward_attrs.num_outputs; i++) { - out_arrs[i] = GetTestOutputArrays(in_arr.arr.shape(), pds, scale_vector); - ex_out_arrs[i] = GetTestOutputArrays(in_arr.arr.shape(), pds, scale_vector); - } - - for (int i = 0; i < forward_attrs.num_inputs; i++) - inputs[i] = &in_arr.arr; - - for (size_t output_i = 0; output_i < out_arrs[0].size(); output_i++) { - for (int i = 0; i < forward_attrs.num_outputs; i++) { - req[i] = kWriteTo; - outputs[i] = &out_arrs[i][output_i].arr; - ex_outputs[i] = &ex_out_arrs[i][output_i].arr; - } - Imperative::Get()->set_is_training(true); - - PrintVerifyMsg(in_arr, out_arrs[0][output_i]); - Imperative::Get()->InvokeOp(Context(), forward_attrs.attrs, inputs, - outputs, req, DispatchMode::kFCompute, mxnet::OpStatePtr()); - Imperative::Get()->InvokeOp(Context(), forward_attrs.attrs, inputs, - ex_outputs, req, DispatchMode::kFComputeEx, mxnet::OpStatePtr()); - Engine::Get()->WaitForAll(); - VerifyCopyResult(outputs, ex_outputs); - - - // backwards test performed same time since output needed - if (backwards_attrs.num_inputs == 3) { - backwards_input[0] = outputs[0]; // output grad - backwards_input[1] = inputs[0]; // input - backwards_input[2] = outputs[0]; // output - } else if (backwards_attrs.num_inputs == 5) { - backwards_input[0] = outputs[0]; // output grad - backwards_input[1] = outputs[0]; // workspace grad - backwards_input[2] = inputs[0]; // input - backwards_input[3] = outputs[0]; // output - backwards_input[4] = ex_outputs[1]; // workspace - } - - // needs copies of inputs since they be reused in next iteration - // cannot use Copy method since we need to maintain MKLDNN format - auto tmp_output = GetTestInputArrays()[i1]; - auto tmp_output2 = GetTestInputArrays()[i1]; - backwards_outputs[0] = &tmp_output.arr; - backwards_ex_outputs[0] = &tmp_output2.arr; - back_req[0] = kWriteTo; - std::cout << "Backwards: "; - PrintVerifyMsg(out_arrs[0][output_i], tmp_output); - Imperative::Get()->InvokeOp( - Context(), backwards_attrs.attrs, backwards_input, backwards_outputs, - back_req, DispatchMode::kFCompute, mxnet::OpStatePtr()); - Imperative::Get()->InvokeOp( - Context(), backwards_attrs.attrs, backwards_input, backwards_ex_outputs, - back_req, DispatchMode::kFComputeEx, mxnet::OpStatePtr()); - Engine::Get()->WaitForAll(); - VerifyCopyResult(backwards_outputs, backwards_ex_outputs); - } - } -} - -TEST(IMPERATIVE, CopyOp) { - OpAttrs attrs = GetCopyOp(); - TestOp(attrs, VerifyCopyResult); -} - -TEST(IMPERATIVE, CopyBackwardsOp) { - OpAttrs attrs = GetCopyBackwardsOp(); - TestOp(attrs, VerifyCopyResult); -} - -TEST(IMPERATIVE, ActOp) { - OpAttrs attrs = GetReluOp(); - TestOp(attrs, VerifyActResult); -} - -TEST(IMPERATIVE, ActBackwardsOp) { - OpAttrs attrs = GetReluBackwardsOp(); - TestOp(attrs, VerifyActBackwardsResult); -} - -TEST(IMPERATIVE, SumOp) { - OpAttrs attrs = GetSumOp(); - TestOp(attrs, VerifySumResult); -} - -TEST(IMPERATIVE, SumBackwardsOp) { - OpAttrs attrs = GetSumBackwardsOp(); - TestOp(attrs, VerifySumBackwardsResult); -} - -TEST(IMPERATIVE, ConcatOp) { - for (int num_inputs = 2; num_inputs < 4; num_inputs++) { - for (int dim = 0; dim < 5; dim++) { - OpAttrs attrs = GetConcatOp(num_inputs, dim); - TestConcatOp(attrs, VerifyConcatResult); - } - } -} - -TEST(IMPERATIVE, ConcatBackwardsOp) { - for (int num_inputs = 2; num_inputs < 4; num_inputs++) { - for (int dim = 0; dim < 5; dim++) { - OpAttrs attrs = GetConcatBackwardsOp(num_inputs, dim); - TestConcatOp(attrs, VerifyConcatBackwardsResult, true); - } - } -} - -TEST(IMPERATIVE, LRNOp) { - OpAttrs forward_attrs = GetLRNOp(); - OpAttrs backwards_attrs = GetLRNBackwardsOp(); - TestOpEx(forward_attrs, backwards_attrs); -} - -TEST(IMPERATIVE, PoolingOp) { - for (int dim = 2; dim < 4; dim++) { - for (int kernel = 1; kernel < 4; kernel++) { - for (int stride = 1; stride < 3; stride++) { - for (int pad = 0; pad < 2; pad++) { - if (kernel / 2. < pad) - continue; - OpAttrs forward_attrs = GetPoolingOp(kernel, dim, stride, pad); - OpAttrs backwards_attrs = GetPoolingBackwardsOp(kernel, dim, stride, pad); - TestPoolingOp(forward_attrs, backwards_attrs); - } - } - } - } -} - TEST(MKLDNN_BASE, MKLDNNSum) { std::vector in_arrs = GetTestInputArrays(); std::vector in_arrs2 = GetTestInputArrays(ArrayTypes::All, true); @@ -1092,7 +291,7 @@ TEST(MKLDNN_BASE, CreateMKLDNNMem) { InitMKLDNNArray(&orig_arr.arr, input_mem->get_primitive_desc()); orig_arr.arr.CopyFrom(*input_mem); auto output_mem_t = CreateMKLDNNMem(in_arr.arr, - input_mem->get_primitive_desc(), kWriteInplace, &in_arr.arr); + input_mem->get_primitive_desc(), kWriteInplace, &in_arr.arr); op::MKLDNNSum(*input_mem, *input_mem2, *output_mem_t.second); CommitOutput(in_arr.arr, output_mem_t); stream->Submit(); @@ -1150,4 +349,50 @@ TEST(MKLDNN_BASE, CreateMKLDNNMem) { } } -#endif +TEST(MKLDNN_NDArray, GetTestInputArraysConcat) { + auto in_arrs = GetTestInputArrays(); + for (int dim = 0; dim < 5; dim++) { + for (int num_inputs = 2; num_inputs < 5; num_inputs++) { + std::vector expanded_arrs = GetTestInputArrays( + ArrayTypes::All, false, num_inputs, dim); + int i = 0; + for (auto &arr : in_arrs) { + if (dim >= arr.arr.shape().ndim()) + continue; + auto ex_arr = expanded_arrs[i]; + PrintVerifyMsg(arr, ex_arr); + EXPECT_EQ(arr.arr.shape().Size() * num_inputs, ex_arr.arr.shape().Size()); + EXPECT_EQ(arr.arr.shape()[dim] * num_inputs, ex_arr.arr.shape()[dim]); + i++; + } + } + } +} + +TEST(MKLDNN_NDArray, GetTestOutputArraysConcat) { + auto shapes_pds = GetTestArrayShapes(); + std::vector shapes; shapes = shapes_pds.shapes; + std::vector pds = shapes_pds.pds; + for (auto &shape : shapes) { + for (int dim = 0; dim < 5; dim++) { + for (int num_inputs = 2; num_inputs < 5; num_inputs++) { + if (shape.ndim() <= dim) + continue; + std::cout << "Extending " << shape << " dim " << + dim << " and " << num_inputs << "num_inputs\n"; + std::vector scale_vector(shape.ndim()); + for (int i = 0; i < shape.ndim(); i++) + scale_vector[i] = 1; + scale_vector[dim] = num_inputs; + auto output_arrs = GetTestOutputArrays(shape, pds, scale_vector); + for (auto &out_arr : output_arrs) { + auto out_shape = out_arr.arr.shape(); + EXPECT_EQ(shape.Size() * num_inputs, out_arr.arr.shape().Size()); + EXPECT_EQ(shape[dim] * num_inputs, out_arr.arr.shape()[dim]); + } + } + } + } +} + +#endif \ No newline at end of file diff --git a/tests/cpp/operator/mkldnn_utils.h b/tests/cpp/operator/mkldnn_utils.h index 0931270b5fe2..50cb97a8ffd6 100644 --- a/tests/cpp/operator/mkldnn_utils.h +++ b/tests/cpp/operator/mkldnn_utils.h @@ -25,73 +25,44 @@ #if MXNET_USE_MKLDNN == 1 +#include +#include +#include +#include +#include "gtest/gtest.h" #include "mxnet/imperative.h" #include "../../src/operator/nn/mkldnn/mkldnn_base-inl.h" +#include "../../src/operator/nn/mkldnn/mkldnn_ops-inl.h" +#include "../../src/operator/nn/mkldnn/mkldnn_pooling-inl.h" +#include "../../src/operator/nn/pooling-inl.h" + +using namespace mxnet; + +static mkldnn::memory::primitive_desc GetMemPD(const 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()); +} + +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"; + nnvm::TShape s(pd.desc().data.ndims); + for (size_t i = 0; i < pd.desc().data.ndims; i++) + s[i] = pd.desc().data.dims[i]; + s[dim] = static_cast(s[dim] * scale); + return GetMemPD(s, mshadow::DataType::kFlag, + static_cast(pd.desc().data.format)); +} struct TestArrayShapes { std::vector shapes; std::vector pds; }; -static TestArrayShapes GetTestArrayShapes() { - int dtype = mshadow::DataType::kFlag; - std::vector shapes; - std::vector pds; - { - // 1D - TShape s(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 - TShape s(2); - 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 - TShape s1(4); - 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)); - - TShape s2(4); - 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 formats = GetMKLDNNFormat(4, dtype); - pds.push_back(GetMemPD(s1, dtype, formats[0])); - pds.push_back(GetMemPD(s2, dtype, formats[1])); - } - { - // 5D - TShape s(5); - 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 formats = GetMKLDNNFormat(5, dtype); - pds.push_back(GetMemPD(s, dtype, formats[0])); - } - - TestArrayShapes ret; - ret.shapes = shapes; - ret.pds = pds; - return ret; -} - // Init arrays with the default layout. static void InitDefaultArray(NDArray *arr, bool is_rand = false) { const TBlob &blob = arr->data(); @@ -122,26 +93,6 @@ static bool IsSameShape(mkldnn::memory::primitive_desc pd, TShape shape) { return true; } -static mkldnn::memory::primitive_desc GetMemPD(const 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()); -} - -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"; - nnvm::TShape s(pd.desc().data.ndims); - for (size_t i = 0; i < pd.desc().data.ndims; i++) - s[i] = pd.desc().data.dims[i]; - s[dim] = static_cast(s[dim] * scale); - return GetMemPD(s, mshadow::DataType::kFlag, - static_cast(pd.desc().data.format)); -} - // 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 @@ -198,6 +149,65 @@ static std::vector GetMKLDNNFormat(size_t num_dims, int } } +static TestArrayShapes GetTestArrayShapes() { + int dtype = mshadow::DataType::kFlag; + std::vector shapes; + std::vector pds; + { + // 1D + TShape s(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 + TShape s(2); + 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 + TShape s1(4); + 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)); + + TShape s2(4); + 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 formats = GetMKLDNNFormat(4, dtype); + pds.push_back(GetMemPD(s1, dtype, formats[0])); + pds.push_back(GetMemPD(s2, dtype, formats[1])); + } + { + // 5D + TShape s(5); + 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 formats = GetMKLDNNFormat(5, dtype); + 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; @@ -521,4 +531,49 @@ int CalculateWidthPoolOutput(int width, int kernel, int padding, int stride) { return (width - kernel + 2 * padding) / stride + 1; } +using VerifyFunc = std::function &in_arrs, + const std::vector &out_arrs)>; + +void VerifyAddRequest(const std::vector &in_arrs, + const std::vector &original_outputs, + const std::vector &new_outputs, + VerifyFunc verify_fn) { + CHECK(original_outputs.size() == new_outputs.size()); + std::vector 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); +} + +void VerifyCopyResult(const std::vector &in_arrs, + const std::vector &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); +} + +void VerifySumResult(const std::vector &in_arrs, + const std::vector &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 *d2 = in2.data().dptr(); + mshadow::default_real_t *o = out.data().dptr(); + for (size_t i = 0; i < in1.shape().Size(); i++) + ASSERT_EQ(d1[i] + d2[i], o[i]); +} + + #endif \ No newline at end of file From 8fad7fd73ea760a209385efcefda08c7ce709573 Mon Sep 17 00:00:00 2001 From: Alexander Zai Date: Thu, 30 Aug 2018 07:34:36 -0700 Subject: [PATCH 03/13] update comments in header --- tests/cpp/operator/mkldnn_operator_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/cpp/operator/mkldnn_operator_test.cc b/tests/cpp/operator/mkldnn_operator_test.cc index 2dac49d5a39e..94a477a3ea67 100644 --- a/tests/cpp/operator/mkldnn_operator_test.cc +++ b/tests/cpp/operator/mkldnn_operator_test.cc @@ -19,7 +19,7 @@ /*! * \file mkldnn_test.cc - * \brief test functions in mkldnn. + * \brief test functions for mkldnn operators. * \author Alex Zai */ From 8daf66de3c7a7b58ebe9536b6b4f303d34c47880 Mon Sep 17 00:00:00 2001 From: Alexander Zai Date: Thu, 30 Aug 2018 07:45:29 -0700 Subject: [PATCH 04/13] move helpers into include dir --- .../mkldnn_utils.h => include/test_mkldnn.h} | 26 ++++++++++--------- tests/cpp/operator/mkldnn_operator_test.cc | 2 +- tests/cpp/operator/mkldnn_test.cc | 2 +- 3 files changed, 16 insertions(+), 14 deletions(-) rename tests/cpp/{operator/mkldnn_utils.h => include/test_mkldnn.h} (95%) diff --git a/tests/cpp/operator/mkldnn_utils.h b/tests/cpp/include/test_mkldnn.h similarity index 95% rename from tests/cpp/operator/mkldnn_utils.h rename to tests/cpp/include/test_mkldnn.h index 50cb97a8ffd6..219f5bb7d7aa 100644 --- a/tests/cpp/operator/mkldnn_utils.h +++ b/tests/cpp/include/test_mkldnn.h @@ -25,16 +25,19 @@ #if MXNET_USE_MKLDNN == 1 -#include -#include -#include -#include -#include "gtest/gtest.h" -#include "mxnet/imperative.h" -#include "../../src/operator/nn/mkldnn/mkldnn_base-inl.h" -#include "../../src/operator/nn/mkldnn/mkldnn_ops-inl.h" -#include "../../src/operator/nn/mkldnn/mkldnn_pooling-inl.h" -#include "../../src/operator/nn/pooling-inl.h" +#include "../../../3rdparty/mkldnn/include/mkldnn_types.h" +#include "../../../../../../Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/cmath" +#include "../../../../../../Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/climits" +#include "../../../../../../Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/set" +#include "../../../3rdparty/googletest/googletest/include/gtest/gtest.h" +#include "../../../include/mxnet/imperative.h" +#include "../../../src/operator/nn/mkldnn/mkldnn_base-inl.h" +#include "../../../src/operator/nn/mkldnn/mkldnn_ops-inl.h" +#include "../../../src/operator/nn/mkldnn/mkldnn_pooling-inl.h" +#include "../../../src/operator/nn/pooling-inl.h" + +#ifndef MXNET_STORAGE_H_ +#define MXNET_STORAGE_H_ using namespace mxnet; @@ -575,5 +578,4 @@ void VerifySumResult(const std::vector &in_arrs, ASSERT_EQ(d1[i] + d2[i], o[i]); } - -#endif \ No newline at end of file +#endif diff --git a/tests/cpp/operator/mkldnn_operator_test.cc b/tests/cpp/operator/mkldnn_operator_test.cc index 94a477a3ea67..ea023ac06468 100644 --- a/tests/cpp/operator/mkldnn_operator_test.cc +++ b/tests/cpp/operator/mkldnn_operator_test.cc @@ -35,7 +35,7 @@ #include "../../src/operator/nn/mkldnn/mkldnn_ops-inl.h" #include "../../src/operator/nn/mkldnn/mkldnn_pooling-inl.h" #include "../../src/operator/nn/pooling-inl.h" -#include "./mkldnn_utils.h" +#include "../include/test_mkldnn.h" using namespace mxnet; diff --git a/tests/cpp/operator/mkldnn_test.cc b/tests/cpp/operator/mkldnn_test.cc index 07ca262adc13..feb9cae4aa4a 100644 --- a/tests/cpp/operator/mkldnn_test.cc +++ b/tests/cpp/operator/mkldnn_test.cc @@ -35,7 +35,7 @@ #include "../../src/operator/nn/mkldnn/mkldnn_ops-inl.h" #include "../../src/operator/nn/mkldnn/mkldnn_pooling-inl.h" #include "../../src/operator/nn/pooling-inl.h" -#include "./mkldnn_utils.h" +#include "../include/test_mkldnn.h" using namespace mxnet; From 5ce1d4f89e2beeea3380ae145f52a28fb4600e3c Mon Sep 17 00:00:00 2001 From: Alexander Zai Date: Thu, 30 Aug 2018 07:47:15 -0700 Subject: [PATCH 05/13] fix lint --- tests/cpp/include/test_mkldnn.h | 6 +++--- tests/cpp/operator/mkldnn_test.cc | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/cpp/include/test_mkldnn.h b/tests/cpp/include/test_mkldnn.h index 219f5bb7d7aa..4d79c38f885b 100644 --- a/tests/cpp/include/test_mkldnn.h +++ b/tests/cpp/include/test_mkldnn.h @@ -23,6 +23,9 @@ * \author Alex Zai */ +#ifndef TESTS_MKLDNN_H_ +#define TESTS_MKLDNN_H_ + #if MXNET_USE_MKLDNN == 1 #include "../../../3rdparty/mkldnn/include/mkldnn_types.h" @@ -36,9 +39,6 @@ #include "../../../src/operator/nn/mkldnn/mkldnn_pooling-inl.h" #include "../../../src/operator/nn/pooling-inl.h" -#ifndef MXNET_STORAGE_H_ -#define MXNET_STORAGE_H_ - using namespace mxnet; static mkldnn::memory::primitive_desc GetMemPD(const TShape s, int dtype, diff --git a/tests/cpp/operator/mkldnn_test.cc b/tests/cpp/operator/mkldnn_test.cc index feb9cae4aa4a..8d586089912d 100644 --- a/tests/cpp/operator/mkldnn_test.cc +++ b/tests/cpp/operator/mkldnn_test.cc @@ -291,7 +291,7 @@ TEST(MKLDNN_BASE, CreateMKLDNNMem) { InitMKLDNNArray(&orig_arr.arr, input_mem->get_primitive_desc()); orig_arr.arr.CopyFrom(*input_mem); auto output_mem_t = CreateMKLDNNMem(in_arr.arr, - input_mem->get_primitive_desc(), kWriteInplace, &in_arr.arr); + input_mem->get_primitive_desc(), kWriteInplace, &in_arr.arr); op::MKLDNNSum(*input_mem, *input_mem2, *output_mem_t.second); CommitOutput(in_arr.arr, output_mem_t); stream->Submit(); @@ -395,4 +395,4 @@ TEST(MKLDNN_NDArray, GetTestOutputArraysConcat) { } } -#endif \ No newline at end of file +#endif From d8b49822a467150b1ba4f01189a426a306a0091e Mon Sep 17 00:00:00 2001 From: Alexander Zai Date: Thu, 30 Aug 2018 07:48:53 -0700 Subject: [PATCH 06/13] update comment --- tests/cpp/include/test_mkldnn.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/cpp/include/test_mkldnn.h b/tests/cpp/include/test_mkldnn.h index 4d79c38f885b..a7c16ec47e52 100644 --- a/tests/cpp/include/test_mkldnn.h +++ b/tests/cpp/include/test_mkldnn.h @@ -18,7 +18,7 @@ */ /*! - * \file mkldnn_utils.cc + * \file test_mkldnn.h * \brief helper functions to test mkldnn. * \author Alex Zai */ From 3f12dea4a9876ea1dc049caceb2ec3f191c21262 Mon Sep 17 00:00:00 2001 From: Alexander Zai Date: Thu, 30 Aug 2018 07:53:09 -0700 Subject: [PATCH 07/13] add stdlib headers --- tests/cpp/include/test_mkldnn.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/cpp/include/test_mkldnn.h b/tests/cpp/include/test_mkldnn.h index a7c16ec47e52..786762dd8ce1 100644 --- a/tests/cpp/include/test_mkldnn.h +++ b/tests/cpp/include/test_mkldnn.h @@ -23,15 +23,15 @@ * \author Alex Zai */ -#ifndef TESTS_MKLDNN_H_ -#define TESTS_MKLDNN_H_ +#ifndef TEST_MKLDNN_H_ +#define TEST_MKLDNN_H_ #if MXNET_USE_MKLDNN == 1 +#include +#include +#include #include "../../../3rdparty/mkldnn/include/mkldnn_types.h" -#include "../../../../../../Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/cmath" -#include "../../../../../../Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/climits" -#include "../../../../../../Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/set" #include "../../../3rdparty/googletest/googletest/include/gtest/gtest.h" #include "../../../include/mxnet/imperative.h" #include "../../../src/operator/nn/mkldnn/mkldnn_base-inl.h" @@ -578,4 +578,4 @@ void VerifySumResult(const std::vector &in_arrs, ASSERT_EQ(d1[i] + d2[i], o[i]); } -#endif +#endif // TEST_MKLDNN_H_ From 9fa8be09f3605209fc4f610901c8e73fb41aa15b Mon Sep 17 00:00:00 2001 From: Alexander Zai Date: Thu, 30 Aug 2018 07:54:09 -0700 Subject: [PATCH 08/13] remove unused headers --- tests/cpp/include/test_mkldnn.h | 4 ---- tests/cpp/operator/mkldnn_test.cc | 3 --- 2 files changed, 7 deletions(-) diff --git a/tests/cpp/include/test_mkldnn.h b/tests/cpp/include/test_mkldnn.h index 786762dd8ce1..a71c39f9248f 100644 --- a/tests/cpp/include/test_mkldnn.h +++ b/tests/cpp/include/test_mkldnn.h @@ -33,11 +33,7 @@ #include #include "../../../3rdparty/mkldnn/include/mkldnn_types.h" #include "../../../3rdparty/googletest/googletest/include/gtest/gtest.h" -#include "../../../include/mxnet/imperative.h" #include "../../../src/operator/nn/mkldnn/mkldnn_base-inl.h" -#include "../../../src/operator/nn/mkldnn/mkldnn_ops-inl.h" -#include "../../../src/operator/nn/mkldnn/mkldnn_pooling-inl.h" -#include "../../../src/operator/nn/pooling-inl.h" using namespace mxnet; diff --git a/tests/cpp/operator/mkldnn_test.cc b/tests/cpp/operator/mkldnn_test.cc index 8d586089912d..97758c91f1ac 100644 --- a/tests/cpp/operator/mkldnn_test.cc +++ b/tests/cpp/operator/mkldnn_test.cc @@ -32,9 +32,6 @@ #include "gtest/gtest.h" #include "mxnet/imperative.h" #include "../../src/operator/nn/mkldnn/mkldnn_base-inl.h" -#include "../../src/operator/nn/mkldnn/mkldnn_ops-inl.h" -#include "../../src/operator/nn/mkldnn/mkldnn_pooling-inl.h" -#include "../../src/operator/nn/pooling-inl.h" #include "../include/test_mkldnn.h" using namespace mxnet; From 57f94495252a6125629546a4e52ee132fc4afb11 Mon Sep 17 00:00:00 2001 From: Alexander Zai Date: Fri, 19 Oct 2018 17:34:52 -0700 Subject: [PATCH 09/13] add endif --- tests/cpp/include/test_mkldnn.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/cpp/include/test_mkldnn.h b/tests/cpp/include/test_mkldnn.h index a71c39f9248f..81edc5dab6ed 100644 --- a/tests/cpp/include/test_mkldnn.h +++ b/tests/cpp/include/test_mkldnn.h @@ -574,4 +574,5 @@ void VerifySumResult(const std::vector &in_arrs, ASSERT_EQ(d1[i] + d2[i], o[i]); } -#endif // TEST_MKLDNN_H_ +#endif +#endif // TEST_MKLDNN_H_ From 3c556ca4a076946167cc28e7e1f19645e774abf6 Mon Sep 17 00:00:00 2001 From: Alexander Zai Date: Fri, 19 Oct 2018 17:37:07 -0700 Subject: [PATCH 10/13] add missing header --- tests/cpp/operator/mkldnn_test.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/cpp/operator/mkldnn_test.cc b/tests/cpp/operator/mkldnn_test.cc index 97758c91f1ac..b1f1dbf256c0 100644 --- a/tests/cpp/operator/mkldnn_test.cc +++ b/tests/cpp/operator/mkldnn_test.cc @@ -31,6 +31,7 @@ #include #include "gtest/gtest.h" #include "mxnet/imperative.h" +#include "../../src/operator/nn/mkldnn/mkldnn_ops-inl.h" #include "../../src/operator/nn/mkldnn/mkldnn_base-inl.h" #include "../include/test_mkldnn.h" From 4a12d6a672505ab43bb69c4fb32a5c16db7581e3 Mon Sep 17 00:00:00 2001 From: Alexander Zai Date: Fri, 19 Oct 2018 17:47:56 -0700 Subject: [PATCH 11/13] add inlines --- tests/cpp/include/test_mkldnn.h | 36 ++++++++++++++++----------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/tests/cpp/include/test_mkldnn.h b/tests/cpp/include/test_mkldnn.h index 81edc5dab6ed..cedb5ca84ef5 100644 --- a/tests/cpp/include/test_mkldnn.h +++ b/tests/cpp/include/test_mkldnn.h @@ -37,7 +37,7 @@ using namespace mxnet; -static mkldnn::memory::primitive_desc GetMemPD(const TShape s, int dtype, +inline static mkldnn::memory::primitive_desc GetMemPD(const TShape s, int dtype, mkldnn::memory::format format) { mkldnn::memory::dims dims(s.ndim()); for (size_t i = 0; i < dims.size(); i++) @@ -46,7 +46,7 @@ static mkldnn::memory::primitive_desc GetMemPD(const TShape s, int dtype, return mkldnn::memory::primitive_desc(desc, CpuEngine::Get()->get_engine()); } -static mkldnn::memory::primitive_desc GetExpandedMemPD( +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"; nnvm::TShape s(pd.desc().data.ndims); @@ -63,7 +63,7 @@ struct TestArrayShapes { }; // Init arrays with the default layout. -static void InitDefaultArray(NDArray *arr, bool is_rand = false) { +inline static void InitDefaultArray(NDArray *arr, bool is_rand = false) { const TBlob &blob = arr->data(); mshadow::default_real_t *data = blob.dptr(); int size = blob.Size(); @@ -78,14 +78,14 @@ static void InitDefaultArray(NDArray *arr, bool is_rand = false) { // Init arrays with the specified layout. -static void InitMKLDNNArray(NDArray *arr, const mkldnn::memory::primitive_desc &pd, +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(); } -static bool IsSameShape(mkldnn::memory::primitive_desc pd, TShape shape) { +inline static bool IsSameShape(mkldnn::memory::primitive_desc pd, 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; @@ -97,7 +97,7 @@ static bool IsSameShape(mkldnn::memory::primitive_desc pd, TShape shape) { // 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. -static std::vector GetMKLDNNFormat(size_t num_dims, int dtype) { +inline static std::vector 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), @@ -148,7 +148,7 @@ static std::vector GetMKLDNNFormat(size_t num_dims, int } } -static TestArrayShapes GetTestArrayShapes() { +inline static TestArrayShapes GetTestArrayShapes() { int dtype = mshadow::DataType::kFlag; std::vector shapes; std::vector pds; @@ -240,7 +240,7 @@ enum ArrayTypes { All = 8191, }; -std::string CreateShapeString(int value, int dim) { +inline std::string CreateShapeString(int value, int dim) { std::stringstream ss; ss << "("; for (int i = 0; i < dim; i++) { @@ -251,7 +251,7 @@ std::string CreateShapeString(int value, int dim) { return ss.str(); } -void PrintVerifyMsg(const NDArrayAttrs &arr1, const NDArrayAttrs &arr2) { +inline void PrintVerifyMsg(const NDArrayAttrs &arr1, const NDArrayAttrs &arr2) { TShape t1 = arr1.arr.shape(); TShape t2 = arr2.arr.shape(); std::stringstream ss; @@ -281,7 +281,7 @@ void PrintVerifyMsg(const NDArrayAttrs &arr1, const NDArrayAttrs &arr2) { * * num_inputs / dim arguments used to scale shape (used for concat backwards to enlarge input shapes) */ -std::vector GetTestInputArrays( +inline std::vector GetTestInputArrays( int types = ArrayTypes::All, bool rand = false, int num_inputs = 1, int dim = 0) { TestArrayShapes tas = GetTestArrayShapes(); @@ -395,7 +395,7 @@ std::vector GetTestInputArrays( * * Optional num_inputs / dim args can be passed to modify input shape (used for Concat test) */ -std::vector GetTestOutputArrays( +inline std::vector GetTestOutputArrays( const TShape &shp, const std::vector &pds, std::vectorscale = {1}, bool rand = true, int types = ArrayTypes::All) { @@ -506,7 +506,7 @@ std::vector GetTestOutputArrays( * Determines axis ndarrays are concatenated by * Used to verify concat/concat backwards operator */ -int GetDim(TShape input_shape, TShape output_shape) { +inline int GetDim(TShape input_shape, 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]) @@ -519,21 +519,21 @@ int GetDim(TShape input_shape, TShape output_shape) { * Calculates the size of continuous block of array inside larger concatenated array * Used to verify concat/concat backwards operator */ -int GetBlockSize(TShape shape, int dim) { +inline int GetBlockSize(TShape shape, int dim) { int block_size = 1; for (int i = shape.ndim() - 1; i >= dim; i--) block_size *= shape[i]; return block_size; } -int CalculateWidthPoolOutput(int width, int kernel, int padding, int stride) { +inline int CalculateWidthPoolOutput(int width, int kernel, int padding, int stride) { return (width - kernel + 2 * padding) / stride + 1; } using VerifyFunc = std::function &in_arrs, const std::vector &out_arrs)>; -void VerifyAddRequest(const std::vector &in_arrs, +inline void VerifyAddRequest(const std::vector &in_arrs, const std::vector &original_outputs, const std::vector &new_outputs, VerifyFunc verify_fn) { @@ -548,7 +548,7 @@ void VerifyAddRequest(const std::vector &in_arrs, verify_fn(in_arrs, tmp_outputs); } -void VerifyCopyResult(const std::vector &in_arrs, +inline void VerifyCopyResult(const std::vector &in_arrs, const std::vector &out_arrs) { NDArray tmp1 = in_arrs[0]->Reorder2Default(); NDArray tmp2 = out_arrs[0]->Reorder2Default(); @@ -559,7 +559,7 @@ void VerifyCopyResult(const std::vector &in_arrs, tmp1.shape().Size() * sizeof(mshadow::default_real_t)), 0); } -void VerifySumResult(const std::vector &in_arrs, +inline void VerifySumResult(const std::vector &in_arrs, const std::vector &out_arrs) { NDArray in1 = in_arrs[0]->Reorder2Default(); NDArray in2 = in_arrs[1]->Reorder2Default(); @@ -574,5 +574,5 @@ void VerifySumResult(const std::vector &in_arrs, ASSERT_EQ(d1[i] + d2[i], o[i]); } -#endif +#endif // MXNET_USE_MKLDNN #endif // TEST_MKLDNN_H_ From 8d9d0ede839522e91b34bf9656ba1f679e847c6f Mon Sep 17 00:00:00 2001 From: Alexander Zai Date: Fri, 19 Oct 2018 17:53:58 -0700 Subject: [PATCH 12/13] fix lint --- tests/cpp/include/test_mkldnn.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/cpp/include/test_mkldnn.h b/tests/cpp/include/test_mkldnn.h index cedb5ca84ef5..ef13e4e74211 100644 --- a/tests/cpp/include/test_mkldnn.h +++ b/tests/cpp/include/test_mkldnn.h @@ -574,5 +574,5 @@ inline void VerifySumResult(const std::vector &in_arrs, ASSERT_EQ(d1[i] + d2[i], o[i]); } -#endif // MXNET_USE_MKLDNN -#endif // TEST_MKLDNN_H_ +#endif // MXNET_USE_MKLDNN +#endif // TEST_MKLDNN_H_ From ce4959c4d93eba1bf1065dc90b51653ff09f5af0 Mon Sep 17 00:00:00 2001 From: Alexander Zai Date: Wed, 24 Oct 2018 11:41:25 -0700 Subject: [PATCH 13/13] move copyfrom test to mkldnn_test --- tests/cpp/operator/mkldnn_operator_test.cc | 20 -------------------- tests/cpp/operator/mkldnn_test.cc | 20 ++++++++++++++++++++ 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/tests/cpp/operator/mkldnn_operator_test.cc b/tests/cpp/operator/mkldnn_operator_test.cc index ea023ac06468..d53f17448d41 100644 --- a/tests/cpp/operator/mkldnn_operator_test.cc +++ b/tests/cpp/operator/mkldnn_operator_test.cc @@ -320,26 +320,6 @@ void VerifyConcatBackwardsResult(const std::vector &in_arrs, } } -TEST(MKLDNN_NDArray, CopyFrom) { - TestArrayShapes tas = GetTestArrayShapes(); - std::vector pds = tas.pds; - - std::vector in_arrs = GetTestInputArrays(); - for (auto &in_arr : in_arrs) { - if (in_arr.arr.IsMKLDNNData() && in_arr.arr.IsView()) - continue; - std::vector out_arrs = GetTestOutputArrays(in_arr.arr.shape(), pds); - for (auto &out_arr : out_arrs) { - const mkldnn::memory *mem = in_arr.arr.GetMKLDNNData(); - out_arr.arr.CopyFrom(*mem); - MKLDNNStream::Get()->Submit(); - std::vector inputs(1); - inputs[0] = &in_arr.arr; - VerifyCopyResult(inputs, {&out_arr.arr}); - } - } -} - void TestOp(const OpAttrs &attrs, VerifyFunc verify_fn) { std::vector inputs(attrs.num_inputs); std::vector outputs(attrs.num_outputs); diff --git a/tests/cpp/operator/mkldnn_test.cc b/tests/cpp/operator/mkldnn_test.cc index b1f1dbf256c0..fbb7215b9862 100644 --- a/tests/cpp/operator/mkldnn_test.cc +++ b/tests/cpp/operator/mkldnn_test.cc @@ -393,4 +393,24 @@ TEST(MKLDNN_NDArray, GetTestOutputArraysConcat) { } } +TEST(MKLDNN_NDArray, CopyFrom) { + TestArrayShapes tas = GetTestArrayShapes(); + std::vector pds = tas.pds; + + std::vector in_arrs = GetTestInputArrays(); + for (auto &in_arr : in_arrs) { + if (in_arr.arr.IsMKLDNNData() && in_arr.arr.IsView()) + continue; + std::vector out_arrs = GetTestOutputArrays(in_arr.arr.shape(), pds); + for (auto &out_arr : out_arrs) { + const mkldnn::memory *mem = in_arr.arr.GetMKLDNNData(); + out_arr.arr.CopyFrom(*mem); + MKLDNNStream::Get()->Submit(); + std::vector inputs(1); + inputs[0] = &in_arr.arr; + VerifyCopyResult(inputs, {&out_arr.arr}); + } + } +} + #endif