diff --git a/.gitmodules b/.gitmodules index 0de60d4c80e7..498ba35b6d15 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,9 @@ [submodule "mshadow"] path = mshadow - url = /~https://github.com/dmlc/mshadow.git + url = /~https://github.com/dato-code/mshadow.git [submodule "dmlc-core"] path = dmlc-core - url = /~https://github.com/dmlc/dmlc-core.git + url = /~https://github.com/dato-code/dmlc-core.git [submodule "ps-lite"] path = ps-lite url = /~https://github.com/dmlc/ps-lite diff --git a/Makefile b/Makefile index 4e95f85ea321..7de7c388688f 100644 --- a/Makefile +++ b/Makefile @@ -12,15 +12,10 @@ ifndef DMLC_CORE DMLC_CORE = dmlc-core endif -ifneq ($(USE_OPENMP), 1) - export NO_OPENMP = 1 -endif - # use customized config file include $(config) include mshadow/make/mshadow.mk include $(DMLC_CORE)/make/dmlc.mk -unexport NO_OPENMP # all the possible warning tread WARNFLAGS= -Wall @@ -32,7 +27,16 @@ ifeq ($(DEBUG), 1) else CFLAGS += -O3 endif -CFLAGS += -I./mshadow/ -I./dmlc-core/include -fPIC -Iinclude $(MSHADOW_CFLAGS) + +ifneq ($(WIN32), 1) + CFLAGS += -fPIC + SHARED_LIB_EXT = so + STATIC_LIB_EXT = a +else + SHARED_LIB_EXT = dll + STATIC_LIB_EXT = lib +endif +CFLAGS += -I./mshadow/ -I./dmlc-core/include -Iinclude $(MSHADOW_CFLAGS) LDFLAGS = -pthread $(MSHADOW_LDFLAGS) $(DMLC_LDFLAGS) ifeq ($(DEBUG), 1) NVCCFLAGS = -g -G -O0 -ccbin $(CXX) $(MSHADOW_NVCCFLAGS) @@ -96,7 +100,7 @@ include $(MXNET_PLUGINS) .PHONY: clean all test lint doc clean_all rcpplint rcppexport roxygen -all: lib/libmxnet.a lib/libmxnet.so $(BIN) +all: lib/libmxnet.$(STATIC_LIB_EXT) lib/libmxnet.$(SHARED_LIB_EXT) $(BIN) SRC = $(wildcard src/*.cc src/*/*.cc) OBJ = $(patsubst %.cc, build/%.o, $(SRC)) @@ -177,19 +181,19 @@ $(EXTRA_OPERATORS)/build/%_gpu.o: $(EXTRA_OPERATORS)/%.cu $(NVCC) $(NVCCFLAGS) -Xcompiler "$(CFLAGS) -Isrc/operator" -M -MT $(EXTRA_OPERATORS)/build/$*_gpu.o $< >$(EXTRA_OPERATORS)/build/$*_gpu.d $(NVCC) -c -o $@ $(NVCCFLAGS) -Xcompiler "$(CFLAGS) -Isrc/operator" $< -# NOTE: to statically link libmxnet.a we need the option -# --Wl,--whole-archive -lmxnet --Wl,--no-whole-archive -lib/libmxnet.a: $(ALL_DEP) - @mkdir -p $(@D) - ar crv $@ $(filter %.o, $?) - copy_cuda_deps: ifdef CUDA_DEP @mkdir -p lib cp $(CUDA_DEP) lib endif -lib/libmxnet.so: $(ALL_DEP) copy_cuda_deps +# NOTE: to statically link libmxnet.a we need the option +# --Wl,--whole-archive -lmxnet --Wl,--no-whole-archive +lib/libmxnet.$(STATIC_LIB_EXT): $(ALL_DEP) + @mkdir -p $(@D) + ar crv $@ $(filter %.o, $?) + +lib/libmxnet.$(SHARED_LIB_EXT): $(ALL_DEP) copy_cuda_deps @mkdir -p $(@D) $(CXX) $(CFLAGS) -shared -o $@ $(filter %.o %.a, $^) $(LDFLAGS) diff --git a/configure b/configure index 292476a58284..bd82450128a1 100755 --- a/configure +++ b/configure @@ -242,15 +242,19 @@ CXX=$CXX # OS Dependent Flags if [[ $OSTYPE == linux* ]]; then USE_OPENMP=1 + WIN32=0 USE_BLAS="blas" SHARED_LINKER_FLAGS="-Wl,-rpath,\\\$\$ORIGIN -Wl,-rpath,${PWD}/deps/local/lib64 -Wl,-rpath,${PWD}/deps/local/lib" elif [[ $OSTYPE == darwin* ]]; then USE_OPENMP=0 + WIN32=0 USE_BLAS="apple" SHARED_LINKER_FLAGS="-Wl,-rpath,@loader_path -Wl,-rpath,${PWD}/deps/local/lib64 -Wl,-rpath,${PWD}/deps/local/lib" elif [[ $OSTYPE == msys ]]; then USE_OPENMP=0 - USE_BLAS="blas" + WIN32=1 + USE_BLAS="openblas" + ADD_CFLAGS="-DMXNET_EXPORTS " SHARED_LINKER_FLAGS="-Wl,-rpath,/mingw64/bin -Wl,-rpath,${PWD}/deps/local/lib64 -Wl,-rpath,${PWD}/deps/local/lib" fi diff --git a/dmlc-core b/dmlc-core index ea9b247b6f99..2d9119a06b1f 160000 --- a/dmlc-core +++ b/dmlc-core @@ -1 +1 @@ -Subproject commit ea9b247b6f9965c95aa66f42374d0867c46d9abd +Subproject commit 2d9119a06b1fed465cdc6679cf76557d5964e23d diff --git a/include/mxnet/base.h b/include/mxnet/base.h index 52100cdf05ea..f9880a20b2cc 100644 --- a/include/mxnet/base.h +++ b/include/mxnet/base.h @@ -52,7 +52,7 @@ /*! * \brief define dllexport for Visual Studio */ -#ifdef _MSC_VER +#if defined(_MSC_VER) || defined(_WIN32) #ifdef MXNET_EXPORTS #define MXNET_API __declspec(dllexport) #else diff --git a/include/mxnet/engine.h b/include/mxnet/engine.h index 011beef5c7b9..529d9d01b2bf 100644 --- a/include/mxnet/engine.h +++ b/include/mxnet/engine.h @@ -171,7 +171,9 @@ class MXNET_API Engine { /*!\brief virtual destructor */ virtual ~Engine() noexcept(false) {} /*! - * \return Engine singleton. + * \return Engine singleton. NULL if Shutdown() is called. + * + * \note Check nullptr if used in destructors. */ static Engine* Get(); /*! @@ -180,9 +182,15 @@ class MXNET_API Engine { * This function is called by another singleton X who requires * engine to be destructed after X. * - * \return A shared pointer to Engine singleton. + * \return A ref of the shared pointer to Engine singleton. + */ + static std::shared_ptr& _GetSharedRef(); + + /** + * \brief Destroy the global engine singleton. Future + * access to engine is disabled. */ - static std::shared_ptr _GetSharedRef(); + static void Shutdown(); /*! * \brief Push an synchronous operation to the engine. * \param exec_fn Execution function that executes the operation. diff --git a/include/mxnet/ndarray.h b/include/mxnet/ndarray.h index 811fa2c968a4..60ffd18b5db2 100644 --- a/include/mxnet/ndarray.h +++ b/include/mxnet/ndarray.h @@ -323,13 +323,18 @@ class NDArray { } /*! \brief destructor */ ~Chunk() { + auto engine_ptr = Engine::Get(); if (static_data || delay_alloc) { - Engine::Get()->DeleteVariable([](RunContext s) {}, shandle.ctx, var); + if (engine_ptr) { + engine_ptr->DeleteVariable([](RunContext s) {}, shandle.ctx, var); + } } else { Storage::Handle h = this->shandle; - Engine::Get()->DeleteVariable([h](RunContext s) { - Storage::Get()->Free(h); - }, shandle.ctx, var); + if (engine_ptr) { + Engine::Get()->DeleteVariable([h](RunContext s) { + Storage::Get()->Free(h); + }, shandle.ctx, var); + } } } }; diff --git a/include/mxnet/resource.h b/include/mxnet/resource.h index ad6a6f11fd95..6c63d2b4d394 100644 --- a/include/mxnet/resource.h +++ b/include/mxnet/resource.h @@ -115,6 +115,11 @@ class ResourceManager { * \return Resource manager singleton. */ static ResourceManager *Get(); + + /** + * Destroy the Resource manager singleton. + */ + static void Shutdown(); }; } // namespace mxnet #endif // MXNET_RESOURCE_H_ diff --git a/make/dato-config.mk b/make/dato-config.mk index 482d086a50d0..e77b6931355d 100644 --- a/make/dato-config.mk +++ b/make/dato-config.mk @@ -26,12 +26,15 @@ export NVCC = ${NVCC} # whether compile with debug DEBUG = 0 +WIN32 = ${WIN32} # the additional link flags you want to add -ADD_LDFLAGS = -L${DEPS}/lib -L${DEPS}/lib64 ${SHARED_LINKER_FLAGS} +ADD_LDFLAGS = ${ADD_LDFLAGS} +ADD_LDFLAGS += -L${DEPS}/lib -L${DEPS}/lib64 ${SHARED_LINKER_FLAGS} # the additional compile flags you want to add -ADD_CFLAGS = -I${DEPS}/include +ADD_CFLAGS = ${ADD_CFLAGS} +ADD_CFLAGS += -I${DEPS}/include #--------------------------------------------- # matrix computation libraries for CPU/GPU @@ -58,6 +61,11 @@ USE_OPENCV = 0 # use openmp for parallelization USE_OPENMP = ${USE_OPENMP} +ifneq ($(USE_OPENMP), 1) + export NO_OPENMP = 1 + ADD_CFLAGS += -DDISABLE_OPENMP +endif + # choose the version of blas you want to use # can be: mkl, blas, atlas, openblas diff --git a/mshadow b/mshadow index 29863c50443d..2068a32beeee 160000 --- a/mshadow +++ b/mshadow @@ -1 +1 @@ -Subproject commit 29863c50443d8338cdfcfb5836a4dd854b2b0e75 +Subproject commit 2068a32beeee5ccc7b5a69bccea4f1756dc3bff6 diff --git a/ps-lite b/ps-lite index ca2a28e27a6d..e86dac79d4ae 160000 --- a/ps-lite +++ b/ps-lite @@ -1 +1 @@ -Subproject commit ca2a28e27a6d3b305d14222f5aa44d419a1a8c14 +Subproject commit e86dac79d4ae93274af8abf6c20c91dc118dc9a2 diff --git a/scripts/build.sh b/scripts/build.sh new file mode 100755 index 000000000000..fd64f18a7541 --- /dev/null +++ b/scripts/build.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) +WORKSPACE=${SCRIPT_DIR}/.. + +cd ${WORKSPACE} +source ${WORKSPACE}/scripts/python_env.sh + +if [ -d "/usr/local/cuda-7.5" ]; then + ./configure --cleanup_if_invalid --yes --cuda_path=/usr/local/cuda-7.5 + HAS_GPU=1 +else + ./configure --cleanup_if_invalid --yes +fi + +make clean_all +make -j4 + +if [[ $OSTYPE != msys ]]; then + nosecmd="${PYTHON_EXECUTABLE} ${NOSETEST_EXECUTABLE}" +else + nosecmd="${NOSETEST_EXECUTABLE}" +fi + +if [ ! -z "$HAS_GPU" ]; then + ${nosecmd} -v --with-id ${WORKSPACE}/tests/python/unittest ${WORKSPACE}/tests/python/train ${WORKSPACE}/tests/python/gpu --with-xunit --xunit-file=alltests.nosetests.xml +else + ${nosecmd} -v --with-id ${WORKSPACE}/tests/python/unittest ${WORKSPACE}/tests/python/train --with-xunit --xunit-file=alltests.nosetests.xml +fi diff --git a/src/c_api/c_api.cc b/src/c_api/c_api.cc index 407a1a3696ae..b030f8956373 100644 --- a/src/c_api/c_api.cc +++ b/src/c_api/c_api.cc @@ -107,6 +107,8 @@ int MXRandomSeed(int seed) { int MXNotifyShutdown() { API_BEGIN(); Engine::Get()->NotifyShutdown(); + ResourceManager::Shutdown(); + Engine::Shutdown(); API_END(); } diff --git a/src/common/object_pool.h b/src/common/object_pool.h index ac12e2b30c6e..3717bb1b9cc8 100644 --- a/src/common/object_pool.h +++ b/src/common/object_pool.h @@ -52,7 +52,7 @@ class ObjectPool { * \brief Internal structure to hold pointers. */ struct LinkedList { -#if defined(_MSC_VER) +#if defined(_MSC_VER) || defined(_WIN32) T t; LinkedList* next{nullptr}; #else @@ -168,7 +168,7 @@ void ObjectPool::AllocateChunk() { static_assert(alignof(LinkedList) % alignof(T) == 0, "ObjectPooll Invariant"); static_assert(kPageSize % alignof(LinkedList) == 0, "ObjectPooll Invariant"); void* new_chunk_ptr; -#ifdef _MSC_VER +#if defined(_MSC_VER) || defined(_WIN32) new_chunk_ptr = _aligned_malloc(kPageSize, kPageSize); CHECK_NE(new_chunk_ptr, NULL) << "Allocation failed"; #else diff --git a/src/engine/engine.cc b/src/engine/engine.cc index ae72861260e1..2067e3b5c44b 100644 --- a/src/engine/engine.cc +++ b/src/engine/engine.cc @@ -39,13 +39,29 @@ inline Engine* CreateEngine() { } } // namespace engine -std::shared_ptr Engine::_GetSharedRef() { + +static bool engine_shutdown = false; + +std::shared_ptr& Engine::_GetSharedRef() { + if (engine_shutdown) LOG(FATAL) << "Call GetRef() after engine already shutdown" << std::endl; static std::shared_ptr sptr(engine::CreateEngine()); return sptr; } Engine* Engine::Get() { + if (engine_shutdown) { + return nullptr; + } static Engine *inst = _GetSharedRef().get(); return inst; } + +void Engine::Shutdown() { + if (!engine_shutdown) { + Engine::Get()->WaitForAll(); + _GetSharedRef().reset(); + engine_shutdown = true; + } +} + } // namespace mxnet diff --git a/src/engine/threaded_engine_perdevice.cc b/src/engine/threaded_engine_perdevice.cc index 62738cbaedac..5e981b547435 100644 --- a/src/engine/threaded_engine_perdevice.cc +++ b/src/engine/threaded_engine_perdevice.cc @@ -38,9 +38,10 @@ class ThreadedEnginePerDevice : public ThreadedEngine { // create CPU task int cpu_priority_nthreads = dmlc::GetEnv("MXNET_CPU_PRIORITY_NTHREADS", 4); cpu_priority_worker_.reset(new ThreadWorkerBlock()); + auto blockptr = cpu_priority_worker_.get(); cpu_priority_worker_->pool.reset(new ThreadPool( - cpu_priority_nthreads, [this] { - this->CPUWorker(cpu_priority_worker_.get()); + cpu_priority_nthreads, [this, blockptr] { + this->CPUWorker(blockptr); })); // GPU tasks will be created lazily } diff --git a/src/resource.cc b/src/resource.cc index b791960fa812..87094f9e7f2d 100644 --- a/src/resource.cc +++ b/src/resource.cc @@ -190,8 +190,22 @@ class ResourceManagerImpl : public ResourceManager { }; } // namespace resource +static resource::ResourceManagerImpl* instance_ptr = nullptr; +static bool rm_shutdown = false; + ResourceManager* ResourceManager::Get() { - static resource::ResourceManagerImpl inst; - return &inst; + if (instance_ptr == nullptr) { + if (rm_shutdown) LOG(FATAL) << "Resource manager already shutdone" << std::endl; + instance_ptr = new resource::ResourceManagerImpl(); + } + return instance_ptr; +} + +void ResourceManager::Shutdown() { + if (instance_ptr != nullptr) { + delete instance_ptr; + instance_ptr = nullptr; + rm_shutdown = true; + } } } // namespace mxnet diff --git a/src/storage/cpu_device_storage.h b/src/storage/cpu_device_storage.h index 6838af037535..bfe364302528 100644 --- a/src/storage/cpu_device_storage.h +++ b/src/storage/cpu_device_storage.h @@ -38,7 +38,7 @@ class CPUDeviceStorage { }; // class CPUDeviceStorage inline void* CPUDeviceStorage::Alloc(size_t size) { -#if _MSC_VER +#if defined(_MSC_VER) || defined(_WIN32) void* ptr; ptr = _aligned_malloc(size, alignment_); return CHECK_NOTNULL(ptr); @@ -51,7 +51,7 @@ inline void* CPUDeviceStorage::Alloc(size_t size) { } inline void CPUDeviceStorage::Free(void* ptr) { -#if _MSC_VER +#if defined(_MSC_VER) || defined(_WIN32) _aligned_free(ptr); #else free(ptr); diff --git a/src/symbol/graph_executor.cc b/src/symbol/graph_executor.cc index c00691bb6daf..c27b2244c7bd 100644 --- a/src/symbol/graph_executor.cc +++ b/src/symbol/graph_executor.cc @@ -261,10 +261,13 @@ GraphExecutor::GetOpExecEntry(uint32_t nid) { } GraphExecutor::~GraphExecutor() { - Engine::Get()->WaitForAll(); - // need to delete the operators before delete the NDArray they referenced. - for (OpNode& node : op_nodes_) { - node.DeleteOperator(); + auto engine_ptr = Engine::Get(); + if (engine_ptr) { + engine_ptr->WaitForAll(); + // need to delete the operators before delete the NDArray they referenced. + for (OpNode& node : op_nodes_) { + node.DeleteOperator(); + } } } diff --git a/tests/python/common/get_data.py b/tests/python/common/get_data.py index e880a9ab0b8d..4a3afd9f1f19 100644 --- a/tests/python/common/get_data.py +++ b/tests/python/common/get_data.py @@ -15,14 +15,14 @@ def download_file(url, target_file): # download mnist.pkl.gz def GetMNIST_pkl(): if not os.path.isdir("data/"): - os.system("mkdir data/") + os.mkdir("data") if not os.path.exists('data/mnist.pkl.gz'): download_file("http://deeplearning.net/data/mnist/mnist.pkl.gz", "data/mnist.pkl.gz") # download ubyte version of mnist and untar def GetMNIST_ubyte(): if not os.path.isdir("data/"): - os.system("mkdir data/") + os.mkdir("data") if (not os.path.exists('data/train-images-idx3-ubyte')) or \ (not os.path.exists('data/train-labels-idx1-ubyte')) or \ (not os.path.exists('data/t10k-images-idx3-ubyte')) or \ @@ -36,7 +36,7 @@ def GetMNIST_ubyte(): # download cifar def GetCifar10(): if not os.path.isdir("data/"): - os.system("mkdir data/") + os.mkdir("data") if not os.path.exists('data/cifar10.zip'): download_file("http://webdocs.cs.ualberta.ca/~bx3/data/cifar10.zip", "data/cfar10.zip") os.chdir("./data")