From cf30d305b6b970e8cba61e7ba95c5bc18751330a Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Tue, 10 Dec 2019 08:17:13 +0000 Subject: [PATCH 01/50] initial commit --- example/extensions/lib_subgraph/Makefile | 24 ++++ .../extensions/lib_subgraph/subgraph_lib.cc | 124 ++++++++++++++++++ .../extensions/lib_subgraph/test_subgraph.py | 60 +++++++++ include/mxnet/lib_api.h | 31 +++++ 4 files changed, 239 insertions(+) create mode 100644 example/extensions/lib_subgraph/Makefile create mode 100644 example/extensions/lib_subgraph/subgraph_lib.cc create mode 100644 example/extensions/lib_subgraph/test_subgraph.py diff --git a/example/extensions/lib_subgraph/Makefile b/example/extensions/lib_subgraph/Makefile new file mode 100644 index 000000000000..c45100b69ef7 --- /dev/null +++ b/example/extensions/lib_subgraph/Makefile @@ -0,0 +1,24 @@ +# 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. + +all: subgraph_lib + +subgraph_lib: + g++ -shared -fPIC -std=c++11 subgraph_lib.cc -o libsubgraph_lib.so -I ../../../include/mxnet + +clean: + rm -rf libsubgraph_lib.so diff --git a/example/extensions/lib_subgraph/subgraph_lib.cc b/example/extensions/lib_subgraph/subgraph_lib.cc new file mode 100644 index 000000000000..43a703480ae8 --- /dev/null +++ b/example/extensions/lib_subgraph/subgraph_lib.cc @@ -0,0 +1,124 @@ +/* + * 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) 2019 by Contributors + * \file subgraph_lib.cc + * \brief subgraph operator implementation library file + */ + +#include +#include "lib_api.h" + +MXReturnValue parseAttrs(std::map attrs, + int* num_in, int* num_out) { + *num_in = 1; + *num_out = 1; + if (attrs.count(SUBGRAPH_SYM_JSON)) { + // example of subgraph json parsing + JsonParser jp; + JsonVal val = jp.parse_to_json(attrs[SUBGRAPH_SYM_JSON]); + int input = 0; + for (auto &item : val.map[JsonVal("nodes")].list) { + if (item.map[JsonVal("op")].str == "null") + input++; + } + int output = val.map[JsonVal("heads")].list.size(); + *num_in = input; + *num_out = output; + } + return MX_SUCCESS; +} + +MXReturnValue inferType(std::map attrs, + std::vector &intypes, + std::vector &outtypes) { + outtypes[0] = intypes[0]; + return MX_SUCCESS; +} + +MXReturnValue inferShape(std::map attrs, + std::vector> &inshapes, + std::vector> &outshapes) { + outshapes[0] = inshapes[0]; + return MX_SUCCESS; +} + +class MyStatefulOp : public CustomStatefulOp { + public: + explicit MyStatefulOp(std::string sym) : subgraph_sym(sym) {} + + MXReturnValue Forward(std::vector inputs, + std::vector outputs, + OpResource op_res) { + std::cout << "Info: subgraph symbol is: " << std::endl; + std::cout << subgraph_sym << std::endl; + float* in_data = inputs[0].data(); + float* out_data = outputs[0].data(); + std::cout << "Info: output is: " << std::endl; + for (int i = 0; i < inputs[0].size(); i++) { + out_data[i] = in_data[i]; + } + return MX_SUCCESS; + } + + private: + std::string subgraph_sym; +}; + +MXReturnValue createOpState(std::map attrs, + CustomStatefulOp** op_inst) { + std::string serialized_subgraph = "[empty]"; + // MXNet subgraph is stored as Symbol in operator node attrs subgraphs field + // custom subgraph is stored as json string in custom operator attrs map entry + if (attrs.count(SUBGRAPH_SYM_JSON)) { + // user can now parse json and run other custom ops inside subgraph + serialized_subgraph = attrs[SUBGRAPH_SYM_JSON]; + } + *op_inst = new MyStatefulOp(serialized_subgraph); + std::cout << "Info: stateful operator created" << std::endl; + return MX_SUCCESS; +} + +REGISTER_OP(_custom_subgraph_op) +.setParseAttrs(parseAttrs) +.setInferType(inferType) +.setInferShape(inferShape) +.setCreateOpState(createOpState); + +class mySubgraphProperty : public CustomSubgraphProperty { + +}; + +CustomSubgraphProperty* CreateMySubgraphProperty() { + CustomSubgraphProperty *inst = new mySubgraphProperty(); + return inst; +} + +REGISTER_SUBGRAPH_PROPERTY(myProp,CreateMySubgraphProperty); + +MXReturnValue initialize(int version) { + if (version >= 10400) { + std::cout << "MXNet version " << version << " supported" << std::endl; + return MX_SUCCESS; + } else { + std::cout << "MXNet version " << version << " not supported" << std::endl; + return MX_FAIL; + } +} diff --git a/example/extensions/lib_subgraph/test_subgraph.py b/example/extensions/lib_subgraph/test_subgraph.py new file mode 100644 index 000000000000..2625b13f6794 --- /dev/null +++ b/example/extensions/lib_subgraph/test_subgraph.py @@ -0,0 +1,60 @@ +#!/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 end of end computation of custom operator + +import mxnet as mx +import os, ctypes +from mxnet.base import _LIB, check_call, mx_uint, c_str, c_str_array, SymbolHandle + +# load library +if (os.name=='posix'): + path = os.path.abspath('libsubgraph_lib.so') + mx.library.load(path) +elif (os.name=='nt'): + path = os.path.abspath('libsubgraph_lib.dll') + mx.library.load(path) + +a = mx.sym.var('a') +b = mx.sym.var('b') +c = a + b +d = mx.sym.exp(c) +ret = mx.sym.log(d) + +op_names = ['exp','log'] +out = SymbolHandle() + +check_call(_LIB.MXBuildSubgraphByOpNames(ret.handle, + c_str('default'), + mx_uint(len(op_names)), + c_str_array(op_names), + ctypes.byref(out))) +partitioned_sym = mx.sym.Symbol(out) +json_sym = partitioned_sym.tojson() + +mystr = json_sym.replace("_CachedOp","_custom_subgraph_op") +mysym = mx.sym.load_json(mystr) + +exe = mysym.bind(ctx=mx.cpu(), args={'a':mx.nd.ones((3,2)), 'b':mx.nd.ones((3,2))}) +out = exe.forward() +print(out) diff --git a/include/mxnet/lib_api.h b/include/mxnet/lib_api.h index 290a63518373..60163aaa4b4c 100644 --- a/include/mxnet/lib_api.h +++ b/include/mxnet/lib_api.h @@ -584,6 +584,25 @@ class CustomOp { createOpState_t create_opstate; }; +/*! + * \brief An abstract class for subgraph property + */ +class CustomSubgraphProperty { + typedef CustomSubgraphProperty* (*SubgraphPropertyCreateFn)(void); + + public: + CustomSubgraphProperty() : name("ERROR") {} + CustomSubgraphProperty(const char* prop_name) : name(prop_name) {} + CustomSubgraphProperty& setCreateFn(SubgraphPropertyCreateFn fn) { + createFn = fn; + return *this; + } + virtual CustomSubgraphProperty* Create(); + private: + const char* name; + SubgraphPropertyCreateFn createFn; +}; + /*! * \brief Registry class to registers things (ops, properties) * Singleton class @@ -640,10 +659,22 @@ class Registry { #define MX_REGISTER_NAME_(Name) MXNet ## _CustomOp ## _ #define MX_REGISTER_DEF_(Name) CustomOp MX_REGISTER_NAME_(Name) +#define MX_REGISTER_PROP_NAME_(Name) MXNet ## _CustomSubProp ## _ +#define MX_REGISTER_PROP_DEF_(Name) CustomSubgraphProperty MX_REGISTER_PROP_NAME_(Name) + /*! \brief assign a var to a value */ #define REGISTER_OP(Name) MX_STR_CONCAT(MX_REGISTER_DEF_(Name), __COUNTER__) = \ Registry::get()->add(MX_TOSTRING(Name)) +#define REGISTER_SUBGRAPH_PROPERTY(Name,SubgraphProperty) \ + MX_STR_CONCAT(MX_REGISTER_PROP_DEF_(Name), __COUNTER__) = \ + Registry::get()->add(MX_TOSTRING(Name)).setCreateFn(SubgraphProperty); + +//Registry::get()->add(MX_TOSTRING(Name)) + +//Registry::get()->add(MX_TOSTRING(Name)) +//, &SubgraphProp) + /* -------------- BELOW ARE CTYPE FUNCTIONS PROTOTYPES --------------- */ /*! From d17a118a2ea5ab4051c17284943ab5945d70b8dc Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Wed, 11 Dec 2019 07:41:15 +0000 Subject: [PATCH 02/50] added subgraphCreate API function to partition the model/graph added partitioner registration to c_api --- .../extensions/lib_subgraph/subgraph_lib.cc | 10 +++- include/mxnet/lib_api.h | 55 ++++++++++++++----- src/c_api/c_api.cc | 28 ++++++++++ 3 files changed, 79 insertions(+), 14 deletions(-) diff --git a/example/extensions/lib_subgraph/subgraph_lib.cc b/example/extensions/lib_subgraph/subgraph_lib.cc index 43a703480ae8..5d206dd2dc2f 100644 --- a/example/extensions/lib_subgraph/subgraph_lib.cc +++ b/example/extensions/lib_subgraph/subgraph_lib.cc @@ -111,7 +111,15 @@ CustomSubgraphProperty* CreateMySubgraphProperty() { return inst; } -REGISTER_SUBGRAPH_PROPERTY(myProp,CreateMySubgraphProperty); +MXReturnValue mySupportedOps(const char *json, + const char *data_names[], + const MXTensor *data, + const int num_data, + int *ids) { + return MX_SUCCESS; +} + +REGISTER_PARTITIONER(myProp,mySupportedOps); MXReturnValue initialize(int version) { if (version >= 10400) { diff --git a/include/mxnet/lib_api.h b/include/mxnet/lib_api.h index 60163aaa4b4c..f8d9cd75c636 100644 --- a/include/mxnet/lib_api.h +++ b/include/mxnet/lib_api.h @@ -584,23 +584,29 @@ class CustomOp { createOpState_t create_opstate; }; +/*! \brief Custom Subgraph Create function template */ +typedef MXReturnValue (*SubgraphCreateFn_t)(const char*, + const char*[], + const MXTensor*, + const int, + int*); + /*! * \brief An abstract class for subgraph property */ class CustomSubgraphProperty { - typedef CustomSubgraphProperty* (*SubgraphPropertyCreateFn)(void); - public: CustomSubgraphProperty() : name("ERROR") {} CustomSubgraphProperty(const char* prop_name) : name(prop_name) {} - CustomSubgraphProperty& setCreateFn(SubgraphPropertyCreateFn fn) { + CustomSubgraphProperty& setSubgraphFn(SubgraphCreateFn_t fn) { createFn = fn; return *this; } - virtual CustomSubgraphProperty* Create(); - private: + + /*! \brief subgraph property name */ const char* name; - SubgraphPropertyCreateFn createFn; + /*! \brief subgraph create function */ + SubgraphCreateFn_t createFn; }; /*! @@ -666,14 +672,9 @@ class Registry { #define REGISTER_OP(Name) MX_STR_CONCAT(MX_REGISTER_DEF_(Name), __COUNTER__) = \ Registry::get()->add(MX_TOSTRING(Name)) -#define REGISTER_SUBGRAPH_PROPERTY(Name,SubgraphProperty) \ +#define REGISTER_PARTITIONER(Name,SubgraphFn) \ MX_STR_CONCAT(MX_REGISTER_PROP_DEF_(Name), __COUNTER__) = \ - Registry::get()->add(MX_TOSTRING(Name)).setCreateFn(SubgraphProperty); - -//Registry::get()->add(MX_TOSTRING(Name)) - -//Registry::get()->add(MX_TOSTRING(Name)) -//, &SubgraphProp) + Registry::get()->add(MX_TOSTRING(Name)).setSubgraphFn(SubgraphFn); /* -------------- BELOW ARE CTYPE FUNCTIONS PROTOTYPES --------------- */ @@ -732,6 +733,12 @@ typedef int (*opCallFStatefulComp_t)(bool, void*, const int64_t**, int*, void**, const int64_t**, int*, void**, int*, int, xpu_malloc_t, void*); +#define MXLIB_PARTREGSIZE_STR "_partRegSize" +typedef int (*partRegSize_t)(void); + +#define MXLIB_PARTREGGET_STR "_partRegGet" +typedef int (*partRegGet_t)(int, const char**, SubgraphCreateFn_t*); + #define MXLIB_INITIALIZE_STR "initialize" typedef int (*initialize_t)(int); @@ -1030,6 +1037,28 @@ extern "C" { return op_ptr->Backward(inputs, outputs, res); } + /*! \brief returns number of partitioners registered in this library */ +#if defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__) + __declspec(dllexport) int __cdecl +#else + int +#endif + _partRegSize() { + return Registry::get()->size(); + } + + /*! \brief returns operator registration at specified index */ +#if defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__) + __declspec(dllexport) void __cdecl +#else + void +#endif + _partRegGet(int idx, const char** name, SubgraphCreateFn_t* createFn) { + CustomSubgraphProperty prop = Registry::get()->get(idx); + *name = prop.name; + *createFn = prop.createFn; + } + /*! * \brief Checks if the MXNet version is supported by the library. * If supported, initializes the library. diff --git a/src/c_api/c_api.cc b/src/c_api/c_api.cc index 24374cf19cdc..731b5f1abe53 100644 --- a/src/c_api/c_api.cc +++ b/src/c_api/c_api.cc @@ -682,6 +682,34 @@ int MXLoadLib(const char *path) { } regOp.add_argument("data", "NDArray[]", "Source inputs"); } + + // get number of operators registered in the library + partRegSize_t partRegSize = get_func(lib, const_cast(MXLIB_PARTREGSIZE_STR)); + int numParts = partRegSize(); + LOG(INFO) << "Found " << numParts << " partitioners in library"; + + /* + * Get all custom operators implementation from custom library + * loop and register each operator in the library to NNVM + */ + partRegGet_t partRegGet = get_func(lib, const_cast(MXLIB_PARTREGGET_STR)); + for (int i = 0; i < numParts; i++) { + const char* name; + // function pointers holding implementation from custom library + SubgraphCreateFn_t subgraphCreateFn_fp = nullptr; + + // get custom operator implemenation from the dynamic library + partRegGet(i, &name, &subgraphCreateFn_fp); + + // validate custom operator functions from the dynamic library + CHECK(subgraphCreateFn_fp != nullptr) << "Error loading '" << name + << "' custom partitioner, subgraphCreateFn function was not set."; + + LOG(INFO) << "\tPartitioner[" << i << "] " << name; + std::string name_str(name); + + } + API_END(); } From d0f2fb3eb19def151ca546a3548ce97434ee0b4a Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Thu, 12 Dec 2019 07:25:57 +0000 Subject: [PATCH 03/50] cleaned up API, added API to specify subgraphOp --- .../extensions/lib_subgraph/subgraph_lib.cc | 13 +---- include/mxnet/lib_api.h | 57 +++++++++++-------- src/c_api/c_api.cc | 31 +++++----- 3 files changed, 52 insertions(+), 49 deletions(-) diff --git a/example/extensions/lib_subgraph/subgraph_lib.cc b/example/extensions/lib_subgraph/subgraph_lib.cc index 5d206dd2dc2f..f5dbc2fb2632 100644 --- a/example/extensions/lib_subgraph/subgraph_lib.cc +++ b/example/extensions/lib_subgraph/subgraph_lib.cc @@ -102,15 +102,6 @@ REGISTER_OP(_custom_subgraph_op) .setInferShape(inferShape) .setCreateOpState(createOpState); -class mySubgraphProperty : public CustomSubgraphProperty { - -}; - -CustomSubgraphProperty* CreateMySubgraphProperty() { - CustomSubgraphProperty *inst = new mySubgraphProperty(); - return inst; -} - MXReturnValue mySupportedOps(const char *json, const char *data_names[], const MXTensor *data, @@ -119,7 +110,9 @@ MXReturnValue mySupportedOps(const char *json, return MX_SUCCESS; } -REGISTER_PARTITIONER(myProp,mySupportedOps); +REGISTER_PARTITIONER(myProp) +.setSupportedOps(mySupportedOps) +.setSubgraphOp("_custom_subgraph_op"); MXReturnValue initialize(int version) { if (version >= 10400) { diff --git a/include/mxnet/lib_api.h b/include/mxnet/lib_api.h index f8d9cd75c636..6a54a26aeebb 100644 --- a/include/mxnet/lib_api.h +++ b/include/mxnet/lib_api.h @@ -530,7 +530,7 @@ typedef MXReturnValue (*inferShape_t)(std::map, std::vector >&, std::vector >&); typedef MXReturnValue (*mutateInputs_t)(std::map, - std::vector&); + std::vector&); typedef MXReturnValue (*createOpState_t)(std::map, CustomStatefulOp**); @@ -585,28 +585,34 @@ class CustomOp { }; /*! \brief Custom Subgraph Create function template */ -typedef MXReturnValue (*SubgraphCreateFn_t)(const char*, - const char*[], - const MXTensor*, - const int, - int*); +typedef MXReturnValue (*supportedOps_t)(const char*, + const char*[], + const MXTensor*, + const int, + int*); /*! * \brief An abstract class for subgraph property */ -class CustomSubgraphProperty { +class CustomPartitioner { public: - CustomSubgraphProperty() : name("ERROR") {} - CustomSubgraphProperty(const char* prop_name) : name(prop_name) {} - CustomSubgraphProperty& setSubgraphFn(SubgraphCreateFn_t fn) { - createFn = fn; + CustomPartitioner() : name("ERROR") {} + explicit CustomPartitioner(const char* prop_name) : name(prop_name) {} + CustomPartitioner& setSupportedOps(supportedOps_t fn) { + supportedOps = fn; return *this; } - - /*! \brief subgraph property name */ + CustomPartitioner& setSubgraphOp(const char* sg_name) { + op_name = sg_name; + return *this; + } + + /*! \brief partitioner name */ const char* name; - /*! \brief subgraph create function */ - SubgraphCreateFn_t createFn; + /*! \brief supported ops function */ + supportedOps_t supportedOps; + /*! \brief subgraph operator name */ + const char* op_name; }; /*! @@ -666,15 +672,15 @@ class Registry { #define MX_REGISTER_DEF_(Name) CustomOp MX_REGISTER_NAME_(Name) #define MX_REGISTER_PROP_NAME_(Name) MXNet ## _CustomSubProp ## _ -#define MX_REGISTER_PROP_DEF_(Name) CustomSubgraphProperty MX_REGISTER_PROP_NAME_(Name) +#define MX_REGISTER_PROP_DEF_(Name) CustomPartitioner MX_REGISTER_PROP_NAME_(Name) /*! \brief assign a var to a value */ #define REGISTER_OP(Name) MX_STR_CONCAT(MX_REGISTER_DEF_(Name), __COUNTER__) = \ Registry::get()->add(MX_TOSTRING(Name)) -#define REGISTER_PARTITIONER(Name,SubgraphFn) \ +#define REGISTER_PARTITIONER(Name) \ MX_STR_CONCAT(MX_REGISTER_PROP_DEF_(Name), __COUNTER__) = \ - Registry::get()->add(MX_TOSTRING(Name)).setSubgraphFn(SubgraphFn); + Registry::get()->add(MX_TOSTRING(Name)) /* -------------- BELOW ARE CTYPE FUNCTIONS PROTOTYPES --------------- */ @@ -737,7 +743,7 @@ typedef int (*opCallFStatefulComp_t)(bool, void*, const int64_t**, int*, void**, typedef int (*partRegSize_t)(void); #define MXLIB_PARTREGGET_STR "_partRegGet" -typedef int (*partRegGet_t)(int, const char**, SubgraphCreateFn_t*); +typedef int (*partRegGet_t)(int, const char**, supportedOps_t*, const char**); #define MXLIB_INITIALIZE_STR "initialize" typedef int (*initialize_t)(int); @@ -1044,19 +1050,20 @@ extern "C" { int #endif _partRegSize() { - return Registry::get()->size(); + return Registry::get()->size(); } - /*! \brief returns operator registration at specified index */ + /*! \brief returns partitioner registration at specified index */ #if defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__) __declspec(dllexport) void __cdecl #else void #endif - _partRegGet(int idx, const char** name, SubgraphCreateFn_t* createFn) { - CustomSubgraphProperty prop = Registry::get()->get(idx); - *name = prop.name; - *createFn = prop.createFn; + _partRegGet(int idx, const char** name, supportedOps_t* fn, const char** op_name) { + CustomPartitioner part = Registry::get()->get(idx); + *name = part.name; + *fn = part.supportedOps; + *op_name = part.op_name; } /*! diff --git a/src/c_api/c_api.cc b/src/c_api/c_api.cc index 731b5f1abe53..f267a97f5a21 100644 --- a/src/c_api/c_api.cc +++ b/src/c_api/c_api.cc @@ -683,31 +683,34 @@ int MXLoadLib(const char *path) { regOp.add_argument("data", "NDArray[]", "Source inputs"); } - // get number of operators registered in the library + // get number of partitioners registered in the library partRegSize_t partRegSize = get_func(lib, const_cast(MXLIB_PARTREGSIZE_STR)); int numParts = partRegSize(); LOG(INFO) << "Found " << numParts << " partitioners in library"; /* - * Get all custom operators implementation from custom library - * loop and register each operator in the library to NNVM + * Get all custom partitioners implementation from custom library + * loop and register each partitioner in the library to NNVM */ partRegGet_t partRegGet = get_func(lib, const_cast(MXLIB_PARTREGGET_STR)); for (int i = 0; i < numParts; i++) { const char* name; // function pointers holding implementation from custom library - SubgraphCreateFn_t subgraphCreateFn_fp = nullptr; - - // get custom operator implemenation from the dynamic library - partRegGet(i, &name, &subgraphCreateFn_fp); - - // validate custom operator functions from the dynamic library - CHECK(subgraphCreateFn_fp != nullptr) << "Error loading '" << name - << "' custom partitioner, subgraphCreateFn function was not set."; - - LOG(INFO) << "\tPartitioner[" << i << "] " << name; + supportedOps_t supportedOps_fp = nullptr; + // name of subgraph op + const char* op_name = nullptr; + + // get custom partitioner implemenation from the dynamic library + partRegGet(i, &name, &supportedOps_fp, &op_name); + + // validate custom partitioner functions from the dynamic library + CHECK(supportedOps_fp != nullptr) << "Error loading '" << name + << "' custom partitioner, supportedOps function was not set."; + CHECK(op_name != nullptr) << "Error loading '" << name + << "' custom partitioner, subgraphOp was not set."; + + LOG(INFO) << "\tPartitioner[" << i << "] " << name << " subgraphOp: '" << op_name << "'"; std::string name_str(name); - } API_END(); From 55e88619f7e5f2295059d316c272d2545f87e1c0 Mon Sep 17 00:00:00 2001 From: samskalicky Date: Thu, 12 Dec 2019 07:37:02 +0000 Subject: [PATCH 04/50] fixed whitespace --- include/mxnet/lib_api.h | 4 ++-- src/c_api/c_api.cc | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/include/mxnet/lib_api.h b/include/mxnet/lib_api.h index 6a54a26aeebb..eacc94efcd2a 100644 --- a/include/mxnet/lib_api.h +++ b/include/mxnet/lib_api.h @@ -530,7 +530,7 @@ typedef MXReturnValue (*inferShape_t)(std::map, std::vector >&, std::vector >&); typedef MXReturnValue (*mutateInputs_t)(std::map, - std::vector&); + std::vector&); typedef MXReturnValue (*createOpState_t)(std::map, CustomStatefulOp**); @@ -606,7 +606,7 @@ class CustomPartitioner { op_name = sg_name; return *this; } - + /*! \brief partitioner name */ const char* name; /*! \brief supported ops function */ diff --git a/src/c_api/c_api.cc b/src/c_api/c_api.cc index f267a97f5a21..c50d2dd3478f 100644 --- a/src/c_api/c_api.cc +++ b/src/c_api/c_api.cc @@ -684,7 +684,8 @@ int MXLoadLib(const char *path) { } // get number of partitioners registered in the library - partRegSize_t partRegSize = get_func(lib, const_cast(MXLIB_PARTREGSIZE_STR)); + partRegSize_t partRegSize = get_func(lib, + const_cast(MXLIB_PARTREGSIZE_STR)); int numParts = partRegSize(); LOG(INFO) << "Found " << numParts << " partitioners in library"; From 8c97c4516ce4dc117f8b855a41e65da220916c47 Mon Sep 17 00:00:00 2001 From: samskalicky Date: Thu, 12 Dec 2019 07:50:20 +0000 Subject: [PATCH 05/50] added custom subgraph property --- .../partitioner/custom_subgraph_property.h | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 src/operator/subgraph/partitioner/custom_subgraph_property.h diff --git a/src/operator/subgraph/partitioner/custom_subgraph_property.h b/src/operator/subgraph/partitioner/custom_subgraph_property.h new file mode 100644 index 000000000000..bcb3bd2a15d3 --- /dev/null +++ b/src/operator/subgraph/partitioner/custom_subgraph_property.h @@ -0,0 +1,77 @@ +/* + * 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. + */ + +#ifndef MXNET_OPERATOR_SUBGRAPH_CUSTOM_SUBGRAPH_PROPERTY_H_ +#define MXNET_OPERATOR_SUBGRAPH_CUSTOM_SUBGRAPH_PROPERTY_H_ + +namespace mxnet { + namespace op { + + /* + * This selects nodes for a subgraph + */ + class CustomContainOpSelector: public SubgraphSelector { + public: + explicit CustomContainOpSelector() {} + + virtual bool Select(const nnvm::Node &n) { + return false; + } + + virtual bool SelectInput(const nnvm::Node &n, const nnvm::Node &new_node) { + return false; + } + + virtual bool SelectOutput(const nnvm::Node &n, const nnvm::Node &new_node) { + return false; + } + }; + + /* + * This subgraph property finds a subgraph + */ + class CustomSubgraphProperty: public SubgraphProperty { + public: + // create custom subgraph property + static SubgraphPropertyPtr Create() { + return std::make_shared(); + } + + // override CreateSubgraphNode + virtual nnvm::NodePtr CreateSubgraphNode(const nnvm::Symbol &sym, + const int subgraph_id = 0) const { + nnvm::NodePtr n = nnvm::Node::Create(); + n->attrs.op = Op::Get("_op"); + n->attrs.name = "_op" + std::to_string(subgraph_id); + n->attrs.subgraphs.push_back(std::make_shared(sym)); + return n; + } + + // override CreateSubgraphSelector + virtual SubgraphSelectorPtr CreateSubgraphSelector() const { + return std::make_shared(); + } + }; + + MXNET_REGISTER_SUBGRAPH_PROPERTY(customProp, CustomSubgraphProperty); + + } // namespace op +} // namespace mxnet + +#endif From 0ebf5b6301c3b9d6b34abc7b68529d555aeabe02 Mon Sep 17 00:00:00 2001 From: samskalicky Date: Thu, 12 Dec 2019 07:53:47 +0000 Subject: [PATCH 06/50] fixed whitespace --- src/c_api/c_api.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/c_api/c_api.cc b/src/c_api/c_api.cc index c50d2dd3478f..b8cd70a1ded8 100644 --- a/src/c_api/c_api.cc +++ b/src/c_api/c_api.cc @@ -700,7 +700,7 @@ int MXLoadLib(const char *path) { supportedOps_t supportedOps_fp = nullptr; // name of subgraph op const char* op_name = nullptr; - + // get custom partitioner implemenation from the dynamic library partRegGet(i, &name, &supportedOps_fp, &op_name); @@ -713,7 +713,6 @@ int MXLoadLib(const char *path) { LOG(INFO) << "\tPartitioner[" << i << "] " << name << " subgraphOp: '" << op_name << "'"; std::string name_str(name); } - API_END(); } From 84f8426998f114539f1cd8b471d4386a11c8a494 Mon Sep 17 00:00:00 2001 From: samskalicky Date: Thu, 12 Dec 2019 08:21:33 +0000 Subject: [PATCH 07/50] fixed subgraph property --- .../extensions/lib_subgraph/test_subgraph.py | 21 +++---------------- src/c_api/c_api.cc | 1 + .../partitioner/custom_subgraph_property.h | 7 ++++++- 3 files changed, 10 insertions(+), 19 deletions(-) diff --git a/example/extensions/lib_subgraph/test_subgraph.py b/example/extensions/lib_subgraph/test_subgraph.py index 2625b13f6794..ef317b8b3589 100644 --- a/example/extensions/lib_subgraph/test_subgraph.py +++ b/example/extensions/lib_subgraph/test_subgraph.py @@ -39,22 +39,7 @@ b = mx.sym.var('b') c = a + b d = mx.sym.exp(c) -ret = mx.sym.log(d) +sym = mx.sym.log(d) -op_names = ['exp','log'] -out = SymbolHandle() - -check_call(_LIB.MXBuildSubgraphByOpNames(ret.handle, - c_str('default'), - mx_uint(len(op_names)), - c_str_array(op_names), - ctypes.byref(out))) -partitioned_sym = mx.sym.Symbol(out) -json_sym = partitioned_sym.tojson() - -mystr = json_sym.replace("_CachedOp","_custom_subgraph_op") -mysym = mx.sym.load_json(mystr) - -exe = mysym.bind(ctx=mx.cpu(), args={'a':mx.nd.ones((3,2)), 'b':mx.nd.ones((3,2))}) -out = exe.forward() -print(out) +part_sym = sym.optimize_for("customBackend") + diff --git a/src/c_api/c_api.cc b/src/c_api/c_api.cc index b8cd70a1ded8..8904a5d2dc2a 100644 --- a/src/c_api/c_api.cc +++ b/src/c_api/c_api.cc @@ -53,6 +53,7 @@ #include "../operator/operator_common.h" #include "../operator/tensor/matrix_op-inl.h" #include "../operator/tvmop/op_module.h" +#include "../operator/subgraph/partitioner/custom_subgraph_property.h" #include "../common/utils.h" #include "nnvm/pass_functions.h" diff --git a/src/operator/subgraph/partitioner/custom_subgraph_property.h b/src/operator/subgraph/partitioner/custom_subgraph_property.h index bcb3bd2a15d3..99b850c75328 100644 --- a/src/operator/subgraph/partitioner/custom_subgraph_property.h +++ b/src/operator/subgraph/partitioner/custom_subgraph_property.h @@ -20,6 +20,10 @@ #ifndef MXNET_OPERATOR_SUBGRAPH_CUSTOM_SUBGRAPH_PROPERTY_H_ #define MXNET_OPERATOR_SUBGRAPH_CUSTOM_SUBGRAPH_PROPERTY_H_ +#include +#include "../common.h" +#include "../subgraph_property.h" + namespace mxnet { namespace op { @@ -69,7 +73,8 @@ namespace mxnet { } }; - MXNET_REGISTER_SUBGRAPH_PROPERTY(customProp, CustomSubgraphProperty); + MXNET_REGISTER_SUBGRAPH_BACKEND(customBackend); + MXNET_REGISTER_SUBGRAPH_PROPERTY(customBackend, CustomSubgraphProperty); } // namespace op } // namespace mxnet From 2a1c6feaa8d01674e93b4f70a8c529c1a5ba7465 Mon Sep 17 00:00:00 2001 From: samskalicky Date: Thu, 12 Dec 2019 17:04:12 +0000 Subject: [PATCH 08/50] fixed whitespace --- .../partitioner/custom_subgraph_property.h | 97 +++++++++---------- 1 file changed, 46 insertions(+), 51 deletions(-) diff --git a/src/operator/subgraph/partitioner/custom_subgraph_property.h b/src/operator/subgraph/partitioner/custom_subgraph_property.h index 99b850c75328..654a11db76ca 100644 --- a/src/operator/subgraph/partitioner/custom_subgraph_property.h +++ b/src/operator/subgraph/partitioner/custom_subgraph_property.h @@ -17,66 +17,61 @@ * under the License. */ -#ifndef MXNET_OPERATOR_SUBGRAPH_CUSTOM_SUBGRAPH_PROPERTY_H_ -#define MXNET_OPERATOR_SUBGRAPH_CUSTOM_SUBGRAPH_PROPERTY_H_ +#ifndef MXNET_OPERATOR_SUBGRAPH_PARTITIONER_CUSTOM_SUBGRAPH_PROPERTY_H_ +#define MXNET_OPERATOR_SUBGRAPH_PARTITIONER_CUSTOM_SUBGRAPH_PROPERTY_H_ #include #include "../common.h" #include "../subgraph_property.h" namespace mxnet { - namespace op { +namespace op { - /* - * This selects nodes for a subgraph - */ - class CustomContainOpSelector: public SubgraphSelector { - public: - explicit CustomContainOpSelector() {} - - virtual bool Select(const nnvm::Node &n) { - return false; - } - - virtual bool SelectInput(const nnvm::Node &n, const nnvm::Node &new_node) { - return false; - } - - virtual bool SelectOutput(const nnvm::Node &n, const nnvm::Node &new_node) { - return false; - } - }; - - /* - * This subgraph property finds a subgraph - */ - class CustomSubgraphProperty: public SubgraphProperty { - public: - // create custom subgraph property - static SubgraphPropertyPtr Create() { - return std::make_shared(); - } - - // override CreateSubgraphNode - virtual nnvm::NodePtr CreateSubgraphNode(const nnvm::Symbol &sym, - const int subgraph_id = 0) const { - nnvm::NodePtr n = nnvm::Node::Create(); - n->attrs.op = Op::Get("_op"); - n->attrs.name = "_op" + std::to_string(subgraph_id); - n->attrs.subgraphs.push_back(std::make_shared(sym)); - return n; - } +/* + * This selects nodes for a subgraph + */ +class CustomContainOpSelector: public SubgraphSelector { + public: + explicit CustomContainOpSelector() {} + virtual bool Select(const nnvm::Node &n) { + return false; + } + virtual bool SelectInput(const nnvm::Node &n, const nnvm::Node &new_node) { + return false; + } + virtual bool SelectOutput(const nnvm::Node &n, const nnvm::Node &new_node) { + return false; + } +}; - // override CreateSubgraphSelector - virtual SubgraphSelectorPtr CreateSubgraphSelector() const { - return std::make_shared(); - } - }; +/* + * This subgraph property finds a subgraph + */ +class CustomSubgraphProperty: public SubgraphProperty { + public: + // create custom subgraph property + static SubgraphPropertyPtr Create() { + return std::make_shared(); + } + // override CreateSubgraphNode + virtual nnvm::NodePtr CreateSubgraphNode(const nnvm::Symbol &sym, + const int subgraph_id = 0) const { + nnvm::NodePtr n = nnvm::Node::Create(); + n->attrs.op = Op::Get("_op"); + n->attrs.name = "_op" + std::to_string(subgraph_id); + n->attrs.subgraphs.push_back(std::make_shared(sym)); + return n; + } + // override CreateSubgraphSelector + virtual SubgraphSelectorPtr CreateSubgraphSelector() const { + return std::make_shared(); + } +}; - MXNET_REGISTER_SUBGRAPH_BACKEND(customBackend); - MXNET_REGISTER_SUBGRAPH_PROPERTY(customBackend, CustomSubgraphProperty); +MXNET_REGISTER_SUBGRAPH_BACKEND(customBackend); +MXNET_REGISTER_SUBGRAPH_PROPERTY(customBackend, CustomSubgraphProperty); - } // namespace op +} // namespace op } // namespace mxnet -#endif +#endif // MXNET_OPERATOR_SUBGRAPH_PARTITIONER_CUSTOM_SUBGRAPH_PROPERTY_H_ From d3c277db3700f7588a6f212c13b6dc04b7e1e371 Mon Sep 17 00:00:00 2001 From: samskalicky Date: Thu, 12 Dec 2019 19:34:00 +0000 Subject: [PATCH 09/50] added registration of custom subgraph property in c_api --- example/extensions/lib_subgraph/test_subgraph.py | 2 +- src/c_api/c_api.cc | 10 ++++++++++ .../subgraph/partitioner/custom_subgraph_property.h | 10 +++------- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/example/extensions/lib_subgraph/test_subgraph.py b/example/extensions/lib_subgraph/test_subgraph.py index ef317b8b3589..1af0a6ba584d 100644 --- a/example/extensions/lib_subgraph/test_subgraph.py +++ b/example/extensions/lib_subgraph/test_subgraph.py @@ -41,5 +41,5 @@ d = mx.sym.exp(c) sym = mx.sym.log(d) -part_sym = sym.optimize_for("customBackend") +part_sym = sym.optimize_for("myProp") diff --git a/src/c_api/c_api.cc b/src/c_api/c_api.cc index 8904a5d2dc2a..0ee57ff674d3 100644 --- a/src/c_api/c_api.cc +++ b/src/c_api/c_api.cc @@ -54,6 +54,7 @@ #include "../operator/tensor/matrix_op-inl.h" #include "../operator/tvmop/op_module.h" #include "../operator/subgraph/partitioner/custom_subgraph_property.h" +#include "../operator/subgraph/subgraph_property.h" #include "../common/utils.h" #include "nnvm/pass_functions.h" @@ -713,6 +714,15 @@ int MXLoadLib(const char *path) { LOG(INFO) << "\tPartitioner[" << i << "] " << name << " subgraphOp: '" << op_name << "'"; std::string name_str(name); + + auto createProp = [=]() { + return static_cast >(std::make_shared()); + }; + + //MXNET_REGISTER_SUBGRAPH_BACKEND(customBackend); + mxnet::op::SubgraphBackendRegistry::Get()->__REGISTER_BACKEND__(name); + //MXNET_REGISTER_SUBGRAPH_PROPERTY(customBackend, CustomSubgraphProperty); + mxnet::op::SubgraphBackendRegistry::Get()->__REGISTER_PROPERTY__(name, createProp); } API_END(); } diff --git a/src/operator/subgraph/partitioner/custom_subgraph_property.h b/src/operator/subgraph/partitioner/custom_subgraph_property.h index 654a11db76ca..86219628d0bb 100644 --- a/src/operator/subgraph/partitioner/custom_subgraph_property.h +++ b/src/operator/subgraph/partitioner/custom_subgraph_property.h @@ -32,7 +32,7 @@ namespace op { */ class CustomContainOpSelector: public SubgraphSelector { public: - explicit CustomContainOpSelector() {} + CustomContainOpSelector() {} virtual bool Select(const nnvm::Node &n) { return false; } @@ -64,14 +64,10 @@ class CustomSubgraphProperty: public SubgraphProperty { } // override CreateSubgraphSelector virtual SubgraphSelectorPtr CreateSubgraphSelector() const { - return std::make_shared(); + return std::make_shared(); } }; - -MXNET_REGISTER_SUBGRAPH_BACKEND(customBackend); -MXNET_REGISTER_SUBGRAPH_PROPERTY(customBackend, CustomSubgraphProperty); - } // namespace op } // namespace mxnet -#endif // MXNET_OPERATOR_SUBGRAPH_PARTITIONER_CUSTOM_SUBGRAPH_PROPERTY_H_ +#endif // MXNET_OPERATOR_SUBGRAPH_PARTITIONER_CUSTOM_SUBGRAPH_PROPERTY_H_ From 55e0974aa4b0dfc2ccd1eda9027d22e6e5dcc7c8 Mon Sep 17 00:00:00 2001 From: samskalicky Date: Fri, 13 Dec 2019 09:23:32 +0000 Subject: [PATCH 10/50] added support for calling supportOps from customSubgraphProperty --- .../extensions/lib_subgraph/subgraph_lib.cc | 1 + include/mxnet/lib_api.h | 18 +++++++++++++ src/c_api/c_api.cc | 15 +++++------ .../partitioner/custom_subgraph_property.h | 25 +++++++++++++++++++ src/operator/subgraph/subgraph_property.h | 9 +++++++ 5 files changed, 61 insertions(+), 7 deletions(-) diff --git a/example/extensions/lib_subgraph/subgraph_lib.cc b/example/extensions/lib_subgraph/subgraph_lib.cc index f5dbc2fb2632..bcf057884507 100644 --- a/example/extensions/lib_subgraph/subgraph_lib.cc +++ b/example/extensions/lib_subgraph/subgraph_lib.cc @@ -107,6 +107,7 @@ MXReturnValue mySupportedOps(const char *json, const MXTensor *data, const int num_data, int *ids) { + std::cout << "in mySupportedOps" << std::endl; return MX_SUCCESS; } diff --git a/include/mxnet/lib_api.h b/include/mxnet/lib_api.h index eacc94efcd2a..b197a78c37aa 100644 --- a/include/mxnet/lib_api.h +++ b/include/mxnet/lib_api.h @@ -745,6 +745,10 @@ typedef int (*partRegSize_t)(void); #define MXLIB_PARTREGGET_STR "_partRegGet" typedef int (*partRegGet_t)(int, const char**, supportedOps_t*, const char**); +#define MXLIB_PARTCALLSUPPORTEDOPS_STR "_partCallSupportedOps" +typedef int (*partCallSupportedOps_t)(supportedOps_t, const char*, const char*[], + const MXTensor*, const int, int *); + #define MXLIB_INITIALIZE_STR "initialize" typedef int (*initialize_t)(int); @@ -1066,6 +1070,20 @@ extern "C" { *op_name = part.op_name; } + /*! \brief returns status of calling parse attributes function for operator from library */ +#if defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__) + __declspec(dllexport) int __cdecl +#else + int +#endif + _partCallSupportedOps(supportedOps_t supportedOps, const char *json, + const char *data_names[], + const MXTensor *data, + const int num_data, + int *ids) { + return supportedOps(json, data_names, data, num_data, ids); + } + /*! * \brief Checks if the MXNet version is supported by the library. * If supported, initializes the library. diff --git a/src/c_api/c_api.cc b/src/c_api/c_api.cc index 0ee57ff674d3..23f31d2b2cb3 100644 --- a/src/c_api/c_api.cc +++ b/src/c_api/c_api.cc @@ -143,6 +143,9 @@ int MXLoadLib(const char *path) { opCallFStatefulComp_t callFStatefulComp = get_func(lib, const_cast(MXLIB_OPCALLFSTATEFULCOMP_STR)); + partCallSupportedOps_t callSupportedOps = + get_func(lib, const_cast(MXLIB_PARTCALLSUPPORTEDOPS_STR)); + // get number of operators registered in the library opRegSize_t opRegSize = get_func(lib, const_cast(MXLIB_OPREGSIZE_STR)); int numOps = opRegSize(); @@ -715,14 +718,12 @@ int MXLoadLib(const char *path) { LOG(INFO) << "\tPartitioner[" << i << "] " << name << " subgraphOp: '" << op_name << "'"; std::string name_str(name); - auto createProp = [=]() { - return static_cast >(std::make_shared()); - }; - - //MXNET_REGISTER_SUBGRAPH_BACKEND(customBackend); + // MXNET_REGISTER_SUBGRAPH_BACKEND(customBackend); mxnet::op::SubgraphBackendRegistry::Get()->__REGISTER_BACKEND__(name); - //MXNET_REGISTER_SUBGRAPH_PROPERTY(customBackend, CustomSubgraphProperty); - mxnet::op::SubgraphBackendRegistry::Get()->__REGISTER_PROPERTY__(name, createProp); + // MXNET_REGISTER_SUBGRAPH_PROPERTY(customBackend, CustomSubgraphProperty); + mxnet::op::SubgraphBackendRegistry::Get()->__REGISTER_CUSTOM_PROPERTY__(name, + std::make_shared( + callSupportedOps, supportedOps_fp)); } API_END(); } diff --git a/src/operator/subgraph/partitioner/custom_subgraph_property.h b/src/operator/subgraph/partitioner/custom_subgraph_property.h index 86219628d0bb..dc6d3c7d7532 100644 --- a/src/operator/subgraph/partitioner/custom_subgraph_property.h +++ b/src/operator/subgraph/partitioner/custom_subgraph_property.h @@ -23,6 +23,7 @@ #include #include "../common.h" #include "../subgraph_property.h" +#include "../../include/mxnet/lib_api.h" namespace mxnet { namespace op { @@ -49,10 +50,31 @@ class CustomContainOpSelector: public SubgraphSelector { */ class CustomSubgraphProperty: public SubgraphProperty { public: + CustomSubgraphProperty() { + supportedOps_ = nullptr; + } + CustomSubgraphProperty(partCallSupportedOps_t callSupportedOps, supportedOps_t supportedOps) { + callSupportedOps_ = callSupportedOps; + supportedOps_ = supportedOps; + } // create custom subgraph property static SubgraphPropertyPtr Create() { return std::make_shared(); } + void PrePartition(const nnvm::Graph& g, + const std::vector>& options_map) { + std::cout << "PrePartition" << std::endl; + if (supportedOps_ == nullptr) + std::cout << "supportedOps_ is null" << std::endl; + else { + char* json = "test"; + const char **data_names; + MXTensor *data; + int num_data; + int *ids; + int retval = callSupportedOps_(supportedOps_, json, data_names, data, num_data, ids); + } + } // override CreateSubgraphNode virtual nnvm::NodePtr CreateSubgraphNode(const nnvm::Symbol &sym, const int subgraph_id = 0) const { @@ -66,6 +88,9 @@ class CustomSubgraphProperty: public SubgraphProperty { virtual SubgraphSelectorPtr CreateSubgraphSelector() const { return std::make_shared(); } + + partCallSupportedOps_t callSupportedOps_; + supportedOps_t supportedOps_; }; } // namespace op } // namespace mxnet diff --git a/src/operator/subgraph/subgraph_property.h b/src/operator/subgraph/subgraph_property.h index bbc8f4076899..643c02a82b13 100644 --- a/src/operator/subgraph/subgraph_property.h +++ b/src/operator/subgraph/subgraph_property.h @@ -520,6 +520,15 @@ class SubgraphBackendRegistry { return SubgraphPropertyEntry(prop); } + SubgraphPropertyEntry __REGISTER_CUSTOM_PROPERTY__(const std::string& name, + SubgraphPropertyPtr cprop) { + auto it = backend_map_.find(name); + CHECK(it != backend_map_.end()) + << "Subgraph backend " << name << " is not found in SubgraphBackendRegistry"; + auto prop = it->second->RegisterSubgraphProperty(cprop); + return SubgraphPropertyEntry(prop); + } + SubgraphBackendRegistry() = default; SubgraphBackendRegistry(const SubgraphBackendRegistry&) = delete; SubgraphBackendRegistry(SubgraphBackendRegistry&&) = delete; From 6a7e3f7dca3d9ce9d6de3f08f65cd04efdb9213a Mon Sep 17 00:00:00 2001 From: samskalicky Date: Fri, 13 Dec 2019 18:03:07 +0000 Subject: [PATCH 11/50] sending symbol json to supportedOps API example is printing --- .../extensions/lib_subgraph/subgraph_lib.cc | 10 +++++--- include/mxnet/lib_api.h | 14 ++++------- .../partitioner/custom_subgraph_property.h | 25 ++++++++++++------- 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/example/extensions/lib_subgraph/subgraph_lib.cc b/example/extensions/lib_subgraph/subgraph_lib.cc index bcf057884507..8b961afc70ae 100644 --- a/example/extensions/lib_subgraph/subgraph_lib.cc +++ b/example/extensions/lib_subgraph/subgraph_lib.cc @@ -102,12 +102,14 @@ REGISTER_OP(_custom_subgraph_op) .setInferShape(inferShape) .setCreateOpState(createOpState); -MXReturnValue mySupportedOps(const char *json, - const char *data_names[], - const MXTensor *data, - const int num_data, +MXReturnValue mySupportedOps(std::string json, + const int num_ids, int *ids) { std::cout << "in mySupportedOps" << std::endl; + std::cout << "num_ids : " << num_ids << std::endl; + std::cout << "json: " << std::endl; + std::cout << json << std::endl; + return MX_SUCCESS; } diff --git a/include/mxnet/lib_api.h b/include/mxnet/lib_api.h index b197a78c37aa..c854daeac2ea 100644 --- a/include/mxnet/lib_api.h +++ b/include/mxnet/lib_api.h @@ -585,9 +585,7 @@ class CustomOp { }; /*! \brief Custom Subgraph Create function template */ -typedef MXReturnValue (*supportedOps_t)(const char*, - const char*[], - const MXTensor*, +typedef MXReturnValue (*supportedOps_t)(std::string, const int, int*); @@ -746,8 +744,7 @@ typedef int (*partRegSize_t)(void); typedef int (*partRegGet_t)(int, const char**, supportedOps_t*, const char**); #define MXLIB_PARTCALLSUPPORTEDOPS_STR "_partCallSupportedOps" -typedef int (*partCallSupportedOps_t)(supportedOps_t, const char*, const char*[], - const MXTensor*, const int, int *); +typedef int (*partCallSupportedOps_t)(supportedOps_t, const char*, const int, int *); #define MXLIB_INITIALIZE_STR "initialize" typedef int (*initialize_t)(int); @@ -1077,11 +1074,10 @@ extern "C" { int #endif _partCallSupportedOps(supportedOps_t supportedOps, const char *json, - const char *data_names[], - const MXTensor *data, - const int num_data, + const int num_ids, int *ids) { - return supportedOps(json, data_names, data, num_data, ids); + std::string subgraph_json(json); + return supportedOps(subgraph_json, num_ids, ids); } /*! diff --git a/src/operator/subgraph/partitioner/custom_subgraph_property.h b/src/operator/subgraph/partitioner/custom_subgraph_property.h index dc6d3c7d7532..c9ab14ac2b3d 100644 --- a/src/operator/subgraph/partitioner/custom_subgraph_property.h +++ b/src/operator/subgraph/partitioner/custom_subgraph_property.h @@ -21,10 +21,12 @@ #define MXNET_OPERATOR_SUBGRAPH_PARTITIONER_CUSTOM_SUBGRAPH_PROPERTY_H_ #include +#include +#include #include "../common.h" #include "../subgraph_property.h" #include "../../include/mxnet/lib_api.h" - +#include namespace mxnet { namespace op { @@ -64,15 +66,20 @@ class CustomSubgraphProperty: public SubgraphProperty { void PrePartition(const nnvm::Graph& g, const std::vector>& options_map) { std::cout << "PrePartition" << std::endl; - if (supportedOps_ == nullptr) + std::string subgraph_json = nnvm::pass::SaveJSON(g); + int num_ids = 0; + DFSVisit(g.outputs, [&](const nnvm::NodePtr& nptr) { + nnvm::Node *node = nptr.get(); + // increment count for number of nodes in model + num_ids++; + }); + std::vector supportedOps(num_ids,0); + if (supportedOps_ == nullptr) { std::cout << "supportedOps_ is null" << std::endl; - else { - char* json = "test"; - const char **data_names; - MXTensor *data; - int num_data; - int *ids; - int retval = callSupportedOps_(supportedOps_, json, data_names, data, num_data, ids); + } else { + const char* json = subgraph_json.c_str(); + int *ids = supportedOps.data(); + int retval = callSupportedOps_(supportedOps_, json, num_ids, ids); } } // override CreateSubgraphNode From fa206e4acd40b38db1f321d65cc6c9c62f64b118 Mon Sep 17 00:00:00 2001 From: samskalicky Date: Sat, 14 Dec 2019 03:29:08 +0000 Subject: [PATCH 12/50] working partitioning example --- .../extensions/lib_subgraph/subgraph_lib.cc | 26 +++++++++++--- .../extensions/lib_subgraph/test_subgraph.py | 7 ++-- src/c_api/c_api.cc | 2 +- .../partitioner/custom_subgraph_property.h | 34 +++++++++++++------ 4 files changed, 51 insertions(+), 18 deletions(-) diff --git a/example/extensions/lib_subgraph/subgraph_lib.cc b/example/extensions/lib_subgraph/subgraph_lib.cc index 8b961afc70ae..0eb8bfc86135 100644 --- a/example/extensions/lib_subgraph/subgraph_lib.cc +++ b/example/extensions/lib_subgraph/subgraph_lib.cc @@ -24,6 +24,7 @@ */ #include +#include #include "lib_api.h" MXReturnValue parseAttrs(std::map attrs, @@ -102,14 +103,29 @@ REGISTER_OP(_custom_subgraph_op) .setInferShape(inferShape) .setCreateOpState(createOpState); +const std::vector op_names({"exp","log"}); + MXReturnValue mySupportedOps(std::string json, const int num_ids, int *ids) { - std::cout << "in mySupportedOps" << std::endl; - std::cout << "num_ids : " << num_ids << std::endl; - std::cout << "json: " << std::endl; - std::cout << json << std::endl; - + //convert json string to json object + JsonParser parser; + JsonVal json_val = parser.parse_to_json(json); + + //get nodes list + JsonVal nodes = json_val.map[JsonVal("nodes")]; + + //loop over nodes + for(int i=0; i__REGISTER_CUSTOM_PROPERTY__(name, std::make_shared( - callSupportedOps, supportedOps_fp)); + callSupportedOps, supportedOps_fp, op_name)); } API_END(); } diff --git a/src/operator/subgraph/partitioner/custom_subgraph_property.h b/src/operator/subgraph/partitioner/custom_subgraph_property.h index c9ab14ac2b3d..57117a7f5f95 100644 --- a/src/operator/subgraph/partitioner/custom_subgraph_property.h +++ b/src/operator/subgraph/partitioner/custom_subgraph_property.h @@ -35,9 +35,9 @@ namespace op { */ class CustomContainOpSelector: public SubgraphSelector { public: - CustomContainOpSelector() {} + CustomContainOpSelector(std::vector supportedNodes) : supportedNodes_(supportedNodes) {} virtual bool Select(const nnvm::Node &n) { - return false; + return std::find(supportedNodes_.begin(), supportedNodes_.end(), n.attrs.name) != supportedNodes_.end(); } virtual bool SelectInput(const nnvm::Node &n, const nnvm::Node &new_node) { return false; @@ -45,6 +45,7 @@ class CustomContainOpSelector: public SubgraphSelector { virtual bool SelectOutput(const nnvm::Node &n, const nnvm::Node &new_node) { return false; } + std::vector supportedNodes_; }; /* @@ -55,10 +56,12 @@ class CustomSubgraphProperty: public SubgraphProperty { CustomSubgraphProperty() { supportedOps_ = nullptr; } - CustomSubgraphProperty(partCallSupportedOps_t callSupportedOps, supportedOps_t supportedOps) { - callSupportedOps_ = callSupportedOps; - supportedOps_ = supportedOps; - } + CustomSubgraphProperty(partCallSupportedOps_t callSupportedOps, + supportedOps_t supportedOps, + std::string op_name) : + callSupportedOps_(callSupportedOps), + supportedOps_(supportedOps), + subgraph_op_name(op_name) {} // create custom subgraph property static SubgraphPropertyPtr Create() { return std::make_shared(); @@ -73,31 +76,42 @@ class CustomSubgraphProperty: public SubgraphProperty { // increment count for number of nodes in model num_ids++; }); - std::vector supportedOps(num_ids,0); + std::vector supportedNodeIDs(num_ids,0); if (supportedOps_ == nullptr) { std::cout << "supportedOps_ is null" << std::endl; } else { const char* json = subgraph_json.c_str(); - int *ids = supportedOps.data(); + int *ids = supportedNodeIDs.data(); int retval = callSupportedOps_(supportedOps_, json, num_ids, ids); } + + const auto& idx = g.indexed_graph(); + std::cout << "supportedNodes:" << std::endl; + for(int i=0; iattrs.name); + std::cout << idx[i].source->attrs.name << std::endl; + } + } } // override CreateSubgraphNode virtual nnvm::NodePtr CreateSubgraphNode(const nnvm::Symbol &sym, const int subgraph_id = 0) const { nnvm::NodePtr n = nnvm::Node::Create(); - n->attrs.op = Op::Get("_op"); + n->attrs.op = Op::Get(subgraph_op_name); n->attrs.name = "_op" + std::to_string(subgraph_id); n->attrs.subgraphs.push_back(std::make_shared(sym)); return n; } // override CreateSubgraphSelector virtual SubgraphSelectorPtr CreateSubgraphSelector() const { - return std::make_shared(); + return std::make_shared(supportedNodes); } partCallSupportedOps_t callSupportedOps_; supportedOps_t supportedOps_; + std::vector supportedNodes; + std::string subgraph_op_name; }; } // namespace op } // namespace mxnet From 79d08eacca4417983d4613e29c7b23393d1e1fe9 Mon Sep 17 00:00:00 2001 From: samskalicky Date: Sat, 14 Dec 2019 03:42:39 +0000 Subject: [PATCH 13/50] fixed whitespace --- .../partitioner/custom_subgraph_property.h | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/operator/subgraph/partitioner/custom_subgraph_property.h b/src/operator/subgraph/partitioner/custom_subgraph_property.h index 57117a7f5f95..df6a59efacd1 100644 --- a/src/operator/subgraph/partitioner/custom_subgraph_property.h +++ b/src/operator/subgraph/partitioner/custom_subgraph_property.h @@ -23,10 +23,10 @@ #include #include #include +#include #include "../common.h" #include "../subgraph_property.h" #include "../../include/mxnet/lib_api.h" -#include namespace mxnet { namespace op { @@ -35,9 +35,11 @@ namespace op { */ class CustomContainOpSelector: public SubgraphSelector { public: - CustomContainOpSelector(std::vector supportedNodes) : supportedNodes_(supportedNodes) {} + CustomContainOpSelector(std::vector supportedNodes) : + supportedNodes_(supportedNodes) {} virtual bool Select(const nnvm::Node &n) { - return std::find(supportedNodes_.begin(), supportedNodes_.end(), n.attrs.name) != supportedNodes_.end(); + return std::find(supportedNodes_.begin(), supportedNodes_.end(), + n.attrs.name) != supportedNodes_.end(); } virtual bool SelectInput(const nnvm::Node &n, const nnvm::Node &new_node) { return false; @@ -59,7 +61,7 @@ class CustomSubgraphProperty: public SubgraphProperty { CustomSubgraphProperty(partCallSupportedOps_t callSupportedOps, supportedOps_t supportedOps, std::string op_name) : - callSupportedOps_(callSupportedOps), + callSupportedOps_(callSupportedOps), supportedOps_(supportedOps), subgraph_op_name(op_name) {} // create custom subgraph property @@ -76,7 +78,7 @@ class CustomSubgraphProperty: public SubgraphProperty { // increment count for number of nodes in model num_ids++; }); - std::vector supportedNodeIDs(num_ids,0); + std::vector supportedNodeIDs(num_ids, 0); if (supportedOps_ == nullptr) { std::cout << "supportedOps_ is null" << std::endl; } else { @@ -84,11 +86,11 @@ class CustomSubgraphProperty: public SubgraphProperty { int *ids = supportedNodeIDs.data(); int retval = callSupportedOps_(supportedOps_, json, num_ids, ids); } - + const auto& idx = g.indexed_graph(); std::cout << "supportedNodes:" << std::endl; - for(int i=0; iattrs.name); std::cout << idx[i].source->attrs.name << std::endl; } From bbe4062e9107e4ade9026fd6d8e4004fe11311ea Mon Sep 17 00:00:00 2001 From: samskalicky Date: Sat, 14 Dec 2019 03:49:49 +0000 Subject: [PATCH 14/50] fixed whitespace --- src/c_api/c_api.cc | 2 +- .../partitioner/custom_subgraph_property.h | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/c_api/c_api.cc b/src/c_api/c_api.cc index be0ac4dc464f..ca0fef2adb04 100644 --- a/src/c_api/c_api.cc +++ b/src/c_api/c_api.cc @@ -723,7 +723,7 @@ int MXLoadLib(const char *path) { // MXNET_REGISTER_SUBGRAPH_PROPERTY(customBackend, CustomSubgraphProperty); mxnet::op::SubgraphBackendRegistry::Get()->__REGISTER_CUSTOM_PROPERTY__(name, std::make_shared( - callSupportedOps, supportedOps_fp, op_name)); + name_str, callSupportedOps, supportedOps_fp, op_name)); } API_END(); } diff --git a/src/operator/subgraph/partitioner/custom_subgraph_property.h b/src/operator/subgraph/partitioner/custom_subgraph_property.h index df6a59efacd1..81667f2f4a48 100644 --- a/src/operator/subgraph/partitioner/custom_subgraph_property.h +++ b/src/operator/subgraph/partitioner/custom_subgraph_property.h @@ -58,10 +58,12 @@ class CustomSubgraphProperty: public SubgraphProperty { CustomSubgraphProperty() { supportedOps_ = nullptr; } - CustomSubgraphProperty(partCallSupportedOps_t callSupportedOps, - supportedOps_t supportedOps, - std::string op_name) : - callSupportedOps_(callSupportedOps), + CustomSubgraphProperty(std::string subgraphProp_name, + partCallSupportedOps_t callSupportedOps, + supportedOps_t supportedOps, + std::string op_name) : + subgraphProp(subgraphProp_name), + callSupportedOps_(callSupportedOps), supportedOps_(supportedOps), subgraph_op_name(op_name) {} // create custom subgraph property @@ -74,7 +76,6 @@ class CustomSubgraphProperty: public SubgraphProperty { std::string subgraph_json = nnvm::pass::SaveJSON(g); int num_ids = 0; DFSVisit(g.outputs, [&](const nnvm::NodePtr& nptr) { - nnvm::Node *node = nptr.get(); // increment count for number of nodes in model num_ids++; }); @@ -84,7 +85,8 @@ class CustomSubgraphProperty: public SubgraphProperty { } else { const char* json = subgraph_json.c_str(); int *ids = supportedNodeIDs.data(); - int retval = callSupportedOps_(supportedOps_, json, num_ids, ids); + CHECK(callSupportedOps_(supportedOps_, json, num_ids, ids)) + << "Error calling supportedOps for '" << subgraphProp << "'"; } const auto& idx = g.indexed_graph(); @@ -114,6 +116,7 @@ class CustomSubgraphProperty: public SubgraphProperty { supportedOps_t supportedOps_; std::vector supportedNodes; std::string subgraph_op_name; + std::string subgraphProp; }; } // namespace op } // namespace mxnet From a41830158f7847159a11c95b5f158607215ea491 Mon Sep 17 00:00:00 2001 From: samskalicky Date: Sat, 14 Dec 2019 04:04:02 +0000 Subject: [PATCH 15/50] fixed style --- .../subgraph/partitioner/custom_subgraph_property.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/operator/subgraph/partitioner/custom_subgraph_property.h b/src/operator/subgraph/partitioner/custom_subgraph_property.h index 81667f2f4a48..275e7a9b3935 100644 --- a/src/operator/subgraph/partitioner/custom_subgraph_property.h +++ b/src/operator/subgraph/partitioner/custom_subgraph_property.h @@ -20,10 +20,10 @@ #ifndef MXNET_OPERATOR_SUBGRAPH_PARTITIONER_CUSTOM_SUBGRAPH_PROPERTY_H_ #define MXNET_OPERATOR_SUBGRAPH_PARTITIONER_CUSTOM_SUBGRAPH_PROPERTY_H_ +#include #include #include #include -#include #include "../common.h" #include "../subgraph_property.h" #include "../../include/mxnet/lib_api.h" @@ -35,7 +35,7 @@ namespace op { */ class CustomContainOpSelector: public SubgraphSelector { public: - CustomContainOpSelector(std::vector supportedNodes) : + explicit CustomContainOpSelector(std::vector supportedNodes) : supportedNodes_(supportedNodes) {} virtual bool Select(const nnvm::Node &n) { return std::find(supportedNodes_.begin(), supportedNodes_.end(), @@ -58,14 +58,14 @@ class CustomSubgraphProperty: public SubgraphProperty { CustomSubgraphProperty() { supportedOps_ = nullptr; } - CustomSubgraphProperty(std::string subgraphProp_name, + CustomSubgraphProperty(std::string subgraphProp_name, partCallSupportedOps_t callSupportedOps, supportedOps_t supportedOps, std::string op_name) : - subgraphProp(subgraphProp_name), callSupportedOps_(callSupportedOps), supportedOps_(supportedOps), subgraph_op_name(op_name) {} + subgraphProp(subgraphProp_name), // create custom subgraph property static SubgraphPropertyPtr Create() { return std::make_shared(); From 420e77a012f67755b471e5570abb4b05b9b42452 Mon Sep 17 00:00:00 2001 From: samskalicky Date: Sat, 14 Dec 2019 04:21:44 +0000 Subject: [PATCH 16/50] fixed style --- src/operator/subgraph/partitioner/custom_subgraph_property.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/operator/subgraph/partitioner/custom_subgraph_property.h b/src/operator/subgraph/partitioner/custom_subgraph_property.h index 275e7a9b3935..e9ea7a50f622 100644 --- a/src/operator/subgraph/partitioner/custom_subgraph_property.h +++ b/src/operator/subgraph/partitioner/custom_subgraph_property.h @@ -64,8 +64,8 @@ class CustomSubgraphProperty: public SubgraphProperty { std::string op_name) : callSupportedOps_(callSupportedOps), supportedOps_(supportedOps), - subgraph_op_name(op_name) {} - subgraphProp(subgraphProp_name), + subgraph_op_name(op_name), + subgraphProp(subgraphProp_name) {} // create custom subgraph property static SubgraphPropertyPtr Create() { return std::make_shared(); From 2f354a4c90fbd76d182aac101efef1681a6a4632 Mon Sep 17 00:00:00 2001 From: samskalicky Date: Sun, 15 Dec 2019 06:52:07 +0000 Subject: [PATCH 17/50] Annotate the symbol json string with shape/type info from the graph object --- .../extensions/lib_subgraph/subgraph_lib.cc | 12 ++- .../extensions/lib_subgraph/test_subgraph.py | 9 +- .../partitioner/custom_subgraph_property.h | 84 +++++++++++++------ 3 files changed, 79 insertions(+), 26 deletions(-) diff --git a/example/extensions/lib_subgraph/subgraph_lib.cc b/example/extensions/lib_subgraph/subgraph_lib.cc index 0eb8bfc86135..85613ccf362b 100644 --- a/example/extensions/lib_subgraph/subgraph_lib.cc +++ b/example/extensions/lib_subgraph/subgraph_lib.cc @@ -111,7 +111,6 @@ MXReturnValue mySupportedOps(std::string json, //convert json string to json object JsonParser parser; JsonVal json_val = parser.parse_to_json(json); - //get nodes list JsonVal nodes = json_val.map[JsonVal("nodes")]; @@ -120,8 +119,19 @@ MXReturnValue mySupportedOps(std::string json, JsonVal node = nodes.list[i]; JsonVal op = node.map[JsonVal("op")]; + //get shape/type if available + std::string shape,dtype; + if(node.map.find(JsonVal("attrs")) != node.map.end()) { + JsonVal attrs = node.map[JsonVal("attrs")]; + if(attrs.map.find(JsonVal("shape")) != attrs.map.end()) + shape = attrs.map[JsonVal("shape")].str; + if(attrs.map.find(JsonVal("dtype")) != attrs.map.end()) + dtype = attrs.map[JsonVal("dtype")].str; + } + //check if op is in whitelist if(std::find(op_names.begin(),op_names.end(),op.str.c_str()) != op_names.end()) { + std::cout << op.str << " : " << shape << " " << dtype << std::endl; // found op in whitelist, set value to 1 to include op in subgraph ids[i]=1; } diff --git a/example/extensions/lib_subgraph/test_subgraph.py b/example/extensions/lib_subgraph/test_subgraph.py index 2a12afc17e4d..c4284759563d 100644 --- a/example/extensions/lib_subgraph/test_subgraph.py +++ b/example/extensions/lib_subgraph/test_subgraph.py @@ -41,8 +41,15 @@ d = mx.sym.exp(c) sym = mx.sym.log(d) -mysym = sym.optimize_for("myProp") +# with propogating shapes/types +arg_array = [mx.nd.ones((3,2),dtype='int'), mx.nd.ones((3,2),dtype='int')] +mysym = sym.optimize_for("myProp", arg_array) +exe = mysym.bind(ctx=mx.cpu(), args={'a':mx.nd.ones((3,2)), 'b':mx.nd.ones((3,2))}) +out = exe.forward() +print(out) +# without propogating shapes/types +mysym = sym.optimize_for("myProp") exe = mysym.bind(ctx=mx.cpu(), args={'a':mx.nd.ones((3,2)), 'b':mx.nd.ones((3,2))}) out = exe.forward() print(out) diff --git a/src/operator/subgraph/partitioner/custom_subgraph_property.h b/src/operator/subgraph/partitioner/custom_subgraph_property.h index e9ea7a50f622..cbb980311cfe 100644 --- a/src/operator/subgraph/partitioner/custom_subgraph_property.h +++ b/src/operator/subgraph/partitioner/custom_subgraph_property.h @@ -31,7 +31,9 @@ namespace mxnet { namespace op { /* - * This selects nodes for a subgraph + * This selects nodes for a subgraph based on node name as supplied + * by the supportedOps from an external library. It visits nodes via + * both input and output links. */ class CustomContainOpSelector: public SubgraphSelector { public: @@ -42,22 +44,29 @@ class CustomContainOpSelector: public SubgraphSelector { n.attrs.name) != supportedNodes_.end(); } virtual bool SelectInput(const nnvm::Node &n, const nnvm::Node &new_node) { - return false; + return std::find(supportedNodes_.begin(), supportedNodes_.end(), + new_node.attrs.name) != supportedNodes_.end(); } virtual bool SelectOutput(const nnvm::Node &n, const nnvm::Node &new_node) { - return false; + return std::find(supportedNodes_.begin(), supportedNodes_.end(), + new_node.attrs.name) != supportedNodes_.end(); } std::vector supportedNodes_; }; /* - * This subgraph property finds a subgraph + * This subgraph property finds a subgraph that only contains + * nodes as specified by the supportedOps from an external library. + * The operators in the subgraph will be executed by the operator + * specified by the external library too. */ class CustomSubgraphProperty: public SubgraphProperty { public: - CustomSubgraphProperty() { - supportedOps_ = nullptr; - } + CustomSubgraphProperty() : + callSupportedOps_(nullptr), + supportedOps_(nullptr), + subgraph_op_name("error"), + subgraphProp("error") {} CustomSubgraphProperty(std::string subgraphProp_name, partCallSupportedOps_t callSupportedOps, supportedOps_t supportedOps, @@ -66,35 +75,62 @@ class CustomSubgraphProperty: public SubgraphProperty { supportedOps_(supportedOps), subgraph_op_name(op_name), subgraphProp(subgraphProp_name) {} + // create custom subgraph property static SubgraphPropertyPtr Create() { return std::make_shared(); } + void PrePartition(const nnvm::Graph& g, const std::vector>& options_map) { std::cout << "PrePartition" << std::endl; - std::string subgraph_json = nnvm::pass::SaveJSON(g); - int num_ids = 0; - DFSVisit(g.outputs, [&](const nnvm::NodePtr& nptr) { - // increment count for number of nodes in model - num_ids++; - }); - std::vector supportedNodeIDs(num_ids, 0); - if (supportedOps_ == nullptr) { - std::cout << "supportedOps_ is null" << std::endl; - } else { - const char* json = subgraph_json.c_str(); - int *ids = supportedNodeIDs.data(); - CHECK(callSupportedOps_(supportedOps_, json, num_ids, ids)) - << "Error calling supportedOps for '" << subgraphProp << "'"; + + // remove all graph attrs, some cannot be saved to json + nnvm::Graph graph = std::move(g); + graph.attrs.clear(); + const nnvm::IndexedGraph& indexed_graph = graph.indexed_graph(); + + // set shape attrs for each node in the graph + if (g.HasAttr("shape")) { + mxnet::ShapeVector shapes = g.GetAttr("shape"); + for (int i = 0; i(indexed_graph[i].source); + mxnet::TShape shape = shapes[i]; + std::stringstream ss; + ss << shape; + node->attrs.dict["shape"]=ss.str(); + } + } + // set dtype attrs for each node in the graph + if (g.HasAttr("dtype")) { + std::vector dtypes = g.GetAttr >("dtype"); + for (int i = 0; i(indexed_graph[i].source); + int dtype = dtypes[i]; + std::stringstream ss; + ss << dtype; + node->attrs.dict["dtype"]=ss.str(); + } } + + CHECK(supportedOps_ == nullptr) + << "supportedOps_ is null for " << subgraphProp << std::endl; + CHECK(callSupportedOps_ == nullptr) + << "callSupportedOps_ is null for " << subgraphProp << std::endl; + + std::string subgraph_json = nnvm::pass::SaveJSON(graph); + std::vector supportedNodeIDs(indexed_graph.num_nodes(), 0); + const char* json = subgraph_json.c_str(); + int *ids = supportedNodeIDs.data(); + + CHECK(callSupportedOps_(supportedOps_, json, supportedNodeIDs.size(), ids)) + << "Error calling supportedOps for '" << subgraphProp << "'"; const auto& idx = g.indexed_graph(); - std::cout << "supportedNodes:" << std::endl; - for (int i = 0; i < num_ids; i++) { + // loop and add node names for each supported node ID + for (int i = 0; i < supportedNodeIDs.size(); i++) { if (supportedNodeIDs[i]) { supportedNodes.push_back(idx[i].source->attrs.name); - std::cout << idx[i].source->attrs.name << std::endl; } } } From 92e6f0024cb59e9b3e0b640c3d8409b3a38b8c81 Mon Sep 17 00:00:00 2001 From: samskalicky Date: Sun, 15 Dec 2019 08:08:03 +0000 Subject: [PATCH 18/50] Support multiple supportOps functions grouped together. Similar to how subgraphProperties are grouped into subgraphBackends --- .../extensions/lib_subgraph/subgraph_lib.cc | 3 +- include/mxnet/lib_api.h | 49 ++++++++++++------ src/c_api/c_api.cc | 50 ++++++++++++------- .../partitioner/custom_subgraph_property.h | 22 ++++---- 4 files changed, 78 insertions(+), 46 deletions(-) diff --git a/example/extensions/lib_subgraph/subgraph_lib.cc b/example/extensions/lib_subgraph/subgraph_lib.cc index 85613ccf362b..c66924cd7181 100644 --- a/example/extensions/lib_subgraph/subgraph_lib.cc +++ b/example/extensions/lib_subgraph/subgraph_lib.cc @@ -140,8 +140,7 @@ MXReturnValue mySupportedOps(std::string json, } REGISTER_PARTITIONER(myProp) -.setSupportedOps(mySupportedOps) -.setSubgraphOp("_custom_subgraph_op"); +.addStrategy("strategy1", mySupportedOps, "_custom_subgraph_op"); MXReturnValue initialize(int version) { if (version >= 10400) { diff --git a/include/mxnet/lib_api.h b/include/mxnet/lib_api.h index c854daeac2ea..4b11e504baa6 100644 --- a/include/mxnet/lib_api.h +++ b/include/mxnet/lib_api.h @@ -595,22 +595,25 @@ typedef MXReturnValue (*supportedOps_t)(std::string, class CustomPartitioner { public: CustomPartitioner() : name("ERROR") {} - explicit CustomPartitioner(const char* prop_name) : name(prop_name) {} - CustomPartitioner& setSupportedOps(supportedOps_t fn) { - supportedOps = fn; - return *this; - } - CustomPartitioner& setSubgraphOp(const char* sg_name) { - op_name = sg_name; + explicit CustomPartitioner(const char* backend_name) : + name(backend_name) {} + CustomPartitioner& addStrategy(const char* prop_name, + supportedOps_t fn, + const char* sg_name) { + strategies.push_back(prop_name); + supportedOps.push_back(fn); + op_names.push_back(sg_name); return *this; } /*! \brief partitioner name */ const char* name; + /*! \brief strategy names */ + std::vector strategies; /*! \brief supported ops function */ - supportedOps_t supportedOps; + std::vector supportedOps; /*! \brief subgraph operator name */ - const char* op_name; + std::vector op_names; }; /*! @@ -740,8 +743,12 @@ typedef int (*opCallFStatefulComp_t)(bool, void*, const int64_t**, int*, void**, #define MXLIB_PARTREGSIZE_STR "_partRegSize" typedef int (*partRegSize_t)(void); +#define MXLIB_PARTREGGETCOUNT_STR "_partRegGetCount" +typedef int (*partRegGetCount_t)(int, int*, const char**); + #define MXLIB_PARTREGGET_STR "_partRegGet" -typedef int (*partRegGet_t)(int, const char**, supportedOps_t*, const char**); +typedef int (*partRegGet_t)(int, int, const char**, + supportedOps_t*, const char**); #define MXLIB_PARTCALLSUPPORTEDOPS_STR "_partCallSupportedOps" typedef int (*partCallSupportedOps_t)(supportedOps_t, const char*, const int, int *); @@ -1054,17 +1061,31 @@ extern "C" { return Registry::get()->size(); } - /*! \brief returns partitioner registration at specified index */ + /* returns number of strategies registered for partitioner + * at specified index */ #if defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__) __declspec(dllexport) void __cdecl #else void #endif - _partRegGet(int idx, const char** name, supportedOps_t* fn, const char** op_name) { + _partRegGetCount(int idx, int* count, const char** name) { CustomPartitioner part = Registry::get()->get(idx); *name = part.name; - *fn = part.supportedOps; - *op_name = part.op_name; + *count = part.strategies.size(); + } + + /*! \brief returns partitioner registration at specified index */ +#if defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__) + __declspec(dllexport) void __cdecl +#else + void +#endif + _partRegGet(int part_idx, int stg_idx, const char** strategy, + supportedOps_t* fn, const char** op_name) { + CustomPartitioner part = Registry::get()->get(part_idx); + *strategy = part.strategies[stg_idx]; + *fn = part.supportedOps[stg_idx]; + *op_name = part.op_names[stg_idx]; } /*! \brief returns status of calling parse attributes function for operator from library */ diff --git a/src/c_api/c_api.cc b/src/c_api/c_api.cc index ca0fef2adb04..279c43daf7ec 100644 --- a/src/c_api/c_api.cc +++ b/src/c_api/c_api.cc @@ -698,32 +698,44 @@ int MXLoadLib(const char *path) { * Get all custom partitioners implementation from custom library * loop and register each partitioner in the library to NNVM */ + partRegGetCount_t partRegGetCount = get_func(lib, + const_cast(MXLIB_PARTREGGETCOUNT_STR)); partRegGet_t partRegGet = get_func(lib, const_cast(MXLIB_PARTREGGET_STR)); for (int i = 0; i < numParts; i++) { + int count=0; const char* name; - // function pointers holding implementation from custom library - supportedOps_t supportedOps_fp = nullptr; - // name of subgraph op - const char* op_name = nullptr; - - // get custom partitioner implemenation from the dynamic library - partRegGet(i, &name, &supportedOps_fp, &op_name); - - // validate custom partitioner functions from the dynamic library - CHECK(supportedOps_fp != nullptr) << "Error loading '" << name - << "' custom partitioner, supportedOps function was not set."; - CHECK(op_name != nullptr) << "Error loading '" << name - << "' custom partitioner, subgraphOp was not set."; - - LOG(INFO) << "\tPartitioner[" << i << "] " << name << " subgraphOp: '" << op_name << "'"; + // get custom partitioner strategy count from the dynamic library + partRegGetCount(i,&count, &name); + CHECK(count > 0) << "Error loading '" << name + << "' custom partitioner, no strategies defined"; std::string name_str(name); - + LOG(INFO) << "\tPartitioner[" << i << "] " << name; // MXNET_REGISTER_SUBGRAPH_BACKEND(customBackend); mxnet::op::SubgraphBackendRegistry::Get()->__REGISTER_BACKEND__(name); - // MXNET_REGISTER_SUBGRAPH_PROPERTY(customBackend, CustomSubgraphProperty); - mxnet::op::SubgraphBackendRegistry::Get()->__REGISTER_CUSTOM_PROPERTY__(name, + + for (int j = 0; j < count; j++) { + const char* strategy; + // function pointers holding implementation from custom library + supportedOps_t supportedOps_fp = nullptr; + // name of subgraph op + const char* op_name = nullptr; + + // get custom partitioner strategy from the dynamic library + partRegGet(i, j, &strategy, &supportedOps_fp, &op_name); + // validate custom partitioner functions from the dynamic library + CHECK(supportedOps_fp != nullptr) << "Error loading '" << name + << "' custom partitioner strategy '" << strategy + << "', supportedOps function was not set."; + std::string strategy_str(strategy); + std::string op_name_str(op_name); + LOG(INFO) << "\t\tStrategy[" << j << "] " << strategy_str + << " subgraphOp: '" << op_name_str << "'"; + + // MXNET_REGISTER_SUBGRAPH_PROPERTY(customBackend, CustomSubgraphProperty); + mxnet::op::SubgraphBackendRegistry::Get()->__REGISTER_CUSTOM_PROPERTY__(name_str, std::make_shared( - name_str, callSupportedOps, supportedOps_fp, op_name)); + strategy_str, callSupportedOps, supportedOps_fp, op_name_str)); + } } API_END(); } diff --git a/src/operator/subgraph/partitioner/custom_subgraph_property.h b/src/operator/subgraph/partitioner/custom_subgraph_property.h index cbb980311cfe..3a362e3c05d0 100644 --- a/src/operator/subgraph/partitioner/custom_subgraph_property.h +++ b/src/operator/subgraph/partitioner/custom_subgraph_property.h @@ -62,7 +62,7 @@ class CustomContainOpSelector: public SubgraphSelector { */ class CustomSubgraphProperty: public SubgraphProperty { public: - CustomSubgraphProperty() : + CustomSubgraphProperty() : callSupportedOps_(nullptr), supportedOps_(nullptr), subgraph_op_name("error"), @@ -93,42 +93,42 @@ class CustomSubgraphProperty: public SubgraphProperty { // set shape attrs for each node in the graph if (g.HasAttr("shape")) { mxnet::ShapeVector shapes = g.GetAttr("shape"); - for (int i = 0; i(indexed_graph[i].source); mxnet::TShape shape = shapes[i]; std::stringstream ss; ss << shape; - node->attrs.dict["shape"]=ss.str(); + node->attrs.dict["shape"] = ss.str(); } } // set dtype attrs for each node in the graph if (g.HasAttr("dtype")) { std::vector dtypes = g.GetAttr >("dtype"); - for (int i = 0; i(indexed_graph[i].source); int dtype = dtypes[i]; std::stringstream ss; ss << dtype; - node->attrs.dict["dtype"]=ss.str(); + node->attrs.dict["dtype"] = ss.str(); } } - - CHECK(supportedOps_ == nullptr) + + CHECK(supportedOps_ != nullptr) << "supportedOps_ is null for " << subgraphProp << std::endl; - CHECK(callSupportedOps_ == nullptr) + CHECK(callSupportedOps_ != nullptr) << "callSupportedOps_ is null for " << subgraphProp << std::endl; - + std::string subgraph_json = nnvm::pass::SaveJSON(graph); std::vector supportedNodeIDs(indexed_graph.num_nodes(), 0); const char* json = subgraph_json.c_str(); int *ids = supportedNodeIDs.data(); - + CHECK(callSupportedOps_(supportedOps_, json, supportedNodeIDs.size(), ids)) << "Error calling supportedOps for '" << subgraphProp << "'"; const auto& idx = g.indexed_graph(); // loop and add node names for each supported node ID - for (int i = 0; i < supportedNodeIDs.size(); i++) { + for (unsigned i = 0; i < supportedNodeIDs.size(); i++) { if (supportedNodeIDs[i]) { supportedNodes.push_back(idx[i].source->attrs.name); } From 901d308f02cb1856eecff51563b73f0cb70355f6 Mon Sep 17 00:00:00 2001 From: samskalicky Date: Sun, 15 Dec 2019 08:24:14 +0000 Subject: [PATCH 19/50] fixed whitespace --- src/c_api/c_api.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/c_api/c_api.cc b/src/c_api/c_api.cc index 279c43daf7ec..c1fc99bb8781 100644 --- a/src/c_api/c_api.cc +++ b/src/c_api/c_api.cc @@ -702,10 +702,10 @@ int MXLoadLib(const char *path) { const_cast(MXLIB_PARTREGGETCOUNT_STR)); partRegGet_t partRegGet = get_func(lib, const_cast(MXLIB_PARTREGGET_STR)); for (int i = 0; i < numParts; i++) { - int count=0; + int count = 0; const char* name; // get custom partitioner strategy count from the dynamic library - partRegGetCount(i,&count, &name); + partRegGetCount(i, &count, &name); CHECK(count > 0) << "Error loading '" << name << "' custom partitioner, no strategies defined"; std::string name_str(name); From 9b160d63c5e60cb4acf6cd6fdf8a1849351ed323 Mon Sep 17 00:00:00 2001 From: samskalicky Date: Mon, 16 Dec 2019 02:47:13 +0000 Subject: [PATCH 20/50] changed vector to unordered_set for performance --- .../partitioner/custom_subgraph_property.h | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/operator/subgraph/partitioner/custom_subgraph_property.h b/src/operator/subgraph/partitioner/custom_subgraph_property.h index 3a362e3c05d0..fbad54b0c0ac 100644 --- a/src/operator/subgraph/partitioner/custom_subgraph_property.h +++ b/src/operator/subgraph/partitioner/custom_subgraph_property.h @@ -37,21 +37,18 @@ namespace op { */ class CustomContainOpSelector: public SubgraphSelector { public: - explicit CustomContainOpSelector(std::vector supportedNodes) : + explicit CustomContainOpSelector(std::unordered_set supportedNodes) : supportedNodes_(supportedNodes) {} virtual bool Select(const nnvm::Node &n) { - return std::find(supportedNodes_.begin(), supportedNodes_.end(), - n.attrs.name) != supportedNodes_.end(); + return supportedNodes_.count(n.attrs.name) > 0; } virtual bool SelectInput(const nnvm::Node &n, const nnvm::Node &new_node) { - return std::find(supportedNodes_.begin(), supportedNodes_.end(), - new_node.attrs.name) != supportedNodes_.end(); + return supportedNodes_.count(new_node.attrs.name) > 0; } virtual bool SelectOutput(const nnvm::Node &n, const nnvm::Node &new_node) { - return std::find(supportedNodes_.begin(), supportedNodes_.end(), - new_node.attrs.name) != supportedNodes_.end(); + return supportedNodes_.count(new_node.attrs.name) > 0; } - std::vector supportedNodes_; + std::unordered_set supportedNodes_; }; /* @@ -130,7 +127,7 @@ class CustomSubgraphProperty: public SubgraphProperty { // loop and add node names for each supported node ID for (unsigned i = 0; i < supportedNodeIDs.size(); i++) { if (supportedNodeIDs[i]) { - supportedNodes.push_back(idx[i].source->attrs.name); + supportedNodes.insert(idx[i].source->attrs.name); } } } @@ -150,7 +147,7 @@ class CustomSubgraphProperty: public SubgraphProperty { partCallSupportedOps_t callSupportedOps_; supportedOps_t supportedOps_; - std::vector supportedNodes; + std::unordered_set supportedNodes; std::string subgraph_op_name; std::string subgraphProp; }; From bd79319574b0aa7a9ef6baeccf324f8b3513b81d Mon Sep 17 00:00:00 2001 From: samskalicky Date: Tue, 17 Dec 2019 08:39:51 +0000 Subject: [PATCH 21/50] fixed clearing supportedNodes set after multiple calls Added example executor in library and implementations for ops --- .../extensions/lib_subgraph/subgraph_lib.cc | 136 +++++++++++++++--- .../extensions/lib_subgraph/test_subgraph.py | 21 +-- .../partitioner/custom_subgraph_property.h | 2 + 3 files changed, 133 insertions(+), 26 deletions(-) diff --git a/example/extensions/lib_subgraph/subgraph_lib.cc b/example/extensions/lib_subgraph/subgraph_lib.cc index c66924cd7181..f975f2774423 100644 --- a/example/extensions/lib_subgraph/subgraph_lib.cc +++ b/example/extensions/lib_subgraph/subgraph_lib.cc @@ -61,6 +61,110 @@ MXReturnValue inferShape(std::map attrs, return MX_SUCCESS; } +/* function to execute log operator on floats */ +void myLog(MXTensor &in, MXTensor &out) { + float* inp = in.data(); + float* outp = out.data(); + for (int i = 0; i < in.size(); i++) { + outp[i] = logf(inp[i]); + } +} +/* function to execute exp operator on floats */ +void myExp(MXTensor &in, MXTensor &out) { + float* inp = in.data(); + float* outp =out.data(); + for (int i = 0; i < in.size(); i++) { + outp[i] = expf(inp[i]); + } +} + +/* function to execute ops in subgraph + * In MXNet, subgraphs are sorted in topological order + * so all we need to do is go through the ops in order + * and execute each op. + */ +MXReturnValue myExecutor(std::vector inputs, + std::vector outputs, + std::string subgraph_sym) { + std::cout << "Info: subgraph symbol is: " << std::endl; + std::cout << subgraph_sym << std::endl; + + // convert json string to json object + JsonParser parser; + JsonVal json_val = parser.parse_to_json(subgraph_sym); + // get nodes list + JsonVal nodes = json_val.map[JsonVal("nodes")]; + //counter for inputs + int input_cnt = 0; + // temporary tensor storage + std::vector data; + // track memory allocations to free later + std::vector to_free; + + // loop over nodes + for(int i=0; i(); + float *res_data = result.data(); + // loop and copy data + for (int i = 0; i < result.size(); i++) { + out_data[i] = res_data[i]; + } + } + + // free allocated temporary storage + for (void* ptr : to_free) { + free(ptr); + } + + return MX_SUCCESS; +} + class MyStatefulOp : public CustomStatefulOp { public: explicit MyStatefulOp(std::string sym) : subgraph_sym(sym) {} @@ -68,15 +172,7 @@ class MyStatefulOp : public CustomStatefulOp { MXReturnValue Forward(std::vector inputs, std::vector outputs, OpResource op_res) { - std::cout << "Info: subgraph symbol is: " << std::endl; - std::cout << subgraph_sym << std::endl; - float* in_data = inputs[0].data(); - float* out_data = outputs[0].data(); - std::cout << "Info: output is: " << std::endl; - for (int i = 0; i < inputs[0].size(); i++) { - out_data[i] = in_data[i]; - } - return MX_SUCCESS; + return myExecutor(inputs, outputs, subgraph_sym); } private: @@ -120,22 +216,26 @@ MXReturnValue mySupportedOps(std::string json, JsonVal op = node.map[JsonVal("op")]; //get shape/type if available - std::string shape,dtype; + std::string shape; + int dtype = -1; if(node.map.find(JsonVal("attrs")) != node.map.end()) { JsonVal attrs = node.map[JsonVal("attrs")]; if(attrs.map.find(JsonVal("shape")) != attrs.map.end()) shape = attrs.map[JsonVal("shape")].str; if(attrs.map.find(JsonVal("dtype")) != attrs.map.end()) - dtype = attrs.map[JsonVal("dtype")].str; + dtype = std::stoi(attrs.map[JsonVal("dtype")].str); } - - //check if op is in whitelist - if(std::find(op_names.begin(),op_names.end(),op.str.c_str()) != op_names.end()) { - std::cout << op.str << " : " << shape << " " << dtype << std::endl; - // found op in whitelist, set value to 1 to include op in subgraph - ids[i]=1; + //check if op dtype is float + std::cout << "dtype: " << dtype << " " << ids[i] <>& options_map) { std::cout << "PrePartition" << std::endl; + // clear supportedNodes to remove state from previous calls + supportedNodes.clear(); // remove all graph attrs, some cannot be saved to json nnvm::Graph graph = std::move(g); From 1e41f8ec03fa6a2817d10798c66d626a7926fd1a Mon Sep 17 00:00:00 2001 From: samskalicky Date: Tue, 17 Dec 2019 16:58:28 +0000 Subject: [PATCH 22/50] changed types to int64_t for tensor sizes, fixed indent --- example/extensions/lib_subgraph/subgraph_lib.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/example/extensions/lib_subgraph/subgraph_lib.cc b/example/extensions/lib_subgraph/subgraph_lib.cc index f975f2774423..5ab116c99890 100644 --- a/example/extensions/lib_subgraph/subgraph_lib.cc +++ b/example/extensions/lib_subgraph/subgraph_lib.cc @@ -65,7 +65,7 @@ MXReturnValue inferShape(std::map attrs, void myLog(MXTensor &in, MXTensor &out) { float* inp = in.data(); float* outp = out.data(); - for (int i = 0; i < in.size(); i++) { + for (int64_t i = 0; i < in.size(); i++) { outp[i] = logf(inp[i]); } } @@ -73,7 +73,7 @@ void myLog(MXTensor &in, MXTensor &out) { void myExp(MXTensor &in, MXTensor &out) { float* inp = in.data(); float* outp =out.data(); - for (int i = 0; i < in.size(); i++) { + for (int64_t i = 0; i < in.size(); i++) { outp[i] = expf(inp[i]); } } @@ -152,7 +152,7 @@ MXReturnValue myExecutor(std::vector inputs, float *out_data = out.data(); float *res_data = result.data(); // loop and copy data - for (int i = 0; i < result.size(); i++) { + for (int64_t i = 0; i < result.size(); i++) { out_data[i] = res_data[i]; } } @@ -202,8 +202,8 @@ REGISTER_OP(_custom_subgraph_op) const std::vector op_names({"exp","log"}); MXReturnValue mySupportedOps(std::string json, - const int num_ids, - int *ids) { + const int num_ids, + int *ids) { //convert json string to json object JsonParser parser; JsonVal json_val = parser.parse_to_json(json); From dd2de0a616ef2b675a832e6ec4722c7d5d03063e Mon Sep 17 00:00:00 2001 From: samskalicky Date: Wed, 18 Dec 2019 06:41:29 +0000 Subject: [PATCH 23/50] added support for passing options to supportedOps --- .../extensions/lib_subgraph/subgraph_lib.cc | 6 +++++- .../extensions/lib_subgraph/test_subgraph.py | 2 +- include/mxnet/lib_api.h | 19 ++++++++++++------- .../partitioner/custom_subgraph_property.h | 9 ++++++++- 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/example/extensions/lib_subgraph/subgraph_lib.cc b/example/extensions/lib_subgraph/subgraph_lib.cc index 5ab116c99890..71b6cdc33a4d 100644 --- a/example/extensions/lib_subgraph/subgraph_lib.cc +++ b/example/extensions/lib_subgraph/subgraph_lib.cc @@ -203,7 +203,11 @@ const std::vector op_names({"exp","log"}); MXReturnValue mySupportedOps(std::string json, const int num_ids, - int *ids) { + int *ids, + std::map options) { + for (auto kv : options) { + std::cout << "option: " << kv.first << " ==> " << kv.second << std::endl; + } //convert json string to json object JsonParser parser; JsonVal json_val = parser.parse_to_json(json); diff --git a/example/extensions/lib_subgraph/test_subgraph.py b/example/extensions/lib_subgraph/test_subgraph.py index 0b52d7dc3d63..640cf70ac99f 100644 --- a/example/extensions/lib_subgraph/test_subgraph.py +++ b/example/extensions/lib_subgraph/test_subgraph.py @@ -54,7 +54,7 @@ print(out2) # without propogating shapes/types -mysym3 = sym.optimize_for("myProp") +mysym3 = sym.optimize_for("myProp", myOpt='yello') exe3 = mysym3.bind(ctx=mx.cpu(), args={'a':mx.nd.ones((3,2)), 'b':mx.nd.ones((3,2))}) out3 = exe3.forward() print(out3) diff --git a/include/mxnet/lib_api.h b/include/mxnet/lib_api.h index 4b11e504baa6..2f610e6835d3 100644 --- a/include/mxnet/lib_api.h +++ b/include/mxnet/lib_api.h @@ -585,9 +585,8 @@ class CustomOp { }; /*! \brief Custom Subgraph Create function template */ -typedef MXReturnValue (*supportedOps_t)(std::string, - const int, - int*); +typedef MXReturnValue (*supportedOps_t)(std::string, const int, int*, + std::map); /*! * \brief An abstract class for subgraph property @@ -751,7 +750,8 @@ typedef int (*partRegGet_t)(int, int, const char**, supportedOps_t*, const char**); #define MXLIB_PARTCALLSUPPORTEDOPS_STR "_partCallSupportedOps" -typedef int (*partCallSupportedOps_t)(supportedOps_t, const char*, const int, int *); +typedef int (*partCallSupportedOps_t)(supportedOps_t, const char*, const int, int *, + const char* const*, const char* const*, int); #define MXLIB_INITIALIZE_STR "initialize" typedef int (*initialize_t)(int); @@ -1095,10 +1095,15 @@ extern "C" { int #endif _partCallSupportedOps(supportedOps_t supportedOps, const char *json, - const int num_ids, - int *ids) { + const int num_ids, int *ids, const char* const* opt_keys, + const char* const* opt_vals, int num_opts) { std::string subgraph_json(json); - return supportedOps(subgraph_json, num_ids, ids); + // create map of attributes from list + std::map opts; + for (int i = 0; i < num_opts; i++) { + opts[std::string(opt_keys[i])] = std::string(opt_vals[i]); + } + return supportedOps(subgraph_json, num_ids, ids, opts); } /*! diff --git a/src/operator/subgraph/partitioner/custom_subgraph_property.h b/src/operator/subgraph/partitioner/custom_subgraph_property.h index b34c798bd432..5fbb7b2b7833 100644 --- a/src/operator/subgraph/partitioner/custom_subgraph_property.h +++ b/src/operator/subgraph/partitioner/custom_subgraph_property.h @@ -122,7 +122,14 @@ class CustomSubgraphProperty: public SubgraphProperty { const char* json = subgraph_json.c_str(); int *ids = supportedNodeIDs.data(); - CHECK(callSupportedOps_(supportedOps_, json, supportedNodeIDs.size(), ids)) + std::vector opt_keys, opt_vals; + for (auto kv : options_map) { + opt_keys.push_back(kv.first.c_str()); + opt_vals.push_back(kv.second.c_str()); + } + + CHECK(callSupportedOps_(supportedOps_, json, supportedNodeIDs.size(), ids, + opt_keys.data(), opt_vals.data(), opt_keys.size())) << "Error calling supportedOps for '" << subgraphProp << "'"; const auto& idx = g.indexed_graph(); From 7ea43306b7b8a711e88f25c4564f5958804417d7 Mon Sep 17 00:00:00 2001 From: samskalicky Date: Thu, 19 Dec 2019 07:45:00 +0000 Subject: [PATCH 24/50] new API to to review subgraphs after partitioning --- .../extensions/lib_subgraph/subgraph_lib.cc | 13 +++- .../extensions/lib_subgraph/test_subgraph.py | 4 +- include/mxnet/lib_api.h | 59 +++++++++++++++---- src/c_api/c_api.cc | 13 ++-- .../partitioner/custom_subgraph_property.h | 54 ++++++++++++----- 5 files changed, 106 insertions(+), 37 deletions(-) diff --git a/example/extensions/lib_subgraph/subgraph_lib.cc b/example/extensions/lib_subgraph/subgraph_lib.cc index 71b6cdc33a4d..91335a50191c 100644 --- a/example/extensions/lib_subgraph/subgraph_lib.cc +++ b/example/extensions/lib_subgraph/subgraph_lib.cc @@ -231,20 +231,27 @@ MXReturnValue mySupportedOps(std::string json, } //check if op dtype is float std::cout << "dtype: " << dtype << " " << ids[i] <= 10400) { diff --git a/example/extensions/lib_subgraph/test_subgraph.py b/example/extensions/lib_subgraph/test_subgraph.py index 640cf70ac99f..02f2889c4b9e 100644 --- a/example/extensions/lib_subgraph/test_subgraph.py +++ b/example/extensions/lib_subgraph/test_subgraph.py @@ -48,7 +48,9 @@ # with propogating shapes/types arg_array = [mx.nd.ones((3,2)), mx.nd.ones((3,2))] -mysym2 = sym.optimize_for("myProp", arg_array) +print(sym.tojson()) +mysym2 = sym.optimize_for("myProp") +print(mysym2.tojson()) exe2 = mysym2.bind(ctx=mx.cpu(), args={'a':mx.nd.ones((3,2)), 'b':mx.nd.ones((3,2))}) out2 = exe2.forward() print(out2) diff --git a/include/mxnet/lib_api.h b/include/mxnet/lib_api.h index 2f610e6835d3..1a80140a2c3a 100644 --- a/include/mxnet/lib_api.h +++ b/include/mxnet/lib_api.h @@ -585,8 +585,9 @@ class CustomOp { }; /*! \brief Custom Subgraph Create function template */ -typedef MXReturnValue (*supportedOps_t)(std::string, const int, int*, +typedef MXReturnValue (*supportedOps_t)(std::string, int, int*, std::map); +typedef MXReturnValue (*acceptSubgraph_t)(std::string, int, bool*); /*! * \brief An abstract class for subgraph property @@ -604,9 +605,21 @@ class CustomPartitioner { op_names.push_back(sg_name); return *this; } - + CustomPartitioner& setAcceptSubgraph(const char* prop_name, acceptSubgraph_t fn) { + accept_map[std::string(prop_name)] = fn; + return *this; + } + acceptSubgraph_t getAcceptSubgraph(int stg_id) { + std::string prop(strategies[stg_id]); + if (accept_map.find(prop) != accept_map.end()) + return accept_map[prop]; + else + return nullptr; + } + /*! \brief partitioner name */ const char* name; + std::map accept_map; /*! \brief strategy names */ std::vector strategies; /*! \brief supported ops function */ @@ -743,15 +756,19 @@ typedef int (*opCallFStatefulComp_t)(bool, void*, const int64_t**, int*, void**, typedef int (*partRegSize_t)(void); #define MXLIB_PARTREGGETCOUNT_STR "_partRegGetCount" -typedef int (*partRegGetCount_t)(int, int*, const char**); +typedef int (*partRegGetCount_t)(int, const char**); #define MXLIB_PARTREGGET_STR "_partRegGet" -typedef int (*partRegGet_t)(int, int, const char**, - supportedOps_t*, const char**); +typedef int (*partRegGet_t)(int, int, const char**, supportedOps_t*, + acceptSubgraph_t*, const char**); #define MXLIB_PARTCALLSUPPORTEDOPS_STR "_partCallSupportedOps" -typedef int (*partCallSupportedOps_t)(supportedOps_t, const char*, const int, int *, +typedef int (*partCallSupportedOps_t)(supportedOps_t, const char*, int, int *, const char* const*, const char* const*, int); +#define MXLIB_PARTCALLACCEPTSUBGRAPH_STR "_partCallAcceptSubgraph" +typedef int (*partCallAcceptSubgraph_t)(acceptSubgraph_t acceptSubgraph, + const char *json, int subgraph_id, + int *accept); #define MXLIB_INITIALIZE_STR "initialize" typedef int (*initialize_t)(int); @@ -1066,12 +1083,12 @@ extern "C" { #if defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__) __declspec(dllexport) void __cdecl #else - void + int #endif - _partRegGetCount(int idx, int* count, const char** name) { + _partRegGetCount(int idx, const char** name) { CustomPartitioner part = Registry::get()->get(idx); *name = part.name; - *count = part.strategies.size(); + return part.strategies.size(); } /*! \brief returns partitioner registration at specified index */ @@ -1080,12 +1097,13 @@ extern "C" { #else void #endif - _partRegGet(int part_idx, int stg_idx, const char** strategy, - supportedOps_t* fn, const char** op_name) { + _partRegGet(int part_idx, int stg_idx, const char** strategy, supportedOps_t* supportedOps, + acceptSubgraph_t* acceptSubgraph, const char** op_name) { CustomPartitioner part = Registry::get()->get(part_idx); *strategy = part.strategies[stg_idx]; - *fn = part.supportedOps[stg_idx]; + *supportedOps = part.supportedOps[stg_idx]; *op_name = part.op_names[stg_idx]; + *acceptSubgraph = part.getAcceptSubgraph(stg_idx); } /*! \brief returns status of calling parse attributes function for operator from library */ @@ -1095,7 +1113,7 @@ extern "C" { int #endif _partCallSupportedOps(supportedOps_t supportedOps, const char *json, - const int num_ids, int *ids, const char* const* opt_keys, + int num_ids, int *ids, const char* const* opt_keys, const char* const* opt_vals, int num_opts) { std::string subgraph_json(json); // create map of attributes from list @@ -1106,6 +1124,21 @@ extern "C" { return supportedOps(subgraph_json, num_ids, ids, opts); } + /*! \brief returns status of calling parse attributes function for operator from library */ +#if defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__) + __declspec(dllexport) int __cdecl +#else + int +#endif + _partCallAcceptSubgraph(acceptSubgraph_t acceptSubgraph, const char *json, + int subgraph_id, int *accept) { + std::string subgraph_json(json); + bool accept_bool=false; + MXReturnValue retval = acceptSubgraph(subgraph_json, subgraph_id, &accept_bool); + *accept = accept_bool; + return retval; + } + /*! * \brief Checks if the MXNet version is supported by the library. * If supported, initializes the library. diff --git a/src/c_api/c_api.cc b/src/c_api/c_api.cc index 96de0000c0ce..0c64bdcd1f8d 100644 --- a/src/c_api/c_api.cc +++ b/src/c_api/c_api.cc @@ -146,6 +146,10 @@ int MXLoadLib(const char *path) { partCallSupportedOps_t callSupportedOps = get_func(lib, const_cast(MXLIB_PARTCALLSUPPORTEDOPS_STR)); + + partCallAcceptSubgraph_t callAcceptSubgraph = + get_func(lib, const_cast(MXLIB_PARTCALLACCEPTSUBGRAPH_STR)); + // get number of operators registered in the library opRegSize_t opRegSize = get_func(lib, const_cast(MXLIB_OPREGSIZE_STR)); int numOps = opRegSize(); @@ -702,10 +706,9 @@ int MXLoadLib(const char *path) { const_cast(MXLIB_PARTREGGETCOUNT_STR)); partRegGet_t partRegGet = get_func(lib, const_cast(MXLIB_PARTREGGET_STR)); for (int i = 0; i < numParts; i++) { - int count = 0; const char* name; // get custom partitioner strategy count from the dynamic library - partRegGetCount(i, &count, &name); + int count = partRegGetCount(i, &name); CHECK(count > 0) << "Error loading '" << name << "' custom partitioner, no strategies defined"; std::string name_str(name); @@ -717,11 +720,12 @@ int MXLoadLib(const char *path) { const char* strategy; // function pointers holding implementation from custom library supportedOps_t supportedOps_fp = nullptr; + acceptSubgraph_t acceptSubgraph_fp = nullptr; // name of subgraph op const char* op_name = nullptr; // get custom partitioner strategy from the dynamic library - partRegGet(i, j, &strategy, &supportedOps_fp, &op_name); + partRegGet(i, j, &strategy, &supportedOps_fp, &acceptSubgraph_fp, &op_name); // validate custom partitioner functions from the dynamic library CHECK(supportedOps_fp != nullptr) << "Error loading '" << name << "' custom partitioner strategy '" << strategy @@ -734,7 +738,8 @@ int MXLoadLib(const char *path) { // MXNET_REGISTER_SUBGRAPH_PROPERTY(customBackend, CustomSubgraphProperty); mxnet::op::SubgraphBackendRegistry::Get()->__REGISTER_CUSTOM_PROPERTY__(name_str, std::make_shared( - strategy_str, callSupportedOps, supportedOps_fp, op_name_str)); + strategy_str, callSupportedOps, supportedOps_fp, + callAcceptSubgraph, acceptSubgraph_fp, op_name_str)); } } API_END(); diff --git a/src/operator/subgraph/partitioner/custom_subgraph_property.h b/src/operator/subgraph/partitioner/custom_subgraph_property.h index 5fbb7b2b7833..c5be54fbb399 100644 --- a/src/operator/subgraph/partitioner/custom_subgraph_property.h +++ b/src/operator/subgraph/partitioner/custom_subgraph_property.h @@ -59,19 +59,25 @@ class CustomContainOpSelector: public SubgraphSelector { */ class CustomSubgraphProperty: public SubgraphProperty { public: - CustomSubgraphProperty() : + CustomSubgraphProperty() : + subgraphProp("error"), callSupportedOps_(nullptr), supportedOps_(nullptr), - subgraph_op_name("error"), - subgraphProp("error") {} + callAcceptSubgraph_(nullptr), + acceptSubgraph_(nullptr), + subgraph_op_name("error") {} CustomSubgraphProperty(std::string subgraphProp_name, - partCallSupportedOps_t callSupportedOps, - supportedOps_t supportedOps, - std::string op_name) : - callSupportedOps_(callSupportedOps), - supportedOps_(supportedOps), - subgraph_op_name(op_name), - subgraphProp(subgraphProp_name) {} + partCallSupportedOps_t callSupportedOps, + supportedOps_t supportedOps, + partCallAcceptSubgraph_t callAcceptSubgraph, + acceptSubgraph_t acceptSubgraph, + std::string op_name) : + subgraphProp(subgraphProp_name), + callSupportedOps_(callSupportedOps), + supportedOps_(supportedOps), + callAcceptSubgraph_(callAcceptSubgraph), + acceptSubgraph_(acceptSubgraph), + subgraph_op_name(op_name) {} // create custom subgraph property static SubgraphPropertyPtr Create() { @@ -143,22 +149,38 @@ class CustomSubgraphProperty: public SubgraphProperty { // override CreateSubgraphNode virtual nnvm::NodePtr CreateSubgraphNode(const nnvm::Symbol &sym, const int subgraph_id = 0) const { - nnvm::NodePtr n = nnvm::Node::Create(); - n->attrs.op = Op::Get(subgraph_op_name); - n->attrs.name = "_op" + std::to_string(subgraph_id); - n->attrs.subgraphs.push_back(std::make_shared(sym)); - return n; + int accept = 1; + if (acceptSubgraph_) { + nnvm::Graph g; + g.outputs = sym.outputs; + std::string subgraph_json = nnvm::pass::SaveJSON(g); + CHECK(callAcceptSubgraph_(acceptSubgraph_, subgraph_json.c_str(), + subgraph_id, &accept)) + << "Error calling acceptSubgraph for '" << subgraphProp << "'"; + + } + if (accept) { + nnvm::NodePtr n = nnvm::Node::Create(); + n->attrs.op = Op::Get(subgraph_op_name); + n->attrs.name = "_op" + std::to_string(subgraph_id); + n->attrs.subgraphs.push_back(std::make_shared(sym)); + return n; + } else { + return NULL; + } } // override CreateSubgraphSelector virtual SubgraphSelectorPtr CreateSubgraphSelector() const { return std::make_shared(supportedNodes); } + std::string subgraphProp; partCallSupportedOps_t callSupportedOps_; supportedOps_t supportedOps_; + partCallAcceptSubgraph_t callAcceptSubgraph_; + acceptSubgraph_t acceptSubgraph_; std::unordered_set supportedNodes; std::string subgraph_op_name; - std::string subgraphProp; }; } // namespace op } // namespace mxnet From b4b4862f5176efad01626ccda74f3c1d181166d4 Mon Sep 17 00:00:00 2001 From: samskalicky Date: Fri, 27 Dec 2019 22:46:07 +0000 Subject: [PATCH 25/50] added function to revert subgraphing if rejected by subgraph prop --- .../extensions/lib_subgraph/subgraph_lib.cc | 22 ++++++++++++++----- .../extensions/lib_subgraph/test_subgraph.py | 13 +++++++---- include/mxnet/lib_api.h | 16 ++++++++++---- src/operator/subgraph/build_subgraph.cc | 14 ++++++++++++ .../partitioner/custom_subgraph_property.h | 18 ++++++++++----- 5 files changed, 64 insertions(+), 19 deletions(-) diff --git a/example/extensions/lib_subgraph/subgraph_lib.cc b/example/extensions/lib_subgraph/subgraph_lib.cc index 91335a50191c..ac2cc5089651 100644 --- a/example/extensions/lib_subgraph/subgraph_lib.cc +++ b/example/extensions/lib_subgraph/subgraph_lib.cc @@ -229,23 +229,33 @@ MXReturnValue mySupportedOps(std::string json, if(attrs.map.find(JsonVal("dtype")) != attrs.map.end()) dtype = std::stoi(attrs.map[JsonVal("dtype")].str); } + //check if op dtype is float - std::cout << "dtype: " << dtype << " " << ids[i] < options) { std::cout << "myAcceptSubgraph" << std::endl; - *accept = true; + for (auto kv : options) { + std::cout << "option: " << kv.first << " ==> " << kv.second << std::endl; + } + if(options.find("reject") != options.end() && + options["reject"].compare("True") == 0) { + *accept = false; + std::cout << "rejecting subgraph" << std::endl; + } else { + *accept = true; + std::cout << "accepting subgraph" << std::endl; + } return MX_SUCCESS; } diff --git a/example/extensions/lib_subgraph/test_subgraph.py b/example/extensions/lib_subgraph/test_subgraph.py index 02f2889c4b9e..3041bb30cd9e 100644 --- a/example/extensions/lib_subgraph/test_subgraph.py +++ b/example/extensions/lib_subgraph/test_subgraph.py @@ -47,10 +47,15 @@ print(out) # with propogating shapes/types -arg_array = [mx.nd.ones((3,2)), mx.nd.ones((3,2))] -print(sym.tojson()) -mysym2 = sym.optimize_for("myProp") -print(mysym2.tojson()) +arg_array = [mx.nd.ones((3,2),dtype='float32'), mx.nd.ones((3,2),dtype='float32')] +mysym2 = sym.optimize_for("myProp",arg_array) +exe2 = mysym2.bind(ctx=mx.cpu(), args={'a':mx.nd.ones((3,2)), 'b':mx.nd.ones((3,2))}) +out2 = exe2.forward() +print(out2) + +# with propogating shapes/types, rejecting subgraph +arg_array = [mx.nd.ones((3,2),dtype='float32'), mx.nd.ones((3,2),dtype='float32')] +mysym2 = sym.optimize_for("myProp", arg_array, reject=True) exe2 = mysym2.bind(ctx=mx.cpu(), args={'a':mx.nd.ones((3,2)), 'b':mx.nd.ones((3,2))}) out2 = exe2.forward() print(out2) diff --git a/include/mxnet/lib_api.h b/include/mxnet/lib_api.h index 1a80140a2c3a..9ee2ba25d1f7 100644 --- a/include/mxnet/lib_api.h +++ b/include/mxnet/lib_api.h @@ -587,7 +587,8 @@ class CustomOp { /*! \brief Custom Subgraph Create function template */ typedef MXReturnValue (*supportedOps_t)(std::string, int, int*, std::map); -typedef MXReturnValue (*acceptSubgraph_t)(std::string, int, bool*); +typedef MXReturnValue (*acceptSubgraph_t)(std::string, int, bool*, + std::map); /*! * \brief An abstract class for subgraph property @@ -768,7 +769,8 @@ typedef int (*partCallSupportedOps_t)(supportedOps_t, const char*, int, int *, #define MXLIB_PARTCALLACCEPTSUBGRAPH_STR "_partCallAcceptSubgraph" typedef int (*partCallAcceptSubgraph_t)(acceptSubgraph_t acceptSubgraph, const char *json, int subgraph_id, - int *accept); + int *accept, const char* const*, + const char* const*, int); #define MXLIB_INITIALIZE_STR "initialize" typedef int (*initialize_t)(int); @@ -1131,10 +1133,16 @@ extern "C" { int #endif _partCallAcceptSubgraph(acceptSubgraph_t acceptSubgraph, const char *json, - int subgraph_id, int *accept) { + int subgraph_id, int *accept, const char* const* opt_keys, + const char* const* opt_vals, int num_opts) { std::string subgraph_json(json); bool accept_bool=false; - MXReturnValue retval = acceptSubgraph(subgraph_json, subgraph_id, &accept_bool); + // create map of attributes from list + std::map opts; + for (int i = 0; i < num_opts; i++) { + opts[std::string(opt_keys[i])] = std::string(opt_vals[i]); + } + MXReturnValue retval = acceptSubgraph(subgraph_json, subgraph_id, &accept_bool, opts); *accept = accept_bool; return retval; } diff --git a/src/operator/subgraph/build_subgraph.cc b/src/operator/subgraph/build_subgraph.cc index 8e7617d57c44..eed93a137f50 100644 --- a/src/operator/subgraph/build_subgraph.cc +++ b/src/operator/subgraph/build_subgraph.cc @@ -563,6 +563,18 @@ void CutGraphInputs(const std::vector &input_entries, } } +/*! + * \brief This function reattaches the original input nodes that were cut + * by CutGraphInputs. + */ +void ReattachGraphInputs(const std::vector &input_entries, + std::vector *orig_entries) { + for (size_t i = 0; i < input_entries.size(); ++i) { + nnvm::NodeEntry *e = input_entries[i]; + *e = orig_entries->at(i); + } +} + /*! * \brief Replace a set of nodes belonging to the same subgraph with a subgrpah node * and keep the subgraph in the subgraph node. @@ -620,6 +632,8 @@ void CreateSubgraphNode(nnvm::Graph* g, sn->outputs[n.get()].push_back(i); } } + } else { + ReattachGraphInputs(input_entries, &orig_input_entries); } #if DEBUG_SUBGRAPH if (n) diff --git a/src/operator/subgraph/partitioner/custom_subgraph_property.h b/src/operator/subgraph/partitioner/custom_subgraph_property.h index c5be54fbb399..9c7965169363 100644 --- a/src/operator/subgraph/partitioner/custom_subgraph_property.h +++ b/src/operator/subgraph/partitioner/custom_subgraph_property.h @@ -128,14 +128,18 @@ class CustomSubgraphProperty: public SubgraphProperty { const char* json = subgraph_json.c_str(); int *ids = supportedNodeIDs.data(); - std::vector opt_keys, opt_vals; + //clear options from previous call + opt_keys_.clear(); + opt_vals_.clear(); + options_map_.clear(); for (auto kv : options_map) { - opt_keys.push_back(kv.first.c_str()); - opt_vals.push_back(kv.second.c_str()); + options_map_.push_back(kv); + opt_keys_.push_back(options_map_.back().first.c_str()); + opt_vals_.push_back(options_map_.back().second.c_str()); } CHECK(callSupportedOps_(supportedOps_, json, supportedNodeIDs.size(), ids, - opt_keys.data(), opt_vals.data(), opt_keys.size())) + opt_keys_.data(), opt_vals_.data(), opt_keys_.size())) << "Error calling supportedOps for '" << subgraphProp << "'"; const auto& idx = g.indexed_graph(); @@ -155,7 +159,8 @@ class CustomSubgraphProperty: public SubgraphProperty { g.outputs = sym.outputs; std::string subgraph_json = nnvm::pass::SaveJSON(g); CHECK(callAcceptSubgraph_(acceptSubgraph_, subgraph_json.c_str(), - subgraph_id, &accept)) + subgraph_id, &accept, opt_keys_.data(), + opt_vals_.data(), opt_keys_.size())) << "Error calling acceptSubgraph for '" << subgraphProp << "'"; } @@ -181,6 +186,9 @@ class CustomSubgraphProperty: public SubgraphProperty { acceptSubgraph_t acceptSubgraph_; std::unordered_set supportedNodes; std::string subgraph_op_name; + std::vector> options_map_; + std::vector opt_keys_, opt_vals_; + }; } // namespace op } // namespace mxnet From cb199cc190b61f75f8c1d14a6bface625659895a Mon Sep 17 00:00:00 2001 From: samskalicky Date: Sat, 28 Dec 2019 15:01:40 +0000 Subject: [PATCH 26/50] added infrastructure to test subgraph libs in the CI --- CMakeLists.txt | 5 ++ Makefile | 8 +-- ci/jenkins/Jenkins_steps.groovy | 14 +++--- tests/python/unittest/test_extensions.py | 62 ++++++++++++++++++++++++ 4 files changed, 79 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f0779abd06d4..7508ddd80410 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -718,7 +718,9 @@ else() endif() add_library(sample_lib SHARED ${CMAKE_CURRENT_SOURCE_DIR}/example/extensions/lib_custom_op/gemm_lib.cc) +add_library(subgraph_lib SHARED ${CMAKE_CURRENT_SOURCE_DIR}/example/extensions/lib_subgraph/subgraph_lib.cc) target_include_directories(sample_lib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include/mxnet) +target_include_directories(subgraph_lib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include/mxnet) set(MXNET_INSTALL_TARGETS mxnet) if(UNIX) # Create dummy file since we want an empty shared library before linking @@ -731,11 +733,14 @@ if(UNIX) target_link_libraries(mxnet PRIVATE mxnet_static) target_link_libraries(mxnet_static PUBLIC ${CMAKE_DL_LIBS}) target_compile_options(sample_lib PUBLIC -shared) + target_compile_options(subgraph_lib PUBLIC -shared) set_target_properties(mxnet_static PROPERTIES OUTPUT_NAME mxnet) else() add_library(mxnet SHARED ${SOURCE}) target_compile_options(sample_lib PUBLIC /LD) + target_compile_options(subgraph_lib PUBLIC /LD) set_target_properties(sample_lib PROPERTIES PREFIX "lib") + set_target_properties(subgraph_lib PROPERTIES PREFIX "lib") endif() if(USE_CUDA) diff --git a/Makefile b/Makefile index 1b858cc48671..9b07d16de70c 100644 --- a/Makefile +++ b/Makefile @@ -457,7 +457,7 @@ endif .PHONY: clean all extra-packages test lint clean_all rcpplint rcppexport roxygen\ cython2 cython3 cython cyclean -all: lib/libmxnet.a lib/libmxnet.so $(BIN) extra-packages sample_lib +all: lib/libmxnet.a lib/libmxnet.so $(BIN) extra-packages sample_lib subgraph_lib SRC = $(wildcard src/*/*/*/*.cc src/*/*/*.cc src/*/*.cc src/*.cc) OBJ = $(patsubst %.cc, build/%.o, $(SRC)) @@ -667,6 +667,8 @@ pylint: # sample lib for MXNet extension dynamically loading custom operator sample_lib: $(CXX) -shared -fPIC -std=c++11 example/extensions/lib_custom_op/gemm_lib.cc -o libsample_lib.so -I include/mxnet +subgraph_lib: + $(CXX) -shared -fPIC -std=c++11 example/extensions/lib_subgraph/subgraph_lib.cc -o libsubgraph_lib.so -I include/mxnet # Cython build cython: @@ -762,7 +764,7 @@ clean: rclean cyclean $(EXTRA_PACKAGES_CLEAN) cd $(NNVM_PATH); $(MAKE) clean; cd - cd $(TVM_PATH); $(MAKE) clean; cd - cd $(AMALGAMATION_PATH); $(MAKE) clean; cd - - $(RM) libsample_lib.so + $(RM) libsample_lib.so libsubgraph_lib.so $(RM) -r $(patsubst %, %/*.d, $(EXTRA_OPERATORS)) $(patsubst %, %/*/*.d, $(EXTRA_OPERATORS)) $(RM) -r $(patsubst %, %/*.o, $(EXTRA_OPERATORS)) $(patsubst %, %/*/*.o, $(EXTRA_OPERATORS)) else @@ -774,7 +776,7 @@ clean: rclean mkldnn_clean cyclean testclean $(EXTRA_PACKAGES_CLEAN) cd $(NNVM_PATH); $(MAKE) clean; cd - cd $(TVM_PATH); $(MAKE) clean; cd - cd $(AMALGAMATION_PATH); $(MAKE) clean; cd - - $(RM) libsample_lib.so + $(RM) libsample_lib.so libsubgraph_lib.so endif clean_all: clean diff --git a/ci/jenkins/Jenkins_steps.groovy b/ci/jenkins/Jenkins_steps.groovy index 81479328f054..c697e1e58788 100644 --- a/ci/jenkins/Jenkins_steps.groovy +++ b/ci/jenkins/Jenkins_steps.groovy @@ -23,23 +23,23 @@ utils = load('ci/Jenkinsfile_utils.groovy') // mxnet libraries -mx_lib = 'lib/libmxnet.so, lib/libmxnet.a, lib/libtvm_runtime.so, lib/libtvmop.so, lib/tvmop.conf, libsample_lib.so, 3rdparty/dmlc-core/libdmlc.a, 3rdparty/tvm/nnvm/lib/libnnvm.a' -mx_lib_cython = 'lib/libmxnet.so, lib/libmxnet.a, lib/libtvm_runtime.so, lib/libtvmop.so, lib/tvmop.conf, libsample_lib.so, 3rdparty/dmlc-core/libdmlc.a, 3rdparty/tvm/nnvm/lib/libnnvm.a, python/mxnet/_cy2/*.so, python/mxnet/_cy3/*.so' +mx_lib = 'lib/libmxnet.so, lib/libmxnet.a, lib/libtvm_runtime.so, lib/libtvmop.so, lib/tvmop.conf, libsample_lib.so, libsubgraph_lib.so, 3rdparty/dmlc-core/libdmlc.a, 3rdparty/tvm/nnvm/lib/libnnvm.a' +mx_lib_cython = 'lib/libmxnet.so, lib/libmxnet.a, lib/libtvm_runtime.so, lib/libtvmop.so, lib/tvmop.conf, libsample_lib.so, libsubgraph_lib.so, 3rdparty/dmlc-core/libdmlc.a, 3rdparty/tvm/nnvm/lib/libnnvm.a, python/mxnet/_cy2/*.so, python/mxnet/_cy3/*.so' // Python wheels mx_pip = 'build/*.whl' // mxnet cmake libraries, in cmake builds we do not produce a libnvvm static library by default. mx_cmake_lib = 'build/libmxnet.so, build/libmxnet.a, build/3rdparty/tvm/libtvm_runtime.so, build/libtvmop.so, build/tvmop.conf, build/3rdparty/dmlc-core/libdmlc.a, build/tests/mxnet_unit_tests, build/3rdparty/openmp/runtime/src/libomp.so' -mx_cmake_lib_no_tvm_op = 'build/libmxnet.so, build/libmxnet.a, build/libsample_lib.so, build/3rdparty/dmlc-core/libdmlc.a, build/tests/mxnet_unit_tests, build/3rdparty/openmp/runtime/src/libomp.so' +mx_cmake_lib_no_tvm_op = 'build/libmxnet.so, build/libmxnet.a, build/libsample_lib.so, build/libsubgraph_lib.so, build/3rdparty/dmlc-core/libdmlc.a, build/tests/mxnet_unit_tests, build/3rdparty/openmp/runtime/src/libomp.so' mx_cmake_lib_cython = 'build/libmxnet.so, build/libmxnet.a, build/3rdparty/tvm/libtvm_runtime.so, build/libtvmop.so, build/tvmop.conf, build/3rdparty/dmlc-core/libdmlc.a, build/tests/mxnet_unit_tests, build/3rdparty/openmp/runtime/src/libomp.so, python/mxnet/_cy2/*.so, python/mxnet/_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/libmxnet.a, build/3rdparty/tvm/libtvm_runtime.so, build/libtvmop.so, build/tvmop.conf, build/libsample_lib.so, build/3rdparty/dmlc-core/libdmlc.a, build/tests/mxnet_unit_tests' +mx_cmake_lib_debug = 'build/libmxnet.so, build/libmxnet.a, build/3rdparty/tvm/libtvm_runtime.so, build/libtvmop.so, build/tvmop.conf, build/libsample_lib.so, build/libsubgraph_lib.so, build/3rdparty/dmlc-core/libdmlc.a, build/tests/mxnet_unit_tests' mx_cmake_mkldnn_lib = 'build/libmxnet.so, build/libmxnet.a, build/3rdparty/tvm/libtvm_runtime.so, build/libtvmop.so, build/tvmop.conf, build/3rdparty/dmlc-core/libdmlc.a, build/tests/mxnet_unit_tests, build/3rdparty/openmp/runtime/src/libomp.so' -mx_mkldnn_lib = 'lib/libmxnet.so, lib/libmxnet.a, lib/libtvm_runtime.so, lib/libtvmop.so, lib/tvmop.conf, libsample_lib.so, 3rdparty/dmlc-core/libdmlc.a, 3rdparty/tvm/nnvm/lib/libnnvm.a' +mx_mkldnn_lib = 'lib/libmxnet.so, lib/libmxnet.a, lib/libtvm_runtime.so, lib/libtvmop.so, lib/tvmop.conf, libsample_lib.so, libsubgraph_lib.so, 3rdparty/dmlc-core/libdmlc.a, 3rdparty/tvm/nnvm/lib/libnnvm.a' mx_tensorrt_lib = 'build/libmxnet.so, build/3rdparty/tvm/libtvm_runtime.so, build/libtvmop.so, build/tvmop.conf, lib/libnvonnxparser_runtime.so.0, lib/libnvonnxparser.so.0, lib/libonnx_proto.so, lib/libonnx.so' -mx_lib_cpp_examples = 'lib/libmxnet.so, lib/libmxnet.a, lib/libtvm_runtime.so, lib/libtvmop.so, lib/tvmop.conf, libsample_lib.so, 3rdparty/dmlc-core/libdmlc.a, 3rdparty/tvm/nnvm/lib/libnnvm.a, 3rdparty/ps-lite/build/libps.a, deps/lib/libprotobuf-lite.a, deps/lib/libzmq.a, build/cpp-package/example/*, python/mxnet/_cy2/*.so, python/mxnet/_cy3/*.so' -mx_lib_cpp_examples_no_tvm_op = 'lib/libmxnet.so, lib/libmxnet.a, libsample_lib.so, 3rdparty/dmlc-core/libdmlc.a, 3rdparty/tvm/nnvm/lib/libnnvm.a, 3rdparty/ps-lite/build/libps.a, deps/lib/libprotobuf-lite.a, deps/lib/libzmq.a, build/cpp-package/example/*, python/mxnet/_cy2/*.so, python/mxnet/_cy3/*.so' +mx_lib_cpp_examples = 'lib/libmxnet.so, lib/libmxnet.a, lib/libtvm_runtime.so, lib/libtvmop.so, lib/tvmop.conf, libsample_lib.so, libsubgraph_lib.so, 3rdparty/dmlc-core/libdmlc.a, 3rdparty/tvm/nnvm/lib/libnnvm.a, 3rdparty/ps-lite/build/libps.a, deps/lib/libprotobuf-lite.a, deps/lib/libzmq.a, build/cpp-package/example/*, python/mxnet/_cy2/*.so, python/mxnet/_cy3/*.so' +mx_lib_cpp_examples_no_tvm_op = 'lib/libmxnet.so, lib/libmxnet.a, libsample_lib.so, libsubgraph_lib.so, 3rdparty/dmlc-core/libdmlc.a, 3rdparty/tvm/nnvm/lib/libnnvm.a, 3rdparty/ps-lite/build/libps.a, deps/lib/libprotobuf-lite.a, deps/lib/libzmq.a, build/cpp-package/example/*, python/mxnet/_cy2/*.so, python/mxnet/_cy3/*.so' mx_lib_cpp_examples_cpu = 'build/libmxnet.so, build/3rdparty/tvm/libtvm_runtime.so, build/libtvmop.so, build/tvmop.conf, build/cpp-package/example/*' // Python unittest for CPU diff --git a/tests/python/unittest/test_extensions.py b/tests/python/unittest/test_extensions.py index cc7858dce0fd..fe57bac53027 100644 --- a/tests/python/unittest/test_extensions.py +++ b/tests/python/unittest/test_extensions.py @@ -84,3 +84,65 @@ def test_custom_op(): assert_almost_equal(in_grad_base[0].asnumpy(), in_grad1[0].asnumpy(), rtol=1e-3, atol=1e-3) assert_almost_equal(in_grad_base[0].asnumpy(), in_grad2[0].asnumpy(), rtol=1e-3, atol=1e-3) + +@unittest.skipIf(check_platform(), "not all machine types supported") +@unittest.skipIf(is_cd_run(), "continuous delivery run - ignoring test") +def test_subgraph(): + # possible places to find library file + if (os.name=='posix'): + lib = 'libsubgraph_lib.so' + if os.path.exists(lib): + # plain make build, when run in the CI + fname = lib + elif os.path.exists('build/'+lib): + # plain cmake build when run in the CI + fname = 'build/'+lib + else: + raise MXNetError("library %s not found " % lib) + elif (os.name=='nt'): + lib = 'libsubgraph_lib.dll' + if os.path.exists('windows_package\\lib\\'+lib): + # plain make build, when run in the CI + fname = 'windows_package\\lib\\'+lib + else: + # plain cmake build when run in the CI + raise MXNetError("library %s not found " % lib) + + fname = os.path.abspath(fname) + mx.library.load(fname) + + # test simple graph with add, exp and log operators, library supports exp/log + a = mx.sym.var('a') + b = mx.sym.var('b') + c = a + b + d = mx.sym.exp(c) + sym = mx.sym.log(d) + + # baseline - regular execution in MXNet + args = {'a':mx.nd.ones((3,2)), 'b':mx.nd.ones((3,2))} + exe = sym.bind(ctx=mx.cpu(), args=args) + out = exe.forward() + + # without propogating shapes/types, passing a custom option to subgraph prop "myOpt" + # should not create subgraph since subgraph prop requires type info + mysym1 = sym.optimize_for("myProp", myOpt='yello') + exe1 = mysym1.bind(ctx=mx.cpu(), args=args) + out1 = exe1.forward() + # check that result matches one executed by MXNet + assert_almost_equal(out[0].asnumpy(), out1[0].asnumpy(), rtol=1e-3, atol=1e-3) + + # with propogating shapes/types, rejecting subgraph + # this tests creating the subgraph and having the subgraph prop reject it + arg_array = [mx.nd.ones((3,2),dtype='float32'), mx.nd.ones((3,2),dtype='float32')] + mysym2 = sym.optimize_for("myProp", arg_array, reject=True) + exe2 = mysym2.bind(ctx=mx.cpu(), args=args) + out2 = exe2.forward() + # check that result matches one executed by MXNet + assert_almost_equal(out[0].asnumpy(), out2[0].asnumpy(), rtol=1e-3, atol=1e-3) + + # with propogating shapes/types + mysym3 = sym.optimize_for("myProp",arg_array) + exe3 = mysym3.bind(ctx=mx.cpu(), args=args) + out3 = exe3.forward() + # check that result matches one executed by MXNet + assert_almost_equal(out[0].asnumpy(), out3[0].asnumpy(), rtol=1e-3, atol=1e-3) From 241b591f8916dd0fbb2853558841ed1989512db6 Mon Sep 17 00:00:00 2001 From: samskalicky Date: Sat, 28 Dec 2019 16:56:46 +0000 Subject: [PATCH 27/50] fixed whitespace --- include/mxnet/lib_api.h | 4 ++-- .../subgraph/partitioner/custom_subgraph_property.h | 12 +++++------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/include/mxnet/lib_api.h b/include/mxnet/lib_api.h index 9ee2ba25d1f7..b162639d07d1 100644 --- a/include/mxnet/lib_api.h +++ b/include/mxnet/lib_api.h @@ -617,7 +617,7 @@ class CustomPartitioner { else return nullptr; } - + /*! \brief partitioner name */ const char* name; std::map accept_map; @@ -1136,7 +1136,7 @@ extern "C" { int subgraph_id, int *accept, const char* const* opt_keys, const char* const* opt_vals, int num_opts) { std::string subgraph_json(json); - bool accept_bool=false; + bool accept_bool = false; // create map of attributes from list std::map opts; for (int i = 0; i < num_opts; i++) { diff --git a/src/operator/subgraph/partitioner/custom_subgraph_property.h b/src/operator/subgraph/partitioner/custom_subgraph_property.h index 9c7965169363..d1a246207e72 100644 --- a/src/operator/subgraph/partitioner/custom_subgraph_property.h +++ b/src/operator/subgraph/partitioner/custom_subgraph_property.h @@ -59,8 +59,8 @@ class CustomContainOpSelector: public SubgraphSelector { */ class CustomSubgraphProperty: public SubgraphProperty { public: - CustomSubgraphProperty() : - subgraphProp("error"), + CustomSubgraphProperty() : + subgraphProp("error"), callSupportedOps_(nullptr), supportedOps_(nullptr), callAcceptSubgraph_(nullptr), @@ -128,16 +128,16 @@ class CustomSubgraphProperty: public SubgraphProperty { const char* json = subgraph_json.c_str(); int *ids = supportedNodeIDs.data(); - //clear options from previous call + // clear options from previous call opt_keys_.clear(); opt_vals_.clear(); options_map_.clear(); for (auto kv : options_map) { - options_map_.push_back(kv); + options_map_.push_back(kv); opt_keys_.push_back(options_map_.back().first.c_str()); opt_vals_.push_back(options_map_.back().second.c_str()); } - + CHECK(callSupportedOps_(supportedOps_, json, supportedNodeIDs.size(), ids, opt_keys_.data(), opt_vals_.data(), opt_keys_.size())) << "Error calling supportedOps for '" << subgraphProp << "'"; @@ -162,7 +162,6 @@ class CustomSubgraphProperty: public SubgraphProperty { subgraph_id, &accept, opt_keys_.data(), opt_vals_.data(), opt_keys_.size())) << "Error calling acceptSubgraph for '" << subgraphProp << "'"; - } if (accept) { nnvm::NodePtr n = nnvm::Node::Create(); @@ -188,7 +187,6 @@ class CustomSubgraphProperty: public SubgraphProperty { std::string subgraph_op_name; std::vector> options_map_; std::vector opt_keys_, opt_vals_; - }; } // namespace op } // namespace mxnet From 25b71fb9f5972ff8204f886b6c7bb5f14ac0d34a Mon Sep 17 00:00:00 2001 From: samskalicky Date: Sat, 28 Dec 2019 23:01:38 +0000 Subject: [PATCH 28/50] changed default context to CPU since GPU is not supported yet --- tests/python/unittest/test_extensions.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/python/unittest/test_extensions.py b/tests/python/unittest/test_extensions.py index fe57bac53027..715deb52325c 100644 --- a/tests/python/unittest/test_extensions.py +++ b/tests/python/unittest/test_extensions.py @@ -118,8 +118,11 @@ def test_subgraph(): d = mx.sym.exp(c) sym = mx.sym.log(d) + args = {'a':mx.nd.ones((3,2),ctx=mx.cpu()), 'b':mx.nd.ones((3,2),ctx=mx.cpu())} + arg_array = [mx.nd.ones((3,2),dtype='float32',ctx=mx.cpu()), + mx.nd.ones((3,2),dtype='float32'),ctx=mx.cpu()] + # baseline - regular execution in MXNet - args = {'a':mx.nd.ones((3,2)), 'b':mx.nd.ones((3,2))} exe = sym.bind(ctx=mx.cpu(), args=args) out = exe.forward() @@ -133,7 +136,6 @@ def test_subgraph(): # with propogating shapes/types, rejecting subgraph # this tests creating the subgraph and having the subgraph prop reject it - arg_array = [mx.nd.ones((3,2),dtype='float32'), mx.nd.ones((3,2),dtype='float32')] mysym2 = sym.optimize_for("myProp", arg_array, reject=True) exe2 = mysym2.bind(ctx=mx.cpu(), args=args) out2 = exe2.forward() From a53277d782546d36449de86aea3e7c33af3b48a2 Mon Sep 17 00:00:00 2001 From: samskalicky Date: Sun, 29 Dec 2019 00:24:54 +0000 Subject: [PATCH 29/50] fixed --- tests/python/unittest/test_extensions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/python/unittest/test_extensions.py b/tests/python/unittest/test_extensions.py index 715deb52325c..63b54d8516b4 100644 --- a/tests/python/unittest/test_extensions.py +++ b/tests/python/unittest/test_extensions.py @@ -120,7 +120,7 @@ def test_subgraph(): args = {'a':mx.nd.ones((3,2),ctx=mx.cpu()), 'b':mx.nd.ones((3,2),ctx=mx.cpu())} arg_array = [mx.nd.ones((3,2),dtype='float32',ctx=mx.cpu()), - mx.nd.ones((3,2),dtype='float32'),ctx=mx.cpu()] + mx.nd.ones((3,2),dtype='float32',ctx=mx.cpu())] # baseline - regular execution in MXNet exe = sym.bind(ctx=mx.cpu(), args=args) From 2fd7423be4ba3181cf417e81c5e3518ceb169da6 Mon Sep 17 00:00:00 2001 From: samskalicky Date: Sun, 29 Dec 2019 14:59:38 +0000 Subject: [PATCH 30/50] added include --- example/extensions/lib_subgraph/subgraph_lib.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/example/extensions/lib_subgraph/subgraph_lib.cc b/example/extensions/lib_subgraph/subgraph_lib.cc index ac2cc5089651..aae57c91f67b 100644 --- a/example/extensions/lib_subgraph/subgraph_lib.cc +++ b/example/extensions/lib_subgraph/subgraph_lib.cc @@ -23,6 +23,7 @@ * \brief subgraph operator implementation library file */ +#include #include #include #include "lib_api.h" From 8434bd8a1589e33185d18745d9ae158ad198f1f9 Mon Sep 17 00:00:00 2001 From: samskalicky Date: Sun, 29 Dec 2019 16:33:14 +0000 Subject: [PATCH 31/50] fixed return types --- include/mxnet/lib_api.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/mxnet/lib_api.h b/include/mxnet/lib_api.h index b162639d07d1..bc5f6070003b 100644 --- a/include/mxnet/lib_api.h +++ b/include/mxnet/lib_api.h @@ -707,10 +707,10 @@ class Registry { typedef int (*opRegSize_t)(void); #define MXLIB_OPREGGET_STR "_opRegGet" -typedef int (*opRegGet_t)(int, const char**, fcomp_t*, fcomp_t*, - parseAttrs_t*, inferType_t*, - inferShape_t*, mutateInputs_t*, - createOpState_t*); +typedef void (*opRegGet_t)(int, const char**, fcomp_t*, fcomp_t*, + parseAttrs_t*, inferType_t*, + inferShape_t*, mutateInputs_t*, + createOpState_t*); #define MXLIB_OPCALLFREE_STR "_opCallFree" typedef int (*opCallFree_t)(void*); @@ -760,7 +760,7 @@ typedef int (*partRegSize_t)(void); typedef int (*partRegGetCount_t)(int, const char**); #define MXLIB_PARTREGGET_STR "_partRegGet" -typedef int (*partRegGet_t)(int, int, const char**, supportedOps_t*, +typedef void (*partRegGet_t)(int, int, const char**, supportedOps_t*, acceptSubgraph_t*, const char**); #define MXLIB_PARTCALLSUPPORTEDOPS_STR "_partCallSupportedOps" @@ -1083,7 +1083,7 @@ extern "C" { /* returns number of strategies registered for partitioner * at specified index */ #if defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__) - __declspec(dllexport) void __cdecl + __declspec(dllexport) int __cdecl #else int #endif From 65012cda0af4c2cfb030949f42167ebe8e67b1f4 Mon Sep 17 00:00:00 2001 From: samskalicky Date: Mon, 30 Dec 2019 14:28:31 +0000 Subject: [PATCH 32/50] added memory free in error condition --- example/extensions/lib_subgraph/subgraph_lib.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/example/extensions/lib_subgraph/subgraph_lib.cc b/example/extensions/lib_subgraph/subgraph_lib.cc index aae57c91f67b..c43129cfbca9 100644 --- a/example/extensions/lib_subgraph/subgraph_lib.cc +++ b/example/extensions/lib_subgraph/subgraph_lib.cc @@ -138,6 +138,9 @@ MXReturnValue myExecutor(std::vector inputs, data.push_back(tmp); } else { std::cout << "Error! Unsupported op '" << op << "' found in myExecutor"; + // free allocated temporary storage + for (void* ptr : to_free) + free(ptr); return MX_FAIL; } } From d8cbfeb3b56f5d23e12db6886289daca00b21144 Mon Sep 17 00:00:00 2001 From: samskalicky Date: Mon, 30 Dec 2019 14:31:41 +0000 Subject: [PATCH 33/50] added clearer function comment --- src/operator/subgraph/build_subgraph.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/operator/subgraph/build_subgraph.cc b/src/operator/subgraph/build_subgraph.cc index eed93a137f50..9aab960866ee 100644 --- a/src/operator/subgraph/build_subgraph.cc +++ b/src/operator/subgraph/build_subgraph.cc @@ -565,7 +565,8 @@ void CutGraphInputs(const std::vector &input_entries, /*! * \brief This function reattaches the original input nodes that were cut - * by CutGraphInputs. + * by CutGraphInputs. This function is used when subgraphs are rejected, it + * reattaches the subgraph back to the main graph where it was cut earlier. */ void ReattachGraphInputs(const std::vector &input_entries, std::vector *orig_entries) { From 9ddf449522e7a1f46ed3e95b28248110880c62d9 Mon Sep 17 00:00:00 2001 From: samskalicky Date: Mon, 30 Dec 2019 14:34:39 +0000 Subject: [PATCH 34/50] added file comment --- .../subgraph/partitioner/custom_subgraph_property.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/operator/subgraph/partitioner/custom_subgraph_property.h b/src/operator/subgraph/partitioner/custom_subgraph_property.h index d1a246207e72..6275fcd37b86 100644 --- a/src/operator/subgraph/partitioner/custom_subgraph_property.h +++ b/src/operator/subgraph/partitioner/custom_subgraph_property.h @@ -17,6 +17,14 @@ * under the License. */ +/* + * This file contains an implementation of a subgraph property + * that interfaces between MXNet and custom subgraph properties + * created by users in external libraries. It does not implement + * any custom subgraphing logic itself, rather it calls APIs + * in the user's custom library to enable control of partitioning + */ + #ifndef MXNET_OPERATOR_SUBGRAPH_PARTITIONER_CUSTOM_SUBGRAPH_PROPERTY_H_ #define MXNET_OPERATOR_SUBGRAPH_PARTITIONER_CUSTOM_SUBGRAPH_PROPERTY_H_ From 17b7631f249378bc2e72049e869684a5bed18030 Mon Sep 17 00:00:00 2001 From: samskalicky Date: Mon, 30 Dec 2019 14:47:54 +0000 Subject: [PATCH 35/50] added more prints in example test for clarity --- example/extensions/lib_subgraph/test_subgraph.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/example/extensions/lib_subgraph/test_subgraph.py b/example/extensions/lib_subgraph/test_subgraph.py index 3041bb30cd9e..c85229da8d94 100644 --- a/example/extensions/lib_subgraph/test_subgraph.py +++ b/example/extensions/lib_subgraph/test_subgraph.py @@ -41,12 +41,16 @@ d = mx.sym.exp(c) sym = mx.sym.log(d) -#execute in MXNet +#execute in MXNet +print('-------------------------------') +print('Testing regular MXNet execution') exe = sym.bind(ctx=mx.cpu(), args={'a':mx.nd.ones((3,2)), 'b':mx.nd.ones((3,2))}) out = exe.forward() print(out) # with propogating shapes/types +print('-------------------------------') +print('Testing partitioning with shapes/types') arg_array = [mx.nd.ones((3,2),dtype='float32'), mx.nd.ones((3,2),dtype='float32')] mysym2 = sym.optimize_for("myProp",arg_array) exe2 = mysym2.bind(ctx=mx.cpu(), args={'a':mx.nd.ones((3,2)), 'b':mx.nd.ones((3,2))}) @@ -54,6 +58,8 @@ print(out2) # with propogating shapes/types, rejecting subgraph +print('-------------------------------') +print('Testing partitioning with shapes/types - rejecting subgraph') arg_array = [mx.nd.ones((3,2),dtype='float32'), mx.nd.ones((3,2),dtype='float32')] mysym2 = sym.optimize_for("myProp", arg_array, reject=True) exe2 = mysym2.bind(ctx=mx.cpu(), args={'a':mx.nd.ones((3,2)), 'b':mx.nd.ones((3,2))}) @@ -61,6 +67,8 @@ print(out2) # without propogating shapes/types +print('-------------------------------') +print('Testing partitioning without shapes/types') mysym3 = sym.optimize_for("myProp", myOpt='yello') exe3 = mysym3.bind(ctx=mx.cpu(), args={'a':mx.nd.ones((3,2)), 'b':mx.nd.ones((3,2))}) out3 = exe3.forward() From fc70db3f301cd4fe3612d0a0bd1dc31741962b01 Mon Sep 17 00:00:00 2001 From: samskalicky Date: Tue, 31 Dec 2019 02:11:14 +0000 Subject: [PATCH 36/50] enhancements from review --- example/extensions/lib_subgraph/subgraph_lib.cc | 5 ++--- include/mxnet/lib_api.h | 9 +++++---- .../subgraph/partitioner/custom_subgraph_property.h | 1 - 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/example/extensions/lib_subgraph/subgraph_lib.cc b/example/extensions/lib_subgraph/subgraph_lib.cc index c43129cfbca9..842c3407c0da 100644 --- a/example/extensions/lib_subgraph/subgraph_lib.cc +++ b/example/extensions/lib_subgraph/subgraph_lib.cc @@ -208,7 +208,7 @@ const std::vector op_names({"exp","log"}); MXReturnValue mySupportedOps(std::string json, const int num_ids, int *ids, - std::map options) { + std::unordered_map& options) { for (auto kv : options) { std::cout << "option: " << kv.first << " ==> " << kv.second << std::endl; } @@ -247,8 +247,7 @@ MXReturnValue mySupportedOps(std::string json, } MXReturnValue myAcceptSubgraph(std::string json, int subraph_id, bool* accept, - std::map options) { - std::cout << "myAcceptSubgraph" << std::endl; + std::unordered_map& options) { for (auto kv : options) { std::cout << "option: " << kv.first << " ==> " << kv.second << std::endl; } diff --git a/include/mxnet/lib_api.h b/include/mxnet/lib_api.h index bc5f6070003b..06be848ae508 100644 --- a/include/mxnet/lib_api.h +++ b/include/mxnet/lib_api.h @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -586,9 +587,9 @@ class CustomOp { /*! \brief Custom Subgraph Create function template */ typedef MXReturnValue (*supportedOps_t)(std::string, int, int*, - std::map); + std::unordered_map&); typedef MXReturnValue (*acceptSubgraph_t)(std::string, int, bool*, - std::map); + std::unordered_map&); /*! * \brief An abstract class for subgraph property @@ -1119,7 +1120,7 @@ extern "C" { const char* const* opt_vals, int num_opts) { std::string subgraph_json(json); // create map of attributes from list - std::map opts; + std::unordered_map opts; for (int i = 0; i < num_opts; i++) { opts[std::string(opt_keys[i])] = std::string(opt_vals[i]); } @@ -1138,7 +1139,7 @@ extern "C" { std::string subgraph_json(json); bool accept_bool = false; // create map of attributes from list - std::map opts; + std::unordered_map opts; for (int i = 0; i < num_opts; i++) { opts[std::string(opt_keys[i])] = std::string(opt_vals[i]); } diff --git a/src/operator/subgraph/partitioner/custom_subgraph_property.h b/src/operator/subgraph/partitioner/custom_subgraph_property.h index 6275fcd37b86..abe95cf8f3bc 100644 --- a/src/operator/subgraph/partitioner/custom_subgraph_property.h +++ b/src/operator/subgraph/partitioner/custom_subgraph_property.h @@ -94,7 +94,6 @@ class CustomSubgraphProperty: public SubgraphProperty { void PrePartition(const nnvm::Graph& g, const std::vector>& options_map) { - std::cout << "PrePartition" << std::endl; // clear supportedNodes to remove state from previous calls supportedNodes.clear(); From fcf483c482c51eb8762a496bf2374b10a2e40789 Mon Sep 17 00:00:00 2001 From: samskalicky Date: Tue, 31 Dec 2019 06:01:33 +0000 Subject: [PATCH 37/50] retrigger CI From 71109ce2ace4575274a7f3e659121f3a1294af3f Mon Sep 17 00:00:00 2001 From: samskalicky Date: Thu, 2 Jan 2020 23:35:48 +0000 Subject: [PATCH 38/50] stopped passing bool types across ABI boundary (changed to int) --- include/mxnet/lib_api.h | 8 ++++---- src/c_api/c_api.cc | 7 +++++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/include/mxnet/lib_api.h b/include/mxnet/lib_api.h index 44428f5da908..78397d0d0918 100644 --- a/include/mxnet/lib_api.h +++ b/include/mxnet/lib_api.h @@ -717,7 +717,7 @@ typedef int (*opRegSize_t)(void); typedef int (*opRegGet_t)(int, const char**, fcomp_t*, fcomp_t*, parseAttrs_t*, inferType_t*, inferShape_t*, mutateInputs_t*, - createOpState_t*, bool*); + createOpState_t*, int*); #define MXLIB_OPCALLFREE_STR "_opCallFree" typedef int (*opCallFree_t)(void*); @@ -756,7 +756,7 @@ typedef int (*opCallCreateOpState_t)(createOpState_t, const char* const*, const void**); #define MXLIB_OPCALLFSTATEFULCOMP_STR "_opCallFStatefulCompute" -typedef int (*opCallFStatefulComp_t)(bool, void*, const int64_t**, int*, void**, int*, int, +typedef int (*opCallFStatefulComp_t)(int, void*, const int64_t**, int*, void**, int*, int, const int64_t**, int*, void**, int*, int, xpu_malloc_t, void*); @@ -815,7 +815,7 @@ extern "C" { _opRegGet(int idx, const char** name, fcomp_t* fcomp, fcomp_t* fgrad, parseAttrs_t* parse, inferType_t* type, inferShape_t* shape, mutateInputs_t* mutate, - createOpState_t* create_op, bool *isSGop) { + createOpState_t* create_op, int *isSGop) { CustomOp op = Registry::get()->get(idx); *name = op.name; *fcomp = op.forward; @@ -1043,7 +1043,7 @@ extern "C" { #else int #endif - _opCallFStatefulCompute(bool is_forward, void* state_op, + _opCallFStatefulCompute(int is_forward, void* state_op, const int64_t** inshapes, int* indims, void** indata, int* intypes, int num_in, const int64_t** outshapes, int* outdims, diff --git a/src/c_api/c_api.cc b/src/c_api/c_api.cc index 41b0936f983c..bb0471f793b6 100644 --- a/src/c_api/c_api.cc +++ b/src/c_api/c_api.cc @@ -173,10 +173,13 @@ int MXLoadLib(const char *path) { mutateInputs_t mutate_fp = nullptr; createOpState_t create_opstate_fp = nullptr; bool isSubgraphOp = false; - + int _isSubgraphOp = 0; + // get custom operator implemenation from the dynamic library opRegGet(i, &name, &fcomp_fp, &fgrad_fp, &parse_fp, &type_fp, &shape_fp, - &mutate_fp, &create_opstate_fp, &isSubgraphOp); + &mutate_fp, &create_opstate_fp, &_isSubgraphOp); + //set bool, dont pass bool across ABI boundary + isSubgraphOp = _isSubgraphOp; CHECK(parse_fp != nullptr) << "Error loading '" << name << "' custom op, ParseAttrs function was not set."; From 5810a1669fa246153627f47911e537f6067ced3e Mon Sep 17 00:00:00 2001 From: samskalicky Date: Fri, 3 Jan 2020 00:48:15 +0000 Subject: [PATCH 39/50] removed subgraph op from custom_op example --- example/extensions/lib_custom_op/Makefile | 7 +- .../extensions/lib_custom_op/subgraph_lib.cc | 98 ------------------- .../extensions/lib_custom_op/test_subgraph.py | 60 ------------ 3 files changed, 2 insertions(+), 163 deletions(-) delete mode 100644 example/extensions/lib_custom_op/subgraph_lib.cc delete mode 100644 example/extensions/lib_custom_op/test_subgraph.py diff --git a/example/extensions/lib_custom_op/Makefile b/example/extensions/lib_custom_op/Makefile index 66079a16a338..090d17d98a22 100644 --- a/example/extensions/lib_custom_op/Makefile +++ b/example/extensions/lib_custom_op/Makefile @@ -15,13 +15,10 @@ # specific language governing permissions and limitations # under the License. -all: subgraph_lib gemm_lib +all: gemm_lib gemm_lib: g++ -shared -fPIC -std=c++11 gemm_lib.cc -o libgemm_lib.so -I ../../../include/mxnet -subgraph_lib: - g++ -shared -fPIC -std=c++11 subgraph_lib.cc -o libsubgraph_lib.so -I ../../../include/mxnet - clean: - rm -rf libsubgraph_lib.so libgemm_lib.so + rm -rf libgemm_lib.so diff --git a/example/extensions/lib_custom_op/subgraph_lib.cc b/example/extensions/lib_custom_op/subgraph_lib.cc deleted file mode 100644 index 27da0fd9a324..000000000000 --- a/example/extensions/lib_custom_op/subgraph_lib.cc +++ /dev/null @@ -1,98 +0,0 @@ -/* - * 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) 2019 by Contributors - * \file subgraph_lib.cc - * \brief subgraph operator implementation library file - */ - -#include -#include "lib_api.h" - -MXReturnValue parseAttrs(std::map attrs, - int* num_in, int* num_out) { - *num_in = 1; - *num_out = 1; - if (attrs.count(SUBGRAPH_SYM_JSON)) { - // example of subgraph json parsing - JsonParser jp; - JsonVal val = jp.parse_to_json(attrs[SUBGRAPH_SYM_JSON]); - int input = 0; - for (auto &item : val.map[JsonVal("nodes")].list) { - if (item.map[JsonVal("op")].str == "null") - input++; - } - int output = val.map[JsonVal("heads")].list.size(); - *num_in = input; - *num_out = output; - } - return MX_SUCCESS; -} - -class MyStatefulOp : public CustomStatefulOp { - public: - explicit MyStatefulOp(std::string sym) : subgraph_sym(sym) {} - - MXReturnValue Forward(std::vector inputs, - std::vector outputs, - OpResource op_res) { - std::cout << "Info: subgraph symbol is: " << std::endl; - std::cout << subgraph_sym << std::endl; - float* in_data = inputs[0].data(); - float* out_data = outputs[0].data(); - std::cout << "Info: output is: " << std::endl; - for (int i = 0; i < inputs[0].size(); i++) { - out_data[i] = in_data[i]; - } - return MX_SUCCESS; - } - - private: - std::string subgraph_sym; -}; - -MXReturnValue createOpState(std::map attrs, - CustomStatefulOp** op_inst) { - std::string serialized_subgraph = "[empty]"; - // MXNet subgraph is stored as Symbol in operator node attrs subgraphs field - // custom subgraph is stored as json string in custom operator attrs map entry - if (attrs.count(SUBGRAPH_SYM_JSON)) { - // user can now parse json and run other custom ops inside subgraph - serialized_subgraph = attrs[SUBGRAPH_SYM_JSON]; - } - *op_inst = new MyStatefulOp(serialized_subgraph); - std::cout << "Info: stateful operator created" << std::endl; - return MX_SUCCESS; -} - -REGISTER_OP(_custom_subgraph_op) -.setParseAttrs(parseAttrs) -.setIsSubgraphOp() -.setCreateOpState(createOpState); - -MXReturnValue initialize(int version) { - if (version >= 10400) { - std::cout << "MXNet version " << version << " supported" << std::endl; - return MX_SUCCESS; - } else { - std::cout << "MXNet version " << version << " not supported" << std::endl; - return MX_FAIL; - } -} diff --git a/example/extensions/lib_custom_op/test_subgraph.py b/example/extensions/lib_custom_op/test_subgraph.py deleted file mode 100644 index 2625b13f6794..000000000000 --- a/example/extensions/lib_custom_op/test_subgraph.py +++ /dev/null @@ -1,60 +0,0 @@ -#!/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 end of end computation of custom operator - -import mxnet as mx -import os, ctypes -from mxnet.base import _LIB, check_call, mx_uint, c_str, c_str_array, SymbolHandle - -# load library -if (os.name=='posix'): - path = os.path.abspath('libsubgraph_lib.so') - mx.library.load(path) -elif (os.name=='nt'): - path = os.path.abspath('libsubgraph_lib.dll') - mx.library.load(path) - -a = mx.sym.var('a') -b = mx.sym.var('b') -c = a + b -d = mx.sym.exp(c) -ret = mx.sym.log(d) - -op_names = ['exp','log'] -out = SymbolHandle() - -check_call(_LIB.MXBuildSubgraphByOpNames(ret.handle, - c_str('default'), - mx_uint(len(op_names)), - c_str_array(op_names), - ctypes.byref(out))) -partitioned_sym = mx.sym.Symbol(out) -json_sym = partitioned_sym.tojson() - -mystr = json_sym.replace("_CachedOp","_custom_subgraph_op") -mysym = mx.sym.load_json(mystr) - -exe = mysym.bind(ctx=mx.cpu(), args={'a':mx.nd.ones((3,2)), 'b':mx.nd.ones((3,2))}) -out = exe.forward() -print(out) From 27d0b204a18bb48ae29b4d597ce74e714288db4b Mon Sep 17 00:00:00 2001 From: samskalicky Date: Fri, 3 Jan 2020 00:48:36 +0000 Subject: [PATCH 40/50] changed subgraph example to use isSubgraphOp --- example/extensions/lib_subgraph/subgraph_lib.cc | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/example/extensions/lib_subgraph/subgraph_lib.cc b/example/extensions/lib_subgraph/subgraph_lib.cc index 842c3407c0da..63f4356516cf 100644 --- a/example/extensions/lib_subgraph/subgraph_lib.cc +++ b/example/extensions/lib_subgraph/subgraph_lib.cc @@ -48,20 +48,6 @@ MXReturnValue parseAttrs(std::map attrs, return MX_SUCCESS; } -MXReturnValue inferType(std::map attrs, - std::vector &intypes, - std::vector &outtypes) { - outtypes[0] = intypes[0]; - return MX_SUCCESS; -} - -MXReturnValue inferShape(std::map attrs, - std::vector> &inshapes, - std::vector> &outshapes) { - outshapes[0] = inshapes[0]; - return MX_SUCCESS; -} - /* function to execute log operator on floats */ void myLog(MXTensor &in, MXTensor &out) { float* inp = in.data(); @@ -199,8 +185,7 @@ MXReturnValue createOpState(std::map attrs, REGISTER_OP(_custom_subgraph_op) .setParseAttrs(parseAttrs) -.setInferType(inferType) -.setInferShape(inferShape) +.setIsSubgraphOp() .setCreateOpState(createOpState); const std::vector op_names({"exp","log"}); From 4278d22ea305bd0538c8970478b1bc2fc5849869 Mon Sep 17 00:00:00 2001 From: samskalicky Date: Fri, 3 Jan 2020 01:47:56 +0000 Subject: [PATCH 41/50] added functionality to set "isArg" attribute for subgraph inputs --- src/operator/subgraph/build_subgraph.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/operator/subgraph/build_subgraph.cc b/src/operator/subgraph/build_subgraph.cc index 9aab960866ee..4e87bdd5cfb2 100644 --- a/src/operator/subgraph/build_subgraph.cc +++ b/src/operator/subgraph/build_subgraph.cc @@ -559,6 +559,10 @@ void CutGraphInputs(const std::vector &input_entries, ++(it->second); } nnvm::NodePtr n = nnvm::CreateVariableNode(var_name + std::to_string(name_count_map[var_name])); + if(e->node->is_variable()) + n->attrs.dict["isArg"]="True"; + else + n->attrs.dict["isArg"]="False"; *e = nnvm::NodeEntry{n, 0, 0}; } } From 4e5d849615205224b7e0d0318bec552b5e2dd02d Mon Sep 17 00:00:00 2001 From: samskalicky Date: Fri, 3 Jan 2020 01:49:34 +0000 Subject: [PATCH 42/50] added comment --- src/operator/subgraph/build_subgraph.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/operator/subgraph/build_subgraph.cc b/src/operator/subgraph/build_subgraph.cc index 4e87bdd5cfb2..b5380b702f6d 100644 --- a/src/operator/subgraph/build_subgraph.cc +++ b/src/operator/subgraph/build_subgraph.cc @@ -559,10 +559,11 @@ void CutGraphInputs(const std::vector &input_entries, ++(it->second); } nnvm::NodePtr n = nnvm::CreateVariableNode(var_name + std::to_string(name_count_map[var_name])); - if(e->node->is_variable()) - n->attrs.dict["isArg"]="True"; + // set attribute for subgraph input to indicate if it is from an arg/param to model + if (e->node->is_variable()) + n->attrs.dict["isArg"] = "True"; else - n->attrs.dict["isArg"]="False"; + n->attrs.dict["isArg"] = "False"; *e = nnvm::NodeEntry{n, 0, 0}; } } From 034cb89fda12fd0179fbe40b275efa271b6cd526 Mon Sep 17 00:00:00 2001 From: samskalicky Date: Fri, 3 Jan 2020 01:50:59 +0000 Subject: [PATCH 43/50] added functionality to set aux attribute for inputs to subgraph --- .../partitioner/custom_subgraph_property.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/operator/subgraph/partitioner/custom_subgraph_property.h b/src/operator/subgraph/partitioner/custom_subgraph_property.h index abe95cf8f3bc..e82271ea1a53 100644 --- a/src/operator/subgraph/partitioner/custom_subgraph_property.h +++ b/src/operator/subgraph/partitioner/custom_subgraph_property.h @@ -29,6 +29,7 @@ #define MXNET_OPERATOR_SUBGRAPH_PARTITIONER_CUSTOM_SUBGRAPH_PROPERTY_H_ #include +#include #include #include #include @@ -164,6 +165,24 @@ class CustomSubgraphProperty: public SubgraphProperty { if (acceptSubgraph_) { nnvm::Graph g; g.outputs = sym.outputs; + const auto& idx = g.indexed_graph(); + + // set isArg/isAux for each null op/param in the graph + const std::vector aux_names = sym.ListInputNames(nnvm::Symbol::kAuxiliaryStates); + std::unordered_set aux_set(aux_names.begin(), aux_names.end()); + for (unsigned i = 0; i < idx.num_nodes(); i++) { + nnvm::Node* node = const_cast(idx[i].source); + // check if this node is input to subgraph + if (node->is_variable()) { + // check if this node is an aux param + if (aux_set.count(node->attrs.name)) + node->attrs.dict["isAux"] = "True"; + else + node->attrs.dict["isAux"] = "False"; + } + } + + std::string subgraph_json = nnvm::pass::SaveJSON(g); CHECK(callAcceptSubgraph_(acceptSubgraph_, subgraph_json.c_str(), subgraph_id, &accept, opt_keys_.data(), From 584c0f654893da4fa861b1dbc0574c1260078725 Mon Sep 17 00:00:00 2001 From: samskalicky Date: Fri, 3 Jan 2020 05:17:06 +0000 Subject: [PATCH 44/50] fixed whitespace and cmakelists --- CMakeLists.txt | 1 - src/c_api/c_api.cc | 4 ++-- src/operator/subgraph/partitioner/custom_subgraph_property.h | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0fbcf2f194d3..66246b3fd528 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -698,7 +698,6 @@ elseif(MSVC) target_compile_options(subgraph_lib PUBLIC /LD) set_target_properties(sample_lib PROPERTIES PREFIX "lib") set_target_properties(subgraph_lib PROPERTIES PREFIX "lib") -endif() if(USE_CUDA) if(MSVC) diff --git a/src/c_api/c_api.cc b/src/c_api/c_api.cc index bb0471f793b6..f558f7fad602 100644 --- a/src/c_api/c_api.cc +++ b/src/c_api/c_api.cc @@ -174,11 +174,11 @@ int MXLoadLib(const char *path) { createOpState_t create_opstate_fp = nullptr; bool isSubgraphOp = false; int _isSubgraphOp = 0; - + // get custom operator implemenation from the dynamic library opRegGet(i, &name, &fcomp_fp, &fgrad_fp, &parse_fp, &type_fp, &shape_fp, &mutate_fp, &create_opstate_fp, &_isSubgraphOp); - //set bool, dont pass bool across ABI boundary + // set bool, dont pass bool across ABI boundary isSubgraphOp = _isSubgraphOp; CHECK(parse_fp != nullptr) << "Error loading '" << name diff --git a/src/operator/subgraph/partitioner/custom_subgraph_property.h b/src/operator/subgraph/partitioner/custom_subgraph_property.h index e82271ea1a53..e941dda4029e 100644 --- a/src/operator/subgraph/partitioner/custom_subgraph_property.h +++ b/src/operator/subgraph/partitioner/custom_subgraph_property.h @@ -182,7 +182,6 @@ class CustomSubgraphProperty: public SubgraphProperty { } } - std::string subgraph_json = nnvm::pass::SaveJSON(g); CHECK(callAcceptSubgraph_(acceptSubgraph_, subgraph_json.c_str(), subgraph_id, &accept, opt_keys_.data(), From d7e5dbb57350ad30cbf1639a6c401a09a84498ae Mon Sep 17 00:00:00 2001 From: samskalicky Date: Sat, 4 Jan 2020 07:44:49 +0000 Subject: [PATCH 45/50] changed acceptSubgraph API to let library set attributes on subgraph node --- .../extensions/lib_subgraph/subgraph_lib.cc | 36 +++++++------------ .../extensions/lib_subgraph/test_subgraph.py | 1 + include/mxnet/lib_api.h | 32 +++++++++++++++-- src/c_api/c_api.cc | 14 ++++---- .../partitioner/custom_subgraph_property.h | 20 +++++++++-- 5 files changed, 68 insertions(+), 35 deletions(-) diff --git a/example/extensions/lib_subgraph/subgraph_lib.cc b/example/extensions/lib_subgraph/subgraph_lib.cc index 63f4356516cf..c52caa89b2f1 100644 --- a/example/extensions/lib_subgraph/subgraph_lib.cc +++ b/example/extensions/lib_subgraph/subgraph_lib.cc @@ -28,26 +28,6 @@ #include #include "lib_api.h" -MXReturnValue parseAttrs(std::map attrs, - int* num_in, int* num_out) { - *num_in = 1; - *num_out = 1; - if (attrs.count(SUBGRAPH_SYM_JSON)) { - // example of subgraph json parsing - JsonParser jp; - JsonVal val = jp.parse_to_json(attrs[SUBGRAPH_SYM_JSON]); - int input = 0; - for (auto &item : val.map[JsonVal("nodes")].list) { - if (item.map[JsonVal("op")].str == "null") - input++; - } - int output = val.map[JsonVal("heads")].list.size(); - *num_in = input; - *num_out = output; - } - return MX_SUCCESS; -} - /* function to execute log operator on floats */ void myLog(MXTensor &in, MXTensor &out) { float* inp = in.data(); @@ -157,7 +137,12 @@ MXReturnValue myExecutor(std::vector inputs, class MyStatefulOp : public CustomStatefulOp { public: - explicit MyStatefulOp(std::string sym) : subgraph_sym(sym) {} + explicit MyStatefulOp(std::string sym, std::map attrs) + : subgraph_sym(sym), attrs_(attrs) { + for (auto kv : attrs) { + std::cout << "subgraphOp attributes: " << kv.first << " ==> " << kv.second << std::endl; + } + } MXReturnValue Forward(std::vector inputs, std::vector outputs, @@ -167,6 +152,7 @@ class MyStatefulOp : public CustomStatefulOp { private: std::string subgraph_sym; + std::map attrs_; }; MXReturnValue createOpState(std::map attrs, @@ -178,13 +164,13 @@ MXReturnValue createOpState(std::map attrs, // user can now parse json and run other custom ops inside subgraph serialized_subgraph = attrs[SUBGRAPH_SYM_JSON]; } - *op_inst = new MyStatefulOp(serialized_subgraph); + attrs.erase(SUBGRAPH_SYM_JSON); + *op_inst = new MyStatefulOp(serialized_subgraph, attrs); std::cout << "Info: stateful operator created" << std::endl; return MX_SUCCESS; } REGISTER_OP(_custom_subgraph_op) -.setParseAttrs(parseAttrs) .setIsSubgraphOp() .setCreateOpState(createOpState); @@ -232,7 +218,8 @@ MXReturnValue mySupportedOps(std::string json, } MXReturnValue myAcceptSubgraph(std::string json, int subraph_id, bool* accept, - std::unordered_map& options) { + std::unordered_map& options, + std::unordered_map& attrs) { for (auto kv : options) { std::cout << "option: " << kv.first << " ==> " << kv.second << std::endl; } @@ -243,6 +230,7 @@ MXReturnValue myAcceptSubgraph(std::string json, int subraph_id, bool* accept, } else { *accept = true; std::cout << "accepting subgraph" << std::endl; + attrs["myKey"] = "myVal"; } return MX_SUCCESS; } diff --git a/example/extensions/lib_subgraph/test_subgraph.py b/example/extensions/lib_subgraph/test_subgraph.py index c85229da8d94..8169261d4d42 100644 --- a/example/extensions/lib_subgraph/test_subgraph.py +++ b/example/extensions/lib_subgraph/test_subgraph.py @@ -53,6 +53,7 @@ print('Testing partitioning with shapes/types') arg_array = [mx.nd.ones((3,2),dtype='float32'), mx.nd.ones((3,2),dtype='float32')] mysym2 = sym.optimize_for("myProp",arg_array) +print(mysym2.tojson()) exe2 = mysym2.bind(ctx=mx.cpu(), args={'a':mx.nd.ones((3,2)), 'b':mx.nd.ones((3,2))}) out2 = exe2.forward() print(out2) diff --git a/include/mxnet/lib_api.h b/include/mxnet/lib_api.h index 78397d0d0918..31285ec5e9f2 100644 --- a/include/mxnet/lib_api.h +++ b/include/mxnet/lib_api.h @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -595,6 +596,7 @@ class CustomOp { typedef MXReturnValue (*supportedOps_t)(std::string, int, int*, std::unordered_map&); typedef MXReturnValue (*acceptSubgraph_t)(std::string, int, bool*, + std::unordered_map&, std::unordered_map&); /*! @@ -777,7 +779,8 @@ typedef int (*partCallSupportedOps_t)(supportedOps_t, const char*, int, int *, typedef int (*partCallAcceptSubgraph_t)(acceptSubgraph_t acceptSubgraph, const char *json, int subgraph_id, int *accept, const char* const*, - const char* const*, int); + const char* const*, int, + char***, char***, int*); #define MXLIB_INITIALIZE_STR "initialize" typedef int (*initialize_t)(int); @@ -1142,7 +1145,8 @@ extern "C" { #endif _partCallAcceptSubgraph(acceptSubgraph_t acceptSubgraph, const char *json, int subgraph_id, int *accept, const char* const* opt_keys, - const char* const* opt_vals, int num_opts) { + const char* const* opt_vals, int num_opts, + char*** attr_keys, char*** attr_vals, int *num_attrs) { std::string subgraph_json(json); bool accept_bool = false; // create map of attributes from list @@ -1150,8 +1154,30 @@ extern "C" { for (int i = 0; i < num_opts; i++) { opts[std::string(opt_keys[i])] = std::string(opt_vals[i]); } - MXReturnValue retval = acceptSubgraph(subgraph_json, subgraph_id, &accept_bool, opts); + + //attributes to set on subgraph node + std::unordered_map attrs; + + MXReturnValue retval = acceptSubgraph(subgraph_json, subgraph_id, &accept_bool, opts, attrs); *accept = accept_bool; + + if (attrs.size() > 0) { + *num_attrs = attrs.size(); + // allocate space for attributes + *attr_keys = static_cast(malloc (attrs.size() * sizeof(char*))); + *attr_vals = static_cast(malloc (attrs.size() * sizeof(char*))); + + // copy attributes + int i=0; + for (auto kv : attrs) { + (*attr_keys)[i] = static_cast(malloc ((kv.first.size()+1) * sizeof(char))); + (*attr_vals)[i] = static_cast(malloc ((kv.second.size()+1) * sizeof(char))); + strcpy((*attr_keys)[i], kv.first.c_str()); + strcpy((*attr_vals)[i], kv.second.c_str()); + i++; + } + } + return retval; } diff --git a/src/c_api/c_api.cc b/src/c_api/c_api.cc index f558f7fad602..cd236c9c44f0 100644 --- a/src/c_api/c_api.cc +++ b/src/c_api/c_api.cc @@ -181,10 +181,10 @@ int MXLoadLib(const char *path) { // set bool, dont pass bool across ABI boundary isSubgraphOp = _isSubgraphOp; - CHECK(parse_fp != nullptr) << "Error loading '" << name - << "' custom op, ParseAttrs function was not set."; if (!isSubgraphOp) { // validate custom operator functions from the dynamic library + CHECK(parse_fp != nullptr) << "Error loading '" << name + << "' custom op, ParseAttrs function was not set."; CHECK(fcomp_fp != nullptr || create_opstate_fp != nullptr) << "Error loading '" << name << "' custom op, Forward or CreateOpState function was not set."; CHECK(type_fp != nullptr) << "Error loading '" << name @@ -655,9 +655,6 @@ int MXLoadLib(const char *path) { // check if operator is already registered const nnvm::Op *regOpPtr = dmlc::Registry::Get()->Find(name); nnvm::Op ®Op = dmlc::Registry::Get()->__REGISTER_OR_GET__(name); - regOp.set_attr_parser(attr_parser); - regOp.set_num_inputs(num_inputs); - regOp.set_num_outputs(num_outputs); int plevel = 10; if (regOpPtr != nullptr) { // overwrite registration of existing op with custom op @@ -667,6 +664,9 @@ int MXLoadLib(const char *path) { plevel++; } if (!isSubgraphOp) { + regOp.set_attr_parser(attr_parser); + regOp.set_num_inputs(num_inputs); + regOp.set_num_outputs(num_outputs); regOp.set_attr("FInferType", infer_type, plevel); regOp.set_attr("FInferShape", infer_shape, plevel); regOp.set_attr("FInferStorageType", infer_storage_type, plevel); @@ -676,6 +676,8 @@ int MXLoadLib(const char *path) { regOp.set_attr("FMutateInputs", mutate_inputs, plevel); } else { using namespace mxnet::op; + regOp.set_num_inputs(DefaultSubgraphOpNumInputs); + regOp.set_num_outputs(DefaultSubgraphOpNumOutputs); regOp.set_attr("FInferType", DefaultSubgraphOpType, plevel); regOp.set_attr("FInferShape", @@ -765,7 +767,7 @@ int MXLoadLib(const char *path) { mxnet::op::SubgraphBackendRegistry::Get()->__REGISTER_CUSTOM_PROPERTY__(name_str, std::make_shared( strategy_str, callSupportedOps, supportedOps_fp, - callAcceptSubgraph, acceptSubgraph_fp, op_name_str)); + callAcceptSubgraph, acceptSubgraph_fp, callFree, op_name_str)); } } API_END(); diff --git a/src/operator/subgraph/partitioner/custom_subgraph_property.h b/src/operator/subgraph/partitioner/custom_subgraph_property.h index e941dda4029e..0b77b37d783c 100644 --- a/src/operator/subgraph/partitioner/custom_subgraph_property.h +++ b/src/operator/subgraph/partitioner/custom_subgraph_property.h @@ -80,12 +80,14 @@ class CustomSubgraphProperty: public SubgraphProperty { supportedOps_t supportedOps, partCallAcceptSubgraph_t callAcceptSubgraph, acceptSubgraph_t acceptSubgraph, + opCallFree_t callFree, std::string op_name) : subgraphProp(subgraphProp_name), callSupportedOps_(callSupportedOps), supportedOps_(supportedOps), callAcceptSubgraph_(callAcceptSubgraph), acceptSubgraph_(acceptSubgraph), + callFree_(callFree), subgraph_op_name(op_name) {} // create custom subgraph property @@ -162,6 +164,9 @@ class CustomSubgraphProperty: public SubgraphProperty { virtual nnvm::NodePtr CreateSubgraphNode(const nnvm::Symbol &sym, const int subgraph_id = 0) const { int accept = 1; + int num_attr = 0; + char** attr_keys = nullptr; + char** attr_vals = nullptr; if (acceptSubgraph_) { nnvm::Graph g; g.outputs = sym.outputs; @@ -181,11 +186,12 @@ class CustomSubgraphProperty: public SubgraphProperty { node->attrs.dict["isAux"] = "False"; } } - + std::string subgraph_json = nnvm::pass::SaveJSON(g); CHECK(callAcceptSubgraph_(acceptSubgraph_, subgraph_json.c_str(), subgraph_id, &accept, opt_keys_.data(), - opt_vals_.data(), opt_keys_.size())) + opt_vals_.data(), opt_keys_.size(), + &attr_keys, &attr_vals, &num_attr)) << "Error calling acceptSubgraph for '" << subgraphProp << "'"; } if (accept) { @@ -193,6 +199,15 @@ class CustomSubgraphProperty: public SubgraphProperty { n->attrs.op = Op::Get(subgraph_op_name); n->attrs.name = "_op" + std::to_string(subgraph_id); n->attrs.subgraphs.push_back(std::make_shared(sym)); + // set user specified attributes + for (int i=0; i < num_attr; i++) { + n->attrs.dict[attr_keys[i]] = attr_vals[i]; + callFree_(attr_vals[i]); + callFree_(attr_keys[i]); + } + // free memory used by custom op to allocate attributes + callFree_(attr_vals); + callFree_(attr_keys); return n; } else { return NULL; @@ -208,6 +223,7 @@ class CustomSubgraphProperty: public SubgraphProperty { supportedOps_t supportedOps_; partCallAcceptSubgraph_t callAcceptSubgraph_; acceptSubgraph_t acceptSubgraph_; + opCallFree_t callFree_; std::unordered_set supportedNodes; std::string subgraph_op_name; std::vector> options_map_; From 34ff9898ff6e21860e01d6c629405526edfe3859 Mon Sep 17 00:00:00 2001 From: samskalicky Date: Sat, 4 Jan 2020 23:46:57 +0000 Subject: [PATCH 46/50] fixed whitespace --- include/mxnet/lib_api.h | 10 +++++----- .../subgraph/partitioner/custom_subgraph_property.h | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/mxnet/lib_api.h b/include/mxnet/lib_api.h index 31285ec5e9f2..89f88dc3a3b7 100644 --- a/include/mxnet/lib_api.h +++ b/include/mxnet/lib_api.h @@ -1155,9 +1155,9 @@ extern "C" { opts[std::string(opt_keys[i])] = std::string(opt_vals[i]); } - //attributes to set on subgraph node + // attributes to set on subgraph node std::unordered_map attrs; - + MXReturnValue retval = acceptSubgraph(subgraph_json, subgraph_id, &accept_bool, opts, attrs); *accept = accept_bool; @@ -1168,12 +1168,12 @@ extern "C" { *attr_vals = static_cast(malloc (attrs.size() * sizeof(char*))); // copy attributes - int i=0; + int i = 0; for (auto kv : attrs) { (*attr_keys)[i] = static_cast(malloc ((kv.first.size()+1) * sizeof(char))); (*attr_vals)[i] = static_cast(malloc ((kv.second.size()+1) * sizeof(char))); - strcpy((*attr_keys)[i], kv.first.c_str()); - strcpy((*attr_vals)[i], kv.second.c_str()); + snprintf((*attr_keys)[i], kv.first.size()+1, kv.first.c_str()); + snprintf((*attr_vals)[i], kv.seconds.size()+1, kv.second.c_str()); i++; } } diff --git a/src/operator/subgraph/partitioner/custom_subgraph_property.h b/src/operator/subgraph/partitioner/custom_subgraph_property.h index 0b77b37d783c..64f93d2dac12 100644 --- a/src/operator/subgraph/partitioner/custom_subgraph_property.h +++ b/src/operator/subgraph/partitioner/custom_subgraph_property.h @@ -186,7 +186,7 @@ class CustomSubgraphProperty: public SubgraphProperty { node->attrs.dict["isAux"] = "False"; } } - + std::string subgraph_json = nnvm::pass::SaveJSON(g); CHECK(callAcceptSubgraph_(acceptSubgraph_, subgraph_json.c_str(), subgraph_id, &accept, opt_keys_.data(), From dda4c8600a28cfaa90e5e89709ab37e2a6fdbd80 Mon Sep 17 00:00:00 2001 From: samskalicky Date: Sun, 5 Jan 2020 00:54:14 +0000 Subject: [PATCH 47/50] fixed seconds --- include/mxnet/lib_api.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/mxnet/lib_api.h b/include/mxnet/lib_api.h index 89f88dc3a3b7..1cc1a9a0cc13 100644 --- a/include/mxnet/lib_api.h +++ b/include/mxnet/lib_api.h @@ -1172,8 +1172,8 @@ extern "C" { for (auto kv : attrs) { (*attr_keys)[i] = static_cast(malloc ((kv.first.size()+1) * sizeof(char))); (*attr_vals)[i] = static_cast(malloc ((kv.second.size()+1) * sizeof(char))); - snprintf((*attr_keys)[i], kv.first.size()+1, kv.first.c_str()); - snprintf((*attr_vals)[i], kv.seconds.size()+1, kv.second.c_str()); + snprintf((*attr_keys)[i], kv.first.size()+1, "%s", kv.first.c_str()); + snprintf((*attr_vals)[i], kv.second.size()+1, "%s", kv.second.c_str()); i++; } } From 19315999935fb124585af30a9c13abdf59d724ad Mon Sep 17 00:00:00 2001 From: samskalicky Date: Sun, 5 Jan 2020 09:28:22 +0000 Subject: [PATCH 48/50] retrigger ci From 39ed29909ffb10603809cb3e5a5d7943ffcb71bf Mon Sep 17 00:00:00 2001 From: samskalicky Date: Tue, 7 Jan 2020 01:53:23 +0000 Subject: [PATCH 49/50] removed unused code --- src/c_api/c_api.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/c_api/c_api.cc b/src/c_api/c_api.cc index cd236c9c44f0..b5fe9301026e 100644 --- a/src/c_api/c_api.cc +++ b/src/c_api/c_api.cc @@ -741,7 +741,7 @@ int MXLoadLib(const char *path) { << "' custom partitioner, no strategies defined"; std::string name_str(name); LOG(INFO) << "\tPartitioner[" << i << "] " << name; - // MXNET_REGISTER_SUBGRAPH_BACKEND(customBackend); + mxnet::op::SubgraphBackendRegistry::Get()->__REGISTER_BACKEND__(name); for (int j = 0; j < count; j++) { From 967787e1cc010370f03c3c3b488ae80d38fed304 Mon Sep 17 00:00:00 2001 From: samskalicky Date: Tue, 7 Jan 2020 21:28:35 +0000 Subject: [PATCH 50/50] fixed variable naming --- .../partitioner/custom_subgraph_property.h | 102 +++++++++--------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/src/operator/subgraph/partitioner/custom_subgraph_property.h b/src/operator/subgraph/partitioner/custom_subgraph_property.h index 64f93d2dac12..b4ea1a087d71 100644 --- a/src/operator/subgraph/partitioner/custom_subgraph_property.h +++ b/src/operator/subgraph/partitioner/custom_subgraph_property.h @@ -46,18 +46,18 @@ namespace op { */ class CustomContainOpSelector: public SubgraphSelector { public: - explicit CustomContainOpSelector(std::unordered_set supportedNodes) : - supportedNodes_(supportedNodes) {} + explicit CustomContainOpSelector(std::unordered_set supported_nodes) : + supported_nodes_(supported_nodes) {} virtual bool Select(const nnvm::Node &n) { - return supportedNodes_.count(n.attrs.name) > 0; + return supported_nodes_.count(n.attrs.name) > 0; } virtual bool SelectInput(const nnvm::Node &n, const nnvm::Node &new_node) { - return supportedNodes_.count(new_node.attrs.name) > 0; + return supported_nodes_.count(new_node.attrs.name) > 0; } virtual bool SelectOutput(const nnvm::Node &n, const nnvm::Node &new_node) { - return supportedNodes_.count(new_node.attrs.name) > 0; + return supported_nodes_.count(new_node.attrs.name) > 0; } - std::unordered_set supportedNodes_; + std::unordered_set supported_nodes_; }; /* @@ -69,25 +69,25 @@ class CustomContainOpSelector: public SubgraphSelector { class CustomSubgraphProperty: public SubgraphProperty { public: CustomSubgraphProperty() : - subgraphProp("error"), - callSupportedOps_(nullptr), - supportedOps_(nullptr), - callAcceptSubgraph_(nullptr), - acceptSubgraph_(nullptr), + subgraph_prop("error"), + call_supported_ops_(nullptr), + supported_ops_(nullptr), + call_accept_subgraph_(nullptr), + accept_subgraph_(nullptr), subgraph_op_name("error") {} - CustomSubgraphProperty(std::string subgraphProp_name, - partCallSupportedOps_t callSupportedOps, - supportedOps_t supportedOps, - partCallAcceptSubgraph_t callAcceptSubgraph, - acceptSubgraph_t acceptSubgraph, - opCallFree_t callFree, + CustomSubgraphProperty(std::string subgraph_prop_name, + partCallSupportedOps_t call_supported_ops, + supportedOps_t supported_ops, + partCallAcceptSubgraph_t call_accept_subgraph, + acceptSubgraph_t accept_subgraph, + opCallFree_t call_free, std::string op_name) : - subgraphProp(subgraphProp_name), - callSupportedOps_(callSupportedOps), - supportedOps_(supportedOps), - callAcceptSubgraph_(callAcceptSubgraph), - acceptSubgraph_(acceptSubgraph), - callFree_(callFree), + subgraph_prop(subgraph_prop_name), + call_supported_ops_(call_supported_ops), + supported_ops_(supported_ops), + call_accept_subgraph_(call_accept_subgraph), + accept_subgraph_(accept_subgraph), + call_free_(call_free), subgraph_op_name(op_name) {} // create custom subgraph property @@ -97,8 +97,8 @@ class CustomSubgraphProperty: public SubgraphProperty { void PrePartition(const nnvm::Graph& g, const std::vector>& options_map) { - // clear supportedNodes to remove state from previous calls - supportedNodes.clear(); + // clear supported_nodes to remove state from previous calls + supported_nodes.clear(); // remove all graph attrs, some cannot be saved to json nnvm::Graph graph = std::move(g); @@ -128,15 +128,15 @@ class CustomSubgraphProperty: public SubgraphProperty { } } - CHECK(supportedOps_ != nullptr) - << "supportedOps_ is null for " << subgraphProp << std::endl; - CHECK(callSupportedOps_ != nullptr) - << "callSupportedOps_ is null for " << subgraphProp << std::endl; + CHECK(supported_ops_ != nullptr) + << "supported_ops_ is null for " << subgraph_prop << std::endl; + CHECK(call_supported_ops_ != nullptr) + << "call_supported_ops_ is null for " << subgraph_prop << std::endl; std::string subgraph_json = nnvm::pass::SaveJSON(graph); - std::vector supportedNodeIDs(indexed_graph.num_nodes(), 0); + std::vector supported_node_IDs(indexed_graph.num_nodes(), 0); const char* json = subgraph_json.c_str(); - int *ids = supportedNodeIDs.data(); + int *ids = supported_node_IDs.data(); // clear options from previous call opt_keys_.clear(); @@ -148,15 +148,15 @@ class CustomSubgraphProperty: public SubgraphProperty { opt_vals_.push_back(options_map_.back().second.c_str()); } - CHECK(callSupportedOps_(supportedOps_, json, supportedNodeIDs.size(), ids, + CHECK(call_supported_ops_(supported_ops_, json, supported_node_IDs.size(), ids, opt_keys_.data(), opt_vals_.data(), opt_keys_.size())) - << "Error calling supportedOps for '" << subgraphProp << "'"; + << "Error calling supported_ops for '" << subgraph_prop << "'"; const auto& idx = g.indexed_graph(); // loop and add node names for each supported node ID - for (unsigned i = 0; i < supportedNodeIDs.size(); i++) { - if (supportedNodeIDs[i]) { - supportedNodes.insert(idx[i].source->attrs.name); + for (unsigned i = 0; i < supported_node_IDs.size(); i++) { + if (supported_node_IDs[i]) { + supported_nodes.insert(idx[i].source->attrs.name); } } } @@ -167,7 +167,7 @@ class CustomSubgraphProperty: public SubgraphProperty { int num_attr = 0; char** attr_keys = nullptr; char** attr_vals = nullptr; - if (acceptSubgraph_) { + if (accept_subgraph_) { nnvm::Graph g; g.outputs = sym.outputs; const auto& idx = g.indexed_graph(); @@ -188,11 +188,11 @@ class CustomSubgraphProperty: public SubgraphProperty { } std::string subgraph_json = nnvm::pass::SaveJSON(g); - CHECK(callAcceptSubgraph_(acceptSubgraph_, subgraph_json.c_str(), + CHECK(call_accept_subgraph_(accept_subgraph_, subgraph_json.c_str(), subgraph_id, &accept, opt_keys_.data(), opt_vals_.data(), opt_keys_.size(), &attr_keys, &attr_vals, &num_attr)) - << "Error calling acceptSubgraph for '" << subgraphProp << "'"; + << "Error calling accept_subgraph for '" << subgraph_prop << "'"; } if (accept) { nnvm::NodePtr n = nnvm::Node::Create(); @@ -202,12 +202,12 @@ class CustomSubgraphProperty: public SubgraphProperty { // set user specified attributes for (int i=0; i < num_attr; i++) { n->attrs.dict[attr_keys[i]] = attr_vals[i]; - callFree_(attr_vals[i]); - callFree_(attr_keys[i]); + call_free_(attr_vals[i]); + call_free_(attr_keys[i]); } // free memory used by custom op to allocate attributes - callFree_(attr_vals); - callFree_(attr_keys); + call_free_(attr_vals); + call_free_(attr_keys); return n; } else { return NULL; @@ -215,16 +215,16 @@ class CustomSubgraphProperty: public SubgraphProperty { } // override CreateSubgraphSelector virtual SubgraphSelectorPtr CreateSubgraphSelector() const { - return std::make_shared(supportedNodes); + return std::make_shared(supported_nodes); } - std::string subgraphProp; - partCallSupportedOps_t callSupportedOps_; - supportedOps_t supportedOps_; - partCallAcceptSubgraph_t callAcceptSubgraph_; - acceptSubgraph_t acceptSubgraph_; - opCallFree_t callFree_; - std::unordered_set supportedNodes; + std::string subgraph_prop; + partCallSupportedOps_t call_supported_ops_; + supportedOps_t supported_ops_; + partCallAcceptSubgraph_t call_accept_subgraph_; + acceptSubgraph_t accept_subgraph_; + opCallFree_t call_free_; + std::unordered_set supported_nodes; std::string subgraph_op_name; std::vector> options_map_; std::vector opt_keys_, opt_vals_;