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

Dynamic Library Loading Support #15489

Merged
merged 38 commits into from
Aug 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
a48210e
Accelerator APIs header file
mseth10 Jul 8, 2019
63958da
rebaseing with master
mseth10 Jul 18, 2019
5f9d6fa
adding example to test accelerator loading
mseth10 Jul 12, 2019
4f9a021
adding c_api function header
mseth10 Jul 15, 2019
d519695
modifying header file path
mseth10 Jul 15, 2019
602a372
creating templates for call to lib fns
mseth10 Jul 15, 2019
565dbd2
modifying target to mxnet_static for libdl
Jul 18, 2019
3dc7832
returning nullptr handle if library not loaded
mseth10 Jul 19, 2019
001bbb1
refactoring code to load libraries dynamically
Jul 23, 2019
3e93d81
addressing review comments
mseth10 Jul 23, 2019
35913da
using static cast
mseth10 Jul 23, 2019
87fa3cf
pylint fix
mseth10 Jul 23, 2019
0dad266
moving library.h file to src/common/
mseth10 Jul 23, 2019
16afd96
adding dynamic loading support for windows
mseth10 Jul 23, 2019
f265d04
updating header guard
mseth10 Jul 23, 2019
955cf98
fixing headers
mseth10 Jul 23, 2019
7d3d530
fixing windows casting error
mseth10 Jul 23, 2019
3e846c4
declaring library functions for windows
mseth10 Jul 24, 2019
bc4cbd2
adding library testing module in examples
mseth10 Jul 25, 2019
53e71df
adding unit test to test library loading
mseth10 Jul 25, 2019
db84009
correcting file names
mseth10 Jul 25, 2019
25a9ccb
updating error messages
mseth10 Jul 25, 2019
1682af3
getting error message from DL library
mseth10 Jul 25, 2019
7551313
adding unit test to gpu suite
mseth10 Jul 25, 2019
68a4e6c
correcting windows pointer
mseth10 Jul 25, 2019
e8c8d2f
requiring absolute path to library
mseth10 Jul 26, 2019
71ac937
changing file description
mseth10 Jul 26, 2019
4cb4caf
addressing review comments - adding more docs, windows error msg
mseth10 Jul 30, 2019
4e2dba8
addressing PR comments
mseth10 Jul 30, 2019
7f8a9fb
checking machine type for unit test
mseth10 Jul 31, 2019
672b2b9
“re-trigger”
mseth10 Aug 1, 2019
2257ff3
added map to store loaded libraries
Aug 2, 2019
0d05d1c
added dlclose calls in naive & threaded engines
samskalicky Aug 2, 2019
3879f61
removed library map declaration in cc file
samskalicky Aug 2, 2019
289c3a5
added windows free
samskalicky Aug 2, 2019
a3b545d
fixed formatting
samskalicky Aug 2, 2019
e512d64
added cast to HMODULE for void* for windows
samskalicky Aug 2, 2019
134cc37
retrigger CI for flaky unix_cpu
samskalicky Aug 2, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,7 @@ if(UNIX)
add_library(mxnet SHARED ${DUMMY_SOURCE})
target_link_libraries(mxnet PRIVATE ${BEGIN_WHOLE_ARCHIVE} $<TARGET_FILE:mxnet_static> ${END_WHOLE_ARCHIVE})
target_link_libraries(mxnet PRIVATE mxnet_static)
target_link_libraries(mxnet_static PUBLIC ${CMAKE_DL_LIBS})
set_target_properties(mxnet_static PROPERTIES OUTPUT_NAME mxnet)
else()
add_library(mxnet SHARED ${SOURCE})
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ else
CFLAGS += -O3 -DNDEBUG=1
endif
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)
LDFLAGS = -pthread $(MSHADOW_LDFLAGS) $(DMLC_LDFLAGS)
LDFLAGS = -pthread -ldl $(MSHADOW_LDFLAGS) $(DMLC_LDFLAGS)

ifeq ($(ENABLE_TESTCOVERAGE), 1)
CFLAGS += --coverage
Expand Down
31 changes: 31 additions & 0 deletions example/lib_api/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# 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:
g++ -shared -fPIC mylib.cc -o mylib.so -I ../../include/mxnet
mseth10 marked this conversation as resolved.
Show resolved Hide resolved

test:
g++ -std=c++11 -O3 -o libtest libtest.cc -ldl -I ../../include/mxnet

windows:
cl /LD mylib.cc

win_test:
cl libtest.cc

clean:
rm -rf mylib.so libtest
78 changes: 78 additions & 0 deletions example/lib_api/libtest.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* 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) 2015 by Contributors
* \file libtest.cc
* \brief This test checks if the library is implemented correctly
* and does not involve dynamic loading of library into MXNet
* This test is supposed to be run before test.py
*/

#if defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__)
#include <windows.h>
#else
#include <dlfcn.h>
#endif

#include <iostream>
#include "lib_api.h"

#define MXNET_VERSION 10500

int main(void) {
// Get a handle to the library.
#if defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__)
HINSTANCE handle;
handle = LoadLibrary(TEXT("mylib.dll"));
#else
void *handle;
handle = dlopen("mylib.so", RTLD_LAZY);
#endif

if (!handle) {
std::cerr << "Unable to load library" << std::endl;
return 1;
}

// get initialize function address from the library
mseth10 marked this conversation as resolved.
Show resolved Hide resolved
initialize_t init_lib;
#if defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__)
init_lib = (initialize_t) GetProcAddress(handle, MXLIB_INITIALIZE_STR);
#else
init_lib = (initialize_t) dlsym(handle, MXLIB_INITIALIZE_STR);
#endif

if (!init_lib) {
std::cerr << "Unable to get function 'intialize' from library" << std::endl;
return 1;
}

// Call the function.
(init_lib)(MXNET_VERSION);

// Deallocate memory.
#if defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__)
FreeLibrary(handle);
#else
dlclose(handle);
#endif

return 0;
}
37 changes: 37 additions & 0 deletions example/lib_api/mylib.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* 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) 2015 by Contributors
* \file mylib.cc
* \brief Sample library file
*/

#include <iostream>
#include "lib_api.h"

int initialize(int version) {
if (version >= 10400) {
std::cout << "MXNet version " << version << " supported" << std::endl;
return 1;
mseth10 marked this conversation as resolved.
Show resolved Hide resolved
} else {
std::cout << "MXNet version " << version << " not supported" << std::endl;
return 0;
}
}
31 changes: 31 additions & 0 deletions example/lib_api/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/usr/bin/env python3

# Licensed to the Apache Software Foundation (ASF) under one
mseth10 marked this conversation as resolved.
Show resolved Hide resolved
# 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

mseth10 marked this conversation as resolved.
Show resolved Hide resolved
# This test checks if dynamic loading of library into MXNet is successful

import mxnet as mx
import os

if (os.name=='posix'):
mx.library.load('mylib.so')
elif (os.name=='nt'):
mx.library.load('mylib.dll')
7 changes: 7 additions & 0 deletions include/mxnet/c_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,13 @@ MXNET_DLL const char *MXGetLastError();
// Part 0: Global State setups
//-------------------------------------

/*!
* \brief Load library dynamically
* \param path to the library .so file
* \return 0 when success, -1 when failure happens.
*/
MXNET_DLL int MXLoadLib(const char *path);

/*!
* \brief Get list of features supported on the runtime
* \param libFeature pointer to array of LibFeature
Expand Down
50 changes: 50 additions & 0 deletions include/mxnet/lib_api.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* 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) 2015 by Contributors
* \file lib_api.h
* \brief APIs to interact with libraries
*/
#ifndef MXNET_LIB_API_H_
#define MXNET_LIB_API_H_

/*!
* \brief Following are the APIs implemented in the external library
* Each API has a #define string that is used to lookup the function in the library
* Followed by the function declaration
*/
#define MXLIB_INITIALIZE_STR "initialize"
typedef int (*initialize_t)(int);

extern "C" {
/*!
* \brief Checks if the MXNet version is supported by the library.
* If supported, initializes the library.
* \param version MXNet version number passed to library and defined as:
* MXNET_VERSION = (MXNET_MAJOR*10000 + MXNET_MINOR*100 + MXNET_PATCH)
* \return Non-zero value on error i.e. library incompatible with passed MXNet version
*/
#if defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__)
__declspec(dllexport) int __cdecl initialize(int);
#else
int initialize(int);
#endif
}
#endif // MXNET_LIB_API_H_
1 change: 1 addition & 0 deletions python/mxnet/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from .base import MXNetError
from .util import is_np_shape, set_np_shape, np_shape, use_np_shape
from . import base
from . import library
from . import contrib
from . import ndarray
from . import ndarray as nd
Expand Down
2 changes: 1 addition & 1 deletion python/mxnet/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def __repr__(self):


class MXNetError(Exception):
"""Error that will be throwed by all mxnet functions."""
"""Error that will be thrown by all mxnet functions."""
pass


Expand Down
49 changes: 49 additions & 0 deletions python/mxnet/library.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# 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
"""Library management API of mxnet."""
from __future__ import absolute_import
import ctypes
import os
from .base import _LIB, check_call, MXNetError

def load(path):
"""Loads library dynamically.

Parameters
---------
path : Path to library .so/.dll file

Returns
---------
void
"""
#check if path exists
if not os.path.exists(path):
mseth10 marked this conversation as resolved.
Show resolved Hide resolved
raise MXNetError("load path %s does NOT exist" % path)
#check if path is an absolute path
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does it have to be an absolute path? Can we clarify? What if the library is installed in /usr/lib?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dynamic loader (dlopen) requires the path to be absolute.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So can't we make it absolute programmatically in a Python level as a convenience for the user?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion @ptrendx. We thought about this. We could, but if the user passes in "./libmyacc.so" and Python is running in a different dir, we're going to make the wrong absolute path "/user/home/libmyacc.so".

So initially we were thinking that it just makes more sense to require the user to provide that as input. It would avoid confusion around "how is this path getting created".

if not os.path.isabs(path):
raise MXNetError("load path %s is not an absolute path" % path)
#check if path is to a library file
_, file_ext = os.path.splitext(path)
if not file_ext in ['.so', '.dll']:
raise MXNetError("load path %s is NOT a library file" % path)

byt_obj = path.encode('utf-8')
chararr = ctypes.c_char_p(byt_obj)
check_call(_LIB.MXLoadLib(chararr))
15 changes: 15 additions & 0 deletions src/c_api/c_api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,13 @@
#include "mxnet/storage.h"
#include "mxnet/libinfo.h"
#include "mxnet/imperative.h"
#include "mxnet/lib_api.h"
#include "./c_api_common.h"
#include "../operator/custom/custom-inl.h"
#include "../operator/tensor/matrix_op-inl.h"
#include "../operator/tvmop/op_module.h"
#include "../common/utils.h"
#include "../common/library.h"

using namespace mxnet;

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

// NOTE: return value is added in API_END

// Loads library and initializes it
int MXLoadLib(const char *path) {
mseth10 marked this conversation as resolved.
Show resolved Hide resolved
API_BEGIN();
void *lib = load_lib(path);
if (!lib)
LOG(FATAL) << "Unable to load library";

initialize_t initialize = get_func<initialize_t>(lib, const_cast<char*>(MXLIB_INITIALIZE_STR));
if (!initialize(static_cast<int>(MXNET_VERSION)))
LOG(FATAL) << "Library failed to initialize";
API_END();
}

int MXLibInfoFeatures(const struct LibFeature **lib_features, size_t *size) {
using namespace features;
API_BEGIN();
Expand Down
Loading