You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mxnet.apache.org by sa...@apache.org on 2020/11/14 07:14:45 UTC
[incubator-mxnet] branch master updated: External Operators 2
(#19431)
This is an automated email from the ASF dual-hosted git repository.
samskalicky pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-mxnet.git
The following commit(s) were added to refs/heads/master by this push:
new 6bc0647 External Operators 2 (#19431)
6bc0647 is described below
commit 6bc064771d805163cd22123e9b3ec25bb55341d3
Author: Sam Skalicky <sa...@gmail.com>
AuthorDate: Fri Nov 13 23:13:06 2020 -0800
External Operators 2 (#19431)
* initial commit
* license fix
* changed path var, formatting
* add test to linux stages in ci
* disable test on osx stage in ci
* cleaned up example CMakeLists.txt removed -shared from GPU
* moved windows check
Co-authored-by: Ubuntu <ub...@ip-172-31-6-220.us-west-2.compute.internal>
Co-authored-by: Manu Seth <se...@amazon.com>
---
.github/workflows/os_x_staticbuild.yml | 6 +-
CMakeLists.txt | 9 +++
ci/docker/runtime_functions.sh | 10 ++++
ci/jenkins/Jenkins_steps.groovy | 10 ++--
example/extensions/lib_external_ops/CMakeLists.txt | 18 ++++++
example/extensions/lib_external_ops/README.md | 70 ++++++++++++++++++++++
example/extensions/lib_external_ops/init_lib.cc | 39 ++++++++++++
example/extensions/lib_external_ops/min_ex-inl.h | 66 ++++++++++++++++++++
example/extensions/lib_external_ops/min_ex.cc | 40 +++++++++++++
example/extensions/lib_external_ops/min_ex.cu | 35 +++++++++++
.../extensions/lib_external_ops/test_loading.py | 42 +++++++++++++
tests/python/gpu/test_extensions_gpu.py | 25 +++++++-
tests/python/unittest/test_extensions.py | 25 +++++++-
13 files changed, 383 insertions(+), 12 deletions(-)
diff --git a/.github/workflows/os_x_staticbuild.yml b/.github/workflows/os_x_staticbuild.yml
index a93e580..780e255 100644
--- a/.github/workflows/os_x_staticbuild.yml
+++ b/.github/workflows/os_x_staticbuild.yml
@@ -44,6 +44,6 @@ jobs:
- name: Test project
run: |
- python3 -m pytest -n 4 --durations=50 --verbose tests/python/unittest/ -k 'not test_operator and not (test_subgraph or test_custom_op or test_recordimage_dataset_with_data_loader_multiworker or test_multi_worker or test_multi_worker_shape or test_multi_worker_forked_data_loader or test_multi_worker_dataloader_release_pool)' -m 'not serial'
- MXNET_ENGINE_TYPE=NaiveEngine python3 -m pytest -n 4 --durations=50 --verbose tests/python/unittest/ -k 'test_operator and not (test_subgraph or test_custom_op or test_recordimage_dataset_with_data_loader_multiworker or test_multi_worker or test_multi_worker_shape or test_multi_worker_forked_data_loader or test_multi_worker_dataloader_release_pool)' -m 'not serial'
- python3 -m pytest --durations=50 --verbose tests/python/unittest/ -k 'not (test_subgraph or test_custom_op or test_recordimage_dataset_with_data_loader_multiworker or test_multi_worker or test_multi_worker_shape or test_multi_worker_forked_data_loader or test_multi_worker_dataloader_release_pool)' -m 'serial'
+ python3 -m pytest -n 4 --durations=50 --verbose tests/python/unittest/ -k 'not test_operator and not (test_subgraph or test_custom_op or test_external_op or test_recordimage_dataset_with_data_loader_multiworker or test_multi_worker or test_multi_worker_shape or test_multi_worker_forked_data_loader or test_multi_worker_dataloader_release_pool)' -m 'not serial'
+ MXNET_ENGINE_TYPE=NaiveEngine python3 -m pytest -n 4 --durations=50 --verbose tests/python/unittest/ -k 'test_operator and not (test_subgraph or test_custom_op or test_external_op or test_recordimage_dataset_with_data_loader_multiworker or test_multi_worker or test_multi_worker_shape or test_multi_worker_forked_data_loader or test_multi_worker_dataloader_release_pool)' -m 'not serial'
+ python3 -m pytest --durations=50 --verbose tests/python/unittest/ -k 'not (test_subgraph or test_custom_op or test_external_op or test_recordimage_dataset_with_data_loader_multiworker or test_multi_worker or test_multi_worker_shape or test_multi_worker_forked_data_loader or test_multi_worker_dataloader_release_pool)' -m 'serial'
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 07075d7..30839b4 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -89,6 +89,7 @@ option(USE_TENSORRT "Enable inference optimization with TensorRT." OFF)
option(USE_ASAN "Enable Clang/GCC ASAN sanitizers." OFF)
cmake_dependent_option(ENABLE_TESTCOVERAGE "Enable compilation with test coverage metric output" OFF "NOT MSVC" OFF)
option(USE_INT64_TENSOR_SIZE "Use int64_t to represent the total number of elements in a tensor" OFF)
+option(BUILD_EXTENSION_PATH "Path to extension to build" "")
option(BUILD_CYTHON_MODULES "Build cython modules." OFF)
option(LOG_FATAL_THROW "Log exceptions but do not abort" ON)
cmake_dependent_option(USE_SPLIT_ARCH_DLL "Build a separate DLL for each Cuda arch (Windows only)." ON "MSVC" OFF)
@@ -787,6 +788,14 @@ add_library(transposerowsp_lib SHARED ${CMAKE_CURRENT_SOURCE_DIR}/example/extens
add_library(subgraph_lib SHARED ${CMAKE_CURRENT_SOURCE_DIR}/example/extensions/lib_subgraph/subgraph_lib.cc ${CMAKE_CURRENT_SOURCE_DIR}/src/lib_api.cc)
add_library(pass_lib SHARED ${CMAKE_CURRENT_SOURCE_DIR}/example/extensions/lib_pass/pass_lib.cc ${CMAKE_CURRENT_SOURCE_DIR}/src/lib_api.cc)
+if(IS_DIRECTORY ${BUILD_EXTENSION_PATH})
+ if(MSVC)
+ message(FATAL_ERROR "Windows builds are not support for external ops")
+ else()
+ add_subdirectory(${BUILD_EXTENSION_PATH} ${BUILD_EXTENSION_PATH}/build)
+ endif()
+endif()
+
target_include_directories(customop_lib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include/mxnet)
target_include_directories(transposecsr_lib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include/mxnet)
target_include_directories(transposerowsp_lib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include/mxnet)
diff --git a/ci/docker/runtime_functions.sh b/ci/docker/runtime_functions.sh
index d436a3e..f5ee9ba 100755
--- a/ci/docker/runtime_functions.sh
+++ b/ci/docker/runtime_functions.sh
@@ -267,6 +267,7 @@ build_centos7_cpu() {
-DUSE_MKLDNN=OFF \
-DUSE_DIST_KVSTORE=ON \
-DUSE_CUDA=OFF \
+ -DBUILD_EXTENSION_PATH=/work/mxnet/example/extensions/lib_external_ops \
-G Ninja /work/mxnet
ninja
}
@@ -298,6 +299,7 @@ build_centos7_gpu() {
-DUSE_CUDA=ON \
-DMXNET_CUDA_ARCH="$CI_CMAKE_CUDA_ARCH" \
-DUSE_DIST_KVSTORE=ON\
+ -DBUILD_EXTENSION_PATH=/work/mxnet/example/extensions/lib_external_ops \
-G Ninja /work/mxnet
ninja
}
@@ -319,6 +321,7 @@ build_ubuntu_cpu_openblas() {
-DUSE_CUDA=OFF \
-DUSE_DIST_KVSTORE=ON \
-DBUILD_CYTHON_MODULES=ON \
+ -DBUILD_EXTENSION_PATH=/work/mxnet/example/extensions/lib_external_ops \
-G Ninja /work/mxnet
ninja
}
@@ -334,6 +337,7 @@ build_ubuntu_cpu_mkl() {
-DUSE_TVM_OP=ON \
-DUSE_MKL_IF_AVAILABLE=ON \
-DUSE_BLAS=MKL \
+ -DBUILD_EXTENSION_PATH=/work/mxnet/example/extensions/lib_external_ops \
-GNinja /work/mxnet
ninja
}
@@ -366,6 +370,7 @@ build_ubuntu_cpu_cmake_no_tvm_op() {
-DUSE_OPENCV=ON \
-DUSE_SIGNAL_HANDLER=ON \
-DCMAKE_BUILD_TYPE=Release \
+ -DBUILD_EXTENSION_PATH=/work/mxnet/example/extensions/lib_external_ops \
-G Ninja \
/work/mxnet
@@ -518,6 +523,7 @@ build_ubuntu_cpu_mkldnn() {
-DUSE_MKLDNN=ON \
-DUSE_CUDA=OFF \
-DUSE_CPP_PACKAGE=ON \
+ -DBUILD_EXTENSION_PATH=/work/mxnet/example/extensions/lib_external_ops \
-G Ninja /work/mxnet
ninja
}
@@ -533,6 +539,7 @@ build_ubuntu_cpu_mkldnn_mkl() {
-DUSE_TVM_OP=ON \
-DUSE_MKL_IF_AVAILABLE=ON \
-DUSE_BLAS=MKL \
+ -DBUILD_EXTENSION_PATH=/work/mxnet/example/extensions/lib_external_ops \
-GNinja /work/mxnet
ninja
}
@@ -604,6 +611,7 @@ build_ubuntu_gpu_mkldnn() {
-DUSE_CUDA=ON \
-DMXNET_CUDA_ARCH="$CI_CMAKE_CUDA_ARCH" \
-DUSE_CPP_PACKAGE=ON \
+ -DBUILD_EXTENSION_PATH=/work/mxnet/example/extensions/lib_external_ops \
-G Ninja /work/mxnet
ninja
}
@@ -618,6 +626,7 @@ build_ubuntu_gpu_mkldnn_nocudnn() {
-DMXNET_CUDA_ARCH="$CI_CMAKE_CUDA_ARCH" \
-DUSE_CUDNN=OFF \
-DUSE_CPP_PACKAGE=ON \
+ -DBUILD_EXTENSION_PATH=/work/mxnet/example/extensions/lib_external_ops \
-G Ninja /work/mxnet
ninja
}
@@ -635,6 +644,7 @@ build_ubuntu_gpu_cuda101_cudnn7() {
-DUSE_CPP_PACKAGE=ON \
-DUSE_DIST_KVSTORE=ON \
-DBUILD_CYTHON_MODULES=ON \
+ -DBUILD_EXTENSION_PATH=/work/mxnet/example/extensions/lib_external_ops \
-G Ninja /work/mxnet
ninja
}
diff --git a/ci/jenkins/Jenkins_steps.groovy b/ci/jenkins/Jenkins_steps.groovy
index d7623f9..fca8e44 100644
--- a/ci/jenkins/Jenkins_steps.groovy
+++ b/ci/jenkins/Jenkins_steps.groovy
@@ -23,18 +23,18 @@
utils = load('ci/Jenkinsfile_utils.groovy')
// mxnet libraries
-mx_lib = 'build/libmxnet.so, build/3rdparty/tvm/libtvm_runtime.so, build/libtvmop.so, build/tvmop.conf, build/libcustomop_lib.so, build/libcustomop_gpu_lib.so, build/libsubgraph_lib.so, build/3rdparty/openmp/runtime/src/libomp.so'
-mx_lib_cython = 'build/libmxnet.so, build/3rdparty/tvm/libtvm_runtime.so, build/libtvmop.so, build/tvmop.conf, build/libcustomop_lib.so, build/libcustomop_gpu_lib.so, build/libsubgraph_lib.so, python/mxnet/_cy3/*.so, build/3rdparty/openmp/runtime/src/libomp.so, python/mxnet/_ffi/_cy3/*.so'
+mx_lib = 'build/libmxnet.so, build/3rdparty/tvm/libtvm_runtime.so, build/libtvmop.so, build/tvmop.conf, build/libcustomop_lib.so, build/libcustomop_gpu_lib.so, build/libsubgraph_lib.so, example/extensions/lib_external_ops/build/libexternal_lib.so, build/3rdparty/openmp/runtime/src/libomp.so'
+mx_lib_cython = 'build/libmxnet.so, build/3rdparty/tvm/libtvm_runtime.so, build/libtvmop.so, build/tvmop.conf, build/libcustomop_lib.so, build/libcustomop_gpu_lib.so, build/libsubgraph_lib.so, example/extensions/lib_external_ops/build/libexternal_lib.so, python/mxnet/_cy3/*.so, build/3rdparty/openmp/runtime/src/libomp.so, python/mxnet/_ffi/_cy3/*.so'
// mxnet cmake libraries, in cmake builds we do not produce a libnvvm static library by default.
mx_cmake_lib = 'build/libmxnet.so, build/3rdparty/tvm/libtvm_runtime.so, build/libtvmop.so, build/tvmop.conf, build/tests/mxnet_unit_tests, build/3rdparty/openmp/runtime/src/libomp.so'
-mx_cmake_lib_no_tvm_op = 'build/libmxnet.so, build/libcustomop_lib.so, build/libcustomop_gpu_lib.so, build/libsubgraph_lib.so, build/tests/mxnet_unit_tests, build/3rdparty/openmp/runtime/src/libomp.so'
+mx_cmake_lib_no_tvm_op = 'build/libmxnet.so, build/libcustomop_lib.so, build/libcustomop_gpu_lib.so, build/libsubgraph_lib.so, example/extensions/lib_external_ops/build/libexternal_lib.so, build/tests/mxnet_unit_tests, build/3rdparty/openmp/runtime/src/libomp.so'
mx_cmake_lib_cython = 'build/libmxnet.so, build/3rdparty/tvm/libtvm_runtime.so, build/libtvmop.so, build/tvmop.conf, build/tests/mxnet_unit_tests, build/3rdparty/openmp/runtime/src/libomp.so, python/mxnet/_cy3/*.so, python/mxnet/_ffi/_cy3/*.so'
// mxnet cmake libraries, in cmake builds we do not produce a libnvvm static library by default.
mx_cmake_lib_debug = 'build/libmxnet.so, build/3rdparty/tvm/libtvm_runtime.so, build/libtvmop.so, build/tvmop.conf, build/libcustomop_lib.so, build/libcustomop_gpu_lib.so, build/libsubgraph_lib.so, build/tests/mxnet_unit_tests'
-mx_mkldnn_lib = 'build/libmxnet.so, build/3rdparty/tvm/libtvm_runtime.so, build/libtvmop.so, build/tvmop.conf, build/3rdparty/openmp/runtime/src/libomp.so, build/libcustomop_lib.so, build/libcustomop_gpu_lib.so, build/libsubgraph_lib.so'
+mx_mkldnn_lib = 'build/libmxnet.so, build/3rdparty/tvm/libtvm_runtime.so, build/libtvmop.so, build/tvmop.conf, build/3rdparty/openmp/runtime/src/libomp.so, build/libcustomop_lib.so, build/libcustomop_gpu_lib.so, build/libsubgraph_lib.so, example/extensions/lib_external_ops/build/libexternal_lib.so'
mx_tensorrt_lib = 'build/libmxnet.so, build/3rdparty/tvm/libtvm_runtime.so, build/libtvmop.so, build/tvmop.conf, build/3rdparty/openmp/runtime/src/libomp.so, lib/libnvonnxparser_runtime.so.0, lib/libnvonnxparser.so.0, lib/libonnx_proto.so, lib/libonnx.so'
-mx_lib_cpp_examples = 'build/libmxnet.so, build/3rdparty/tvm/libtvm_runtime.so, build/libtvmop.so, build/tvmop.conf, build/3rdparty/openmp/runtime/src/libomp.so, build/libcustomop_lib.so, build/libcustomop_gpu_lib.so, build/libsubgraph_lib.so, python/mxnet/_cy3/*.so, python/mxnet/_ffi/_cy3/*.so'
+mx_lib_cpp_examples = 'build/libmxnet.so, build/3rdparty/tvm/libtvm_runtime.so, build/libtvmop.so, build/tvmop.conf, build/3rdparty/openmp/runtime/src/libomp.so, build/libcustomop_lib.so, build/libcustomop_gpu_lib.so, build/libsubgraph_lib.so, example/extensions/lib_external_ops/build/libexternal_lib.so, python/mxnet/_cy3/*.so, python/mxnet/_ffi/_cy3/*.so'
mx_lib_cpp_examples_no_tvm_op = 'build/libmxnet.so, build/libcustomop_lib.so, build/libcustomop_gpu_lib.so, build/libsubgraph_lib.so, build/3rdparty/openmp/runtime/src/libomp.so, python/mxnet/_cy3/*.so, python/mxnet/_ffi/_cy3/*.so'
mx_lib_cpp_examples_cpu = 'build/libmxnet.so, build/3rdparty/tvm/libtvm_runtime.so, build/libtvmop.so, build/tvmop.conf, build/3rdparty/openmp/runtime/src/libomp.so'
mx_cd_lib = 'lib/libmxnet.so, licenses/*, lib/libgfortran.so.*, lib/libopenblas.so.0, include/mkldnn/dnnl_version.h, include/mkldnn/dnnl_config.h'
diff --git a/example/extensions/lib_external_ops/CMakeLists.txt b/example/extensions/lib_external_ops/CMakeLists.txt
new file mode 100644
index 0000000..383e529
--- /dev/null
+++ b/example/extensions/lib_external_ops/CMakeLists.txt
@@ -0,0 +1,18 @@
+# specify CXX sources
+FILE(GLOB CXX_SRCS
+ # Required files
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/lib_api.cc
+ # Your custom files
+ ${CMAKE_CURRENT_SOURCE_DIR}/init_lib.cc
+ ${CMAKE_CURRENT_SOURCE_DIR}/min_ex.cc
+ )
+
+# create library & set libraries
+add_library(external_lib SHARED ${CXX_SRCS})
+target_link_libraries(external_lib PUBLIC mxnet)
+
+if(USE_CUDA)
+ # specify GPU sources (optional)
+ FILE(GLOB CU_SRCS "*.cu")
+ target_sources(external_lib PUBLIC ${CU_SRCS})
+endif(USE_CUDA)
diff --git a/example/extensions/lib_external_ops/README.md b/example/extensions/lib_external_ops/README.md
new file mode 100644
index 0000000..40291e1
--- /dev/null
+++ b/example/extensions/lib_external_ops/README.md
@@ -0,0 +1,70 @@
+<!--- 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. -->
+
+External Operators Example and Tutorial
+=======================================
+
+## Introduction
+
+Extending MXNet with custom components used to mean distributing a custom fork. This feature allows adding custom components to MXNet by dynamically loading external libraries at runtime. Currently it is only supported on Linux systems (Windows and Mac are __NOT__ supported).
+
+## Getting Started
+
+### Have MXNet Ready
+
+For this tutorial, clone MXNet from source like:
+```
+git clone https://github.com/apache/incubator-mxnet.git --recursive --init
+```
+
+Build MXNet like:
+```
+cp config/linux.cmake config.cmake
+mkdir build
+cd build
+cmake ..
+cmake --build .
+```
+
+## Run An Example
+
+This example shows compiling a custom backend operator and then dynamically loading it into MXNet at runtime. Go to the **lib_external_ops** directory and follow these steps:
+
+1. Touch or modify the **min_ex.cc** and/or **min_ex-inl.h** file(s)
+2. Go into the **build** directory that was created when building MXNet.
+3. Run `cmake .. -DBUILD_EXTENSION_PATH=$(pwd)/../example/extensions/lib_external_ops`
+4. Run `cmake --build .`
+5. Go to the **example/extensions/lib_external_ops** directory again
+6. Run `python test_loading.py` to execute the test program. You should see the following output:
+```
+Operator not registered yet
+MXNet version 20000 supported
+[]
+Operator executed successfully
+```
+
+## Writing an External Operator Library
+To build your own library containing custom components, compose a C++ source file like `mycomp_lib.cc`, include the `lib_api.h` header file, compile the `lib_api.cc` file, and implement the following required function:
+- `initialize` - Library Initialization Function
+
+Then create a CMakeLists.txt file and set `mxnet` as a link library like:
+```
+add_library(external_lib SHARED ${SRCS})
+target_link_libraries(external_lib PUBLIC mxnet)
+```
+
+Next, build MXNet and set the path to your directory with the CMakeLists.txt file via the `BUILD_EXTENSION_PATH` option. This will build your library with all of the MXNet includes.
diff --git a/example/extensions/lib_external_ops/init_lib.cc b/example/extensions/lib_external_ops/init_lib.cc
new file mode 100644
index 0000000..efc5eb7
--- /dev/null
+++ b/example/extensions/lib_external_ops/init_lib.cc
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+/*!
+ * Copyright (c) 2020 by Contributors
+ * \file init_lib.cc
+ * \brief initialize function implementation library file
+ */
+
+#include <iostream>
+#include "mxnet/lib_api.h"
+
+using namespace mxnet::ext;
+
+MXReturnValue initialize(int version) {
+ if (version >= 10700) {
+ std::cout << "MXNet version " << version << " supported" << std::endl;
+ return MX_SUCCESS;
+ } else {
+ MX_ERROR_MSG << "MXNet version " << version << " not supported";
+ return MX_FAIL;
+ }
+}
diff --git a/example/extensions/lib_external_ops/min_ex-inl.h b/example/extensions/lib_external_ops/min_ex-inl.h
new file mode 100644
index 0000000..79ce5d4
--- /dev/null
+++ b/example/extensions/lib_external_ops/min_ex-inl.h
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+/*!
+ * Copyright (c) 2020 by Contributors
+ * \file min_ex-inl.h
+ * \brief example external operator header file
+ */
+
+#ifndef MXNET_OPERATOR_TENSOR_MIN_EX_OP_INL_H_
+#define MXNET_OPERATOR_TENSOR_MIN_EX_OP_INL_H_
+
+#include <dmlc/parameter.h>
+#include <vector>
+#include <algorithm>
+#include "operator/mxnet_op.h"
+#include "operator/operator_common.h"
+#include "operator/elemwise_op_common.h"
+
+namespace mxnet {
+namespace op {
+
+template<typename xpu>
+void MinExForward(const nnvm::NodeAttrs& attrs,
+ const OpContext& ctx,
+ const std::vector<TBlob>& inputs,
+ const std::vector<OpReqType>& req,
+ const std::vector<TBlob>& outputs) {
+ //do nothing
+}
+
+
+inline bool MinExOpShape(const nnvm::NodeAttrs& attrs,
+ mxnet::ShapeVector* in_attrs,
+ mxnet::ShapeVector* out_attrs) {
+ //do nothing
+ return true;
+}
+
+inline bool MinExOpType(const nnvm::NodeAttrs& attrs,
+ std::vector<int> *in_attrs,
+ std::vector<int> *out_attrs) {
+ //do nothing
+ return true;
+}
+
+} // namespace op
+} // namespace mxnet
+
+#endif // MXNET_OPERATOR_TENSOR_MIN_EX_OP_INL_H_
diff --git a/example/extensions/lib_external_ops/min_ex.cc b/example/extensions/lib_external_ops/min_ex.cc
new file mode 100644
index 0000000..cb9f6dd
--- /dev/null
+++ b/example/extensions/lib_external_ops/min_ex.cc
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+/*!
+ * Copyright (c) 2020 by Contributors
+ * \file min_ex.cc
+ * \brief example external operator source file
+ */
+
+#include "min_ex-inl.h"
+
+namespace mxnet {
+namespace op {
+
+NNVM_REGISTER_OP(min_ex)
+.describe("some description")
+.set_num_inputs(0)
+.set_num_outputs(0)
+.set_attr<mxnet::FInferShape>("FInferShape", MinExOpShape)
+.set_attr<nnvm::FInferType>("FInferType", MinExOpType)
+.set_attr<FCompute>("FCompute<cpu>", MinExForward<cpu>);
+
+} // namespace op
+} // namespace mxnet
diff --git a/example/extensions/lib_external_ops/min_ex.cu b/example/extensions/lib_external_ops/min_ex.cu
new file mode 100644
index 0000000..6257ea7
--- /dev/null
+++ b/example/extensions/lib_external_ops/min_ex.cu
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+/*!
+ * Copyright (c) 2020 by Contributors
+ * \file min_ex.cu
+ * \brief example external operator CUDA source file
+ */
+
+#include "./min_ex-inl.h"
+
+namespace mxnet {
+namespace op {
+
+NNVM_REGISTER_OP(min_ex)
+.set_attr<FCompute>("FCompute<gpu>", MinExForward<gpu>);
+
+} // namespace op
+} // namespace mxnet
diff --git a/example/extensions/lib_external_ops/test_loading.py b/example/extensions/lib_external_ops/test_loading.py
new file mode 100644
index 0000000..1b4357c
--- /dev/null
+++ b/example/extensions/lib_external_ops/test_loading.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python3
+
+# 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.
+
+# coding: utf-8
+# pylint: disable=arguments-differ
+
+# This test checks if dynamic loading of library into MXNet is successful
+# and checks the computation of an external operator
+
+import mxnet as mx
+import os
+
+# check if operator exists
+if hasattr(mx.nd, 'min_ex'):
+ raise Exception('Operator already loaded')
+else:
+ print('Operator not registered yet')
+
+# test loading library
+if (os.name == 'posix'):
+ path = os.path.abspath('build/libexternal_lib.so')
+ mx.library.load(path, False)
+
+# execute operator
+print(mx.nd.min_ex())
+print('Operator executed successfully')
diff --git a/tests/python/gpu/test_extensions_gpu.py b/tests/python/gpu/test_extensions_gpu.py
index 1cc06cd..9d36831 100644
--- a/tests/python/gpu/test_extensions_gpu.py
+++ b/tests/python/gpu/test_extensions_gpu.py
@@ -28,8 +28,8 @@ from mxnet.test_utils import download, is_cd_run, assert_almost_equal, default_c
import pytest
base_path = os.path.join(os.path.dirname(__file__), "../../..")
-def check_platform():
- return platform.machine() not in ['x86_64', 'AMD64']
+def check_platform(supported_platforms=['x86_64', 'AMD64']):
+ return platform.machine() not in supported_platforms
@pytest.mark.skipif(check_platform(), reason="not all machine types supported")
@pytest.mark.skipif(is_cd_run(), reason="continuous delivery run - ignoring test")
@@ -89,3 +89,24 @@ def test_custom_op_gpu():
mx.random.seed(128, ctx=mx.gpu())
r4 = mx.nd.my_noisy_relu(d2)
assert_almost_equal(r3.asnumpy(), r4.asnumpy(), rtol=1e-3, atol=1e-3)
+
+@pytest.mark.skipif(check_platform(['x86_64']), reason="not all machine types supported")
+@pytest.mark.skipif(is_cd_run(), reason="continuous delivery run - ignoring test")
+def test_external_op():
+ # check if operator already exists
+ if hasattr(mx.nd, 'min_ex'):
+ raise MXNetError('Operator already loaded')
+
+ lib = 'libexternal_lib.so'
+ fname = os.path.join(base_path,'example/extensions/lib_external_ops/build/'+lib)
+ if not os.path.exists(fname):
+ raise MXNetError("library %s not found " % lib)
+
+ fname = os.path.abspath(fname)
+ mx.library.load(fname, False)
+
+ # execute operator
+ try:
+ mx.nd.min_ex()
+ except:
+ raise MXNetError('Operator not loaded successfully')
diff --git a/tests/python/unittest/test_extensions.py b/tests/python/unittest/test_extensions.py
index 8d94680..9785901 100644
--- a/tests/python/unittest/test_extensions.py
+++ b/tests/python/unittest/test_extensions.py
@@ -28,8 +28,8 @@ from mxnet.test_utils import download, is_cd_run, assert_almost_equal, default_c
import pytest
base_path = os.path.join(os.path.dirname(__file__), "../../..")
-def check_platform():
- return platform.machine() not in ['x86_64', 'AMD64']
+def check_platform(supported_platforms=['x86_64', 'AMD64']):
+ return platform.machine() not in supported_platforms
@pytest.mark.skipif(check_platform(), reason="not all machine types supported")
@pytest.mark.skipif(is_cd_run(), reason="continuous delivery run - ignoring test")
@@ -186,3 +186,24 @@ def test_subgraph():
out5 = sym_block4(a_data, b_data)
# check that result matches one executed by MXNet
assert_almost_equal(out[0].asnumpy(), out5[0].asnumpy(), rtol=1e-3, atol=1e-3)
+
+@pytest.mark.skipif(check_platform(['x86_64']), reason="not all machine types supported")
+@pytest.mark.skipif(is_cd_run(), reason="continuous delivery run - ignoring test")
+def test_external_op():
+ # check if operator already exists
+ if hasattr(mx.nd, 'min_ex'):
+ raise MXNetError('Operator already loaded')
+
+ lib = 'libexternal_lib.so'
+ fname = os.path.join(base_path,'example/extensions/lib_external_ops/build/'+lib)
+ if not os.path.exists(fname):
+ raise MXNetError("library %s not found " % lib)
+
+ fname = os.path.abspath(fname)
+ mx.library.load(fname, False)
+
+ # execute operator
+ try:
+ mx.nd.min_ex()
+ except:
+ raise MXNetError('Operator not loaded successfully')