Skip to content

Commit

Permalink
Dynamic Library Loading Support (apache#15489)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
mseth10 authored and Ubuntu committed Aug 20, 2019
1 parent 1977724 commit e639671
Show file tree
Hide file tree
Showing 18 changed files with 533 additions and 2 deletions.
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

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
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;
} 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
# 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

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):
raise MXNetError("load path %s does NOT exist" % path)
#check if path is an absolute path
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) {
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

0 comments on commit e639671

Please sign in to comment.