Skip to content
This repository was archived by the owner on Nov 17, 2023. It is now read-only.

Commit 3112893

Browse files
mseth10eric-haibin-lin
authored andcommitted
Dynamic Library Loading Support (#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
1 parent 3255d87 commit 3112893

File tree

18 files changed

+533
-2
lines changed

18 files changed

+533
-2
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,7 @@ if(UNIX)
701701
add_library(mxnet SHARED ${DUMMY_SOURCE})
702702
target_link_libraries(mxnet PRIVATE ${BEGIN_WHOLE_ARCHIVE} $<TARGET_FILE:mxnet_static> ${END_WHOLE_ARCHIVE})
703703
target_link_libraries(mxnet PRIVATE mxnet_static)
704+
target_link_libraries(mxnet_static PUBLIC ${CMAKE_DL_LIBS})
704705
set_target_properties(mxnet_static PROPERTIES OUTPUT_NAME mxnet)
705706
else()
706707
add_library(mxnet SHARED ${SOURCE})

Makefile

Lines changed: 1 addition & 1 deletion
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

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))

src/c_api/c_api.cc

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,13 @@
4545
#include "mxnet/storage.h"
4646
#include "mxnet/libinfo.h"
4747
#include "mxnet/imperative.h"
48+
#include "mxnet/lib_api.h"
4849
#include "./c_api_common.h"
4950
#include "../operator/custom/custom-inl.h"
5051
#include "../operator/tensor/matrix_op-inl.h"
5152
#include "../operator/tvmop/op_module.h"
5253
#include "../common/utils.h"
54+
#include "../common/library.h"
5355

5456
using namespace mxnet;
5557

@@ -90,6 +92,19 @@ inline int MXAPIGetFunctionRegInfo(const FunRegType *e,
9092

9193
// NOTE: return value is added in API_END
9294

95+
// Loads library and initializes it
96+
int MXLoadLib(const char *path) {
97+
API_BEGIN();
98+
void *lib = load_lib(path);
99+
if (!lib)
100+
LOG(FATAL) << "Unable to load library";
101+
102+
initialize_t initialize = get_func<initialize_t>(lib, const_cast<char*>(MXLIB_INITIALIZE_STR));
103+
if (!initialize(static_cast<int>(MXNET_VERSION)))
104+
LOG(FATAL) << "Library failed to initialize";
105+
API_END();
106+
}
107+
93108
int MXLibInfoFeatures(const struct LibFeature **lib_features, size_t *size) {
94109
using namespace features;
95110
API_BEGIN();

0 commit comments

Comments
 (0)