Skip to content

Commit fab9688

Browse files
mseth10Rohit Kumar Srivastava
authored and
Rohit Kumar Srivastava
committed
Fix PR apache#15489 (Dynamic Library Loading Support) (apache#15760)
* Dynamic Library Loading Support (apache#15489) * Accelerator APIs header file * adding example to test accelerator loading * adding c_api function header * modifying header file path * creating templates for call to lib fns * modifying target to mxnet_static for libdl * rebaseing with master * returning nullptr handle if library not loaded * refactoring code to load libraries dynamically * addressing review comments * using static cast * pylint fix * moving library.h file to src/common/ * adding dynamic loading support for windows * updating header guard * fixing headers * fixing windows casting error * declaring library functions for windows * adding library testing module in examples * adding unit test to test library loading * correcting file names * updating error messages * getting error message from DL library * adding unit test to gpu suite * correcting windows pointer * requiring absolute path to library * changing file description * addressing review comments - adding more docs, windows error msg * addressing PR comments * checking machine type for unit test * “re-trigger” * added map to store loaded libraries * added dlclose calls in naive & threaded engines * removed library map declaration in cc file * added windows free * fixed formatting * added cast to HMODULE for void* for windows * retrigger CI for flaky unix_cpu * build library and stash it in CI * modifying unittest to use CI built binary * Retrigger CI * adding dlclose to initialize destructor * adding sample_lib target to all in Makefile
1 parent ed1998b commit fab9688

File tree

18 files changed

+555
-8
lines changed

18 files changed

+555
-8
lines changed

CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,8 @@ else()
692692

693693
endif()
694694

695+
add_library(sample_lib SHARED ${CMAKE_CURRENT_SOURCE_DIR}/example/lib_api/mylib.cc)
696+
target_include_directories(sample_lib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include/mxnet)
695697
set(MXNET_INSTALL_TARGETS mxnet)
696698
if(UNIX)
697699
# Create dummy file since we want an empty shared library before linking
@@ -702,9 +704,13 @@ if(UNIX)
702704
add_library(mxnet SHARED ${DUMMY_SOURCE})
703705
target_link_libraries(mxnet PRIVATE ${BEGIN_WHOLE_ARCHIVE} $<TARGET_FILE:mxnet_static> ${END_WHOLE_ARCHIVE})
704706
target_link_libraries(mxnet PRIVATE mxnet_static)
707+
target_link_libraries(mxnet_static PUBLIC ${CMAKE_DL_LIBS})
708+
target_compile_options(sample_lib PUBLIC -shared)
705709
set_target_properties(mxnet_static PROPERTIES OUTPUT_NAME mxnet)
706710
else()
707711
add_library(mxnet SHARED ${SOURCE})
712+
target_compile_options(sample_lib PUBLIC /LD)
713+
set_target_properties(sample_lib PROPERTIES PREFIX "lib")
708714
endif()
709715

710716
if(USE_CUDA)

Makefile

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ else
107107
CFLAGS += -O3 -DNDEBUG=1
108108
endif
109109
CFLAGS += -I$(TPARTYDIR)/mshadow/ -I$(TPARTYDIR)/dmlc-core/include -fPIC -I$(NNVM_PATH)/include -I$(DLPACK_PATH)/include -I$(TPARTYDIR)/tvm/include -Iinclude $(MSHADOW_CFLAGS)
110-
LDFLAGS = -pthread $(MSHADOW_LDFLAGS) $(DMLC_LDFLAGS)
110+
LDFLAGS = -pthread -ldl $(MSHADOW_LDFLAGS) $(DMLC_LDFLAGS)
111111

112112
ifeq ($(ENABLE_TESTCOVERAGE), 1)
113113
CFLAGS += --coverage
@@ -453,7 +453,7 @@ endif
453453
.PHONY: clean all extra-packages test lint docs clean_all rcpplint rcppexport roxygen\
454454
cython2 cython3 cython cyclean
455455

456-
all: lib/libmxnet.a lib/libmxnet.so $(BIN) extra-packages
456+
all: lib/libmxnet.a lib/libmxnet.so $(BIN) extra-packages sample_lib
457457

458458
SRC = $(wildcard src/*/*/*/*.cc src/*/*/*.cc src/*/*.cc src/*.cc)
459459
OBJ = $(patsubst %.cc, build/%.o, $(SRC))
@@ -658,6 +658,9 @@ cpplint:
658658
pylint:
659659
python3 -m pylint --rcfile=$(ROOTDIR)/ci/other/pylintrc --ignore-patterns=".*\.so$$,.*\.dll$$,.*\.dylib$$" python/mxnet tools/caffe_converter/*.py
660660

661+
sample_lib:
662+
$(CXX) -shared -fPIC example/lib_api/mylib.cc -o libsample_lib.so -I include/mxnet
663+
661664
doc: docs
662665

663666
docs:

ci/jenkins/Jenkins_steps.groovy

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323
utils = load('ci/Jenkinsfile_utils.groovy')
2424

2525
// mxnet libraries
26-
mx_lib = 'lib/libmxnet.so, lib/libmxnet.a, 3rdparty/dmlc-core/libdmlc.a, 3rdparty/tvm/nnvm/lib/libnnvm.a'
27-
mx_lib_cython = 'lib/libmxnet.so, lib/libmxnet.a, 3rdparty/dmlc-core/libdmlc.a, 3rdparty/tvm/nnvm/lib/libnnvm.a, python/mxnet/_cy2/*.so, python/mxnet/_cy3/*.so'
26+
mx_lib = 'lib/libmxnet.so, lib/libmxnet.a, libsample_lib.so, 3rdparty/dmlc-core/libdmlc.a, 3rdparty/tvm/nnvm/lib/libnnvm.a'
27+
mx_lib_cython = 'lib/libmxnet.so, lib/libmxnet.a, libsample_lib.so, 3rdparty/dmlc-core/libdmlc.a, 3rdparty/tvm/nnvm/lib/libnnvm.a, python/mxnet/_cy2/*.so, python/mxnet/_cy3/*.so'
2828

2929
// Python wheels
3030
mx_pip = 'build/*.whl'
@@ -33,11 +33,11 @@ mx_pip = 'build/*.whl'
3333
mx_cmake_lib = 'build/libmxnet.so, build/libmxnet.a, build/3rdparty/dmlc-core/libdmlc.a, build/tests/mxnet_unit_tests, build/3rdparty/openmp/runtime/src/libomp.so'
3434
mx_cmake_lib_cython = 'build/libmxnet.so, build/libmxnet.a, 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'
3535
// mxnet cmake libraries, in cmake builds we do not produce a libnvvm static library by default.
36-
mx_cmake_lib_debug = 'build/libmxnet.so, build/libmxnet.a, build/3rdparty/dmlc-core/libdmlc.a, build/tests/mxnet_unit_tests'
36+
mx_cmake_lib_debug = 'build/libmxnet.so, build/libmxnet.a, build/libsample_lib.so, build/3rdparty/dmlc-core/libdmlc.a, build/tests/mxnet_unit_tests'
3737
mx_cmake_mkldnn_lib = 'build/libmxnet.so, build/libmxnet.a, build/3rdparty/dmlc-core/libdmlc.a, build/tests/mxnet_unit_tests, build/3rdparty/openmp/runtime/src/libomp.so, build/3rdparty/mkldnn/src/libmkldnn.so.0'
38-
mx_mkldnn_lib = 'lib/libmxnet.so, lib/libmxnet.a, lib/libiomp5.so, lib/libmkldnn.so.0, lib/libmklml_intel.so, 3rdparty/dmlc-core/libdmlc.a, 3rdparty/tvm/nnvm/lib/libnnvm.a'
38+
mx_mkldnn_lib = 'lib/libmxnet.so, lib/libmxnet.a, libsample_lib.so, lib/libiomp5.so, lib/libmkldnn.so.0, lib/libmklml_intel.so, 3rdparty/dmlc-core/libdmlc.a, 3rdparty/tvm/nnvm/lib/libnnvm.a'
3939
mx_tensorrt_lib = 'build/libmxnet.so, lib/libnvonnxparser_runtime.so.0, lib/libnvonnxparser.so.0, lib/libonnx_proto.so, lib/libonnx.so'
40-
mx_lib_cpp_examples = 'lib/libmxnet.so, lib/libmxnet.a, 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'
40+
mx_lib_cpp_examples = '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'
4141
mx_lib_cpp_examples_cpu = 'build/libmxnet.so, build/cpp-package/example/*'
4242

4343
// Python unittest for CPU

example/lib_api/Makefile

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
18+
all:
19+
g++ -shared -fPIC mylib.cc -o mylib.so -I ../../include/mxnet
20+
21+
test:
22+
g++ -std=c++11 -O3 -o libtest libtest.cc -ldl -I ../../include/mxnet
23+
24+
windows:
25+
cl /LD mylib.cc
26+
27+
win_test:
28+
cl libtest.cc
29+
30+
clean:
31+
rm -rf mylib.so libtest

example/lib_api/libtest.cc

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
/*!
21+
* Copyright (c) 2015 by Contributors
22+
* \file libtest.cc
23+
* \brief This test checks if the library is implemented correctly
24+
* and does not involve dynamic loading of library into MXNet
25+
* This test is supposed to be run before test.py
26+
*/
27+
28+
#if defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__)
29+
#include <windows.h>
30+
#else
31+
#include <dlfcn.h>
32+
#endif
33+
34+
#include <iostream>
35+
#include "lib_api.h"
36+
37+
#define MXNET_VERSION 10500
38+
39+
int main(void) {
40+
// Get a handle to the library.
41+
#if defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__)
42+
HINSTANCE handle;
43+
handle = LoadLibrary(TEXT("mylib.dll"));
44+
#else
45+
void *handle;
46+
handle = dlopen("mylib.so", RTLD_LAZY);
47+
#endif
48+
49+
if (!handle) {
50+
std::cerr << "Unable to load library" << std::endl;
51+
return 1;
52+
}
53+
54+
// get initialize function address from the library
55+
initialize_t init_lib;
56+
#if defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__)
57+
init_lib = (initialize_t) GetProcAddress(handle, MXLIB_INITIALIZE_STR);
58+
#else
59+
init_lib = (initialize_t) dlsym(handle, MXLIB_INITIALIZE_STR);
60+
#endif
61+
62+
if (!init_lib) {
63+
std::cerr << "Unable to get function 'intialize' from library" << std::endl;
64+
return 1;
65+
}
66+
67+
// Call the function.
68+
(init_lib)(MXNET_VERSION);
69+
70+
// Deallocate memory.
71+
#if defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__)
72+
FreeLibrary(handle);
73+
#else
74+
dlclose(handle);
75+
#endif
76+
77+
return 0;
78+
}

example/lib_api/mylib.cc

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
/*!
21+
* Copyright (c) 2015 by Contributors
22+
* \file mylib.cc
23+
* \brief Sample library file
24+
*/
25+
26+
#include <iostream>
27+
#include "lib_api.h"
28+
29+
int initialize(int version) {
30+
if (version >= 10400) {
31+
std::cout << "MXNet version " << version << " supported" << std::endl;
32+
return 1;
33+
} else {
34+
std::cout << "MXNet version " << version << " not supported" << std::endl;
35+
return 0;
36+
}
37+
}

example/lib_api/test.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#!/usr/bin/env python3
2+
3+
# Licensed to the Apache Software Foundation (ASF) under one
4+
# or more contributor license agreements. See the NOTICE file
5+
# distributed with this work for additional information
6+
# regarding copyright ownership. The ASF licenses this file
7+
# to you under the Apache License, Version 2.0 (the
8+
# "License"); you may not use this file except in compliance
9+
# with the License. You may obtain a copy of the License at
10+
#
11+
# http://www.apache.org/licenses/LICENSE-2.0
12+
#
13+
# Unless required by applicable law or agreed to in writing,
14+
# software distributed under the License is distributed on an
15+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
# KIND, either express or implied. See the License for the
17+
# specific language governing permissions and limitations
18+
# under the License.
19+
20+
# coding: utf-8
21+
# pylint: disable=arguments-differ
22+
23+
# This test checks if dynamic loading of library into MXNet is successful
24+
25+
import mxnet as mx
26+
import os
27+
28+
if (os.name=='posix'):
29+
mx.library.load('mylib.so')
30+
elif (os.name=='nt'):
31+
mx.library.load('mylib.dll')

include/mxnet/c_api.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,13 @@ MXNET_DLL const char *MXGetLastError();
226226
// Part 0: Global State setups
227227
//-------------------------------------
228228

229+
/*!
230+
* \brief Load library dynamically
231+
* \param path to the library .so file
232+
* \return 0 when success, -1 when failure happens.
233+
*/
234+
MXNET_DLL int MXLoadLib(const char *path);
235+
229236
/*!
230237
* \brief Get list of features supported on the runtime
231238
* \param libFeature pointer to array of LibFeature

include/mxnet/lib_api.h

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
/*!
21+
* Copyright (c) 2015 by Contributors
22+
* \file lib_api.h
23+
* \brief APIs to interact with libraries
24+
*/
25+
#ifndef MXNET_LIB_API_H_
26+
#define MXNET_LIB_API_H_
27+
28+
/*!
29+
* \brief Following are the APIs implemented in the external library
30+
* Each API has a #define string that is used to lookup the function in the library
31+
* Followed by the function declaration
32+
*/
33+
#define MXLIB_INITIALIZE_STR "initialize"
34+
typedef int (*initialize_t)(int);
35+
36+
extern "C" {
37+
/*!
38+
* \brief Checks if the MXNet version is supported by the library.
39+
* If supported, initializes the library.
40+
* \param version MXNet version number passed to library and defined as:
41+
* MXNET_VERSION = (MXNET_MAJOR*10000 + MXNET_MINOR*100 + MXNET_PATCH)
42+
* \return Non-zero value on error i.e. library incompatible with passed MXNet version
43+
*/
44+
#if defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__)
45+
__declspec(dllexport) int __cdecl initialize(int);
46+
#else
47+
int initialize(int);
48+
#endif
49+
}
50+
#endif // MXNET_LIB_API_H_

python/mxnet/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from .base import MXNetError
2727
from .util import is_np_shape, set_np_shape, np_shape, use_np_shape
2828
from . import base
29+
from . import library
2930
from . import contrib
3031
from . import ndarray
3132
from . import ndarray as nd

python/mxnet/base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ def __repr__(self):
8686

8787

8888
class MXNetError(Exception):
89-
"""Error that will be throwed by all mxnet functions."""
89+
"""Error that will be thrown by all mxnet functions."""
9090
pass
9191

9292

python/mxnet/library.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
18+
# coding: utf-8
19+
"""Library management API of mxnet."""
20+
from __future__ import absolute_import
21+
import ctypes
22+
import os
23+
from .base import _LIB, check_call, MXNetError
24+
25+
def load(path):
26+
"""Loads library dynamically.
27+
28+
Parameters
29+
---------
30+
path : Path to library .so/.dll file
31+
32+
Returns
33+
---------
34+
void
35+
"""
36+
#check if path exists
37+
if not os.path.exists(path):
38+
raise MXNetError("load path %s does NOT exist" % path)
39+
#check if path is an absolute path
40+
if not os.path.isabs(path):
41+
raise MXNetError("load path %s is not an absolute path" % path)
42+
#check if path is to a library file
43+
_, file_ext = os.path.splitext(path)
44+
if not file_ext in ['.so', '.dll']:
45+
raise MXNetError("load path %s is NOT a library file" % path)
46+
47+
byt_obj = path.encode('utf-8')
48+
chararr = ctypes.c_char_p(byt_obj)
49+
check_call(_LIB.MXLoadLib(chararr))

0 commit comments

Comments
 (0)