diff --git a/CMakeLists.txt b/CMakeLists.txt index 2f974e8b987c..d8ef524bb389 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -294,8 +294,8 @@ else() add_definitions(-DMXNET_USE_NCCL=0) endif() +include(cmake/ChooseBlas.cmake) if(USE_CUDA AND FIRST_CUDA) - include(cmake/ChooseBlas.cmake) include(3rdparty/mshadow/cmake/Utils.cmake) include(cmake/FirstClassLangCuda.cmake) include_directories(${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES}) diff --git a/include/mxnet/base.h b/include/mxnet/base.h index 7f12643119f7..26c1a1bd2b29 100644 --- a/include/mxnet/base.h +++ b/include/mxnet/base.h @@ -35,7 +35,7 @@ #include "nnvm/op.h" #include "nnvm/tuple.h" #include "nnvm/symbolic.h" -#include "mxfeatures.h" +#include "libinfo.h" /*! @@ -403,7 +403,14 @@ template<> struct hash { return res; } }; + +#if __cplusplus < 201402L && !defined(_MSC_VER) +template +inline std::unique_ptr make_unique(Args&&... args) { + return std::unique_ptr(new T(std::forward(args)...)); } +#endif +} // namespace std #include "./tensor_blob.h" //! \endcond diff --git a/include/mxnet/c_api.h b/include/mxnet/c_api.h index d6e13ebcf051..e5e57c10faaa 100644 --- a/include/mxnet/c_api.h +++ b/include/mxnet/c_api.h @@ -139,6 +139,12 @@ struct MXCallbackList { void **contexts; }; +struct LibFeature { + const char* name; + uint32_t index; + bool enabled; +}; + enum CustomOpCallbacks { kCustomOpDelete, kCustomOpForward, @@ -210,12 +216,12 @@ MXNET_DLL const char *MXGetLastError(); //------------------------------------- /*! - * \brief - * \param feature to check mxfeatures.h - * \param out set to true if the feature is enabled, false otherwise + * \brief Get list of features supported on the runtime + * \param libFeature pointer to array of LibFeature + * \param size of the array * \return 0 when success, -1 when failure happens. */ -MXNET_DLL int MXHasFeature(const mx_uint feature, bool* out); +MXNET_DLL int MXLibInfoFeatures(const struct LibFeature **libFeature, size_t *size); /*! * \brief Seed all global random number generators in mxnet. diff --git a/include/mxnet/mxfeatures.h b/include/mxnet/libinfo.h similarity index 82% rename from include/mxnet/mxfeatures.h rename to include/mxnet/libinfo.h index 10f9b3656692..f35d41a9aa8a 100644 --- a/include/mxnet/mxfeatures.h +++ b/include/mxnet/libinfo.h @@ -18,21 +18,27 @@ */ /*! - * Copyright (c) 2018 by Contributors - * \file mxfeatures.h - * \brief check MXNet features including compile time support + * Copyright (c) 2018 by Contributors + * \file libinfo.h + * \author larroy + * \brief get features of the MXNet library at runtime */ #pragma once +#include +#include +#include +#include #include "dmlc/base.h" #include "mshadow/base.h" +#include "c_api.h" /*! *\brief whether to use opencv support */ #ifndef MXNET_USE_OPENCV -#define MXNET_USE_OPENCV 1 +#define MXNET_USE_OPENCV 0 #endif /*! @@ -124,7 +130,8 @@ namespace features { // Check compile flags such as CMakeLists.txt /// Compile time features -enum : uint32_t { +// ATTENTION: When changing this enum, match the strings in the implementation file! +enum : unsigned { // NVIDIA, CUDA CUDA = 0, CUDNN, @@ -179,10 +186,25 @@ enum : uint32_t { }; +struct EnumNames { + static const std::vector names; +}; + +struct LibInfo { + LibInfo(); + static LibInfo* getInstance(); + const std::array& getFeatures() { + return m_lib_features; + } + private: + std::array m_lib_features; + static std::unique_ptr m_inst; +}; + /*! * \return true if the given feature is supported */ -bool is_enabled(uint32_t feat); +bool is_enabled(unsigned feat); } // namespace features } // namespace mxnet diff --git a/python/mxnet/mxfeatures.py b/python/mxnet/mxfeatures.py deleted file mode 100644 index c546151ab06d..000000000000 --- a/python/mxnet/mxfeatures.py +++ /dev/null @@ -1,103 +0,0 @@ -# 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=not-an-iterable - -"""runtime detection of compile time features in the native library""" - -import ctypes -import enum -from .base import _LIB, check_call, mx_uint - -feature_names = [ - "CUDA", - "CUDNN", - "NCCL", - "CUDA_RTC", - "TENSORRT", - "CPU_SSE", - "CPU_SSE2", - "CPU_SSE3", - "CPU_SSE4_1", - "CPU_SSE4_2", - "CPU_SSE4A", - "CPU_AVX", - "CPU_AVX2", - "OPENMP", - "SSE", - "F16C", - "JEMALLOC", - "BLAS_OPEN", - "BLAS_ATLAS", - "BLAS_MKL", - "BLAS_APPLE", - "LAPACK", - "MKLDNN", - "OPENCV", - "CAFFE", - "PROFILER", - "DIST_KVSTORE", - "CXX14", - "SIGNAL_HANDLER", - "DEBUG" -] - - -Feature = enum.Enum('Feature', {name: index for index, name in enumerate(feature_names)}) - - -def has_feature(feature): - """ - Check the library for compile-time feature at runtime - - Parameters - ---------- - feature : int - An integer representing the feature to check - - Returns - ------- - boolean - True if the feature is enabled, false otherwise - """ - res = ctypes.c_bool() - check_call(_LIB.MXHasFeature(mx_uint(feature), ctypes.byref(res))) - return res.value - - -def features_enabled(): - """ - Returns - ------- - features: list of Feature - list of enabled features in the back-end - """ - res = [] - for f in Feature: - if has_feature(f.value): - res.append(f) - return res - -def features_enabled_str(sep=', '): - """ - Returns - ------- - string with a comma separated list of enabled features in the back-end. For example: - "CPU_SSE, OPENMP, F16C, LAPACK, MKLDNN, OPENCV, SIGNAL_HANDLER, DEBUG" - """ - return sep.join(map(lambda x: x.name, features_enabled())) diff --git a/python/mxnet/runtime.py b/python/mxnet/runtime.py new file mode 100644 index 000000000000..afb393281420 --- /dev/null +++ b/python/mxnet/runtime.py @@ -0,0 +1,48 @@ +# 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=not-an-iterable + +"""runtime querying of compile time features in the native library""" + +import ctypes +from .base import _LIB, check_call + +class LibFeature(ctypes.Structure): + """ + Compile time feature description + """ + _fields_ = [ + ("name", ctypes.c_char_p), + ("index", ctypes.c_uint32), + ("enabled", ctypes.c_bool) + ] + +def libinfo_features(): + """ + Check the library for compile-time features. The list of features are maintained in libinfo.h and libinfo.cc + + Returns + ------- + A list of class LibFeature indicating which features are available and enabled + """ + lib_features = ctypes.POINTER(LibFeature)() + lib_features_size = ctypes.c_size_t() + check_call(_LIB.MXLibInfoFeatures(ctypes.byref(lib_features), ctypes.byref(lib_features_size))) + feature_list = [lib_features[i] for i in range(lib_features_size.value)] + return feature_list diff --git a/src/c_api/c_api.cc b/src/c_api/c_api.cc index b436e8ca601b..7e03acccdfae 100644 --- a/src/c_api/c_api.cc +++ b/src/c_api/c_api.cc @@ -43,7 +43,7 @@ #include "mxnet/kvstore.h" #include "mxnet/rtc.h" #include "mxnet/storage.h" -#include "mxnet/mxfeatures.h" +#include "mxnet/libinfo.h" #include "./c_api_common.h" #include "../operator/custom/custom-inl.h" #include "../operator/tensor/matrix_op-inl.h" @@ -87,9 +87,12 @@ inline int MXAPIGetFunctionRegInfo(const FunRegType *e, // NOTE: return value is added in API_END -int MXHasFeature(const mx_uint feature, bool* out) { +int MXLibInfoFeatures(const struct LibFeature **lib_features, size_t *size) { + using namespace features; API_BEGIN(); - *out = features::is_enabled(feature); + LibInfo* lib_info = LibInfo::getInstance(); + *lib_features = lib_info->getFeatures().data(); + *size = lib_info->getFeatures().size(); API_END(); } diff --git a/src/c_api/c_api_profile.cc b/src/c_api/c_api_profile.cc index dc1b7810147f..0de7b485531c 100644 --- a/src/c_api/c_api_profile.cc +++ b/src/c_api/c_api_profile.cc @@ -52,11 +52,6 @@ struct APICallTimingData { #endif // PROFILE_API_INCLUDE_AS_EVENT }; -template -inline std::unique_ptr make_unique(Args&&... args) { - return std::unique_ptr(new T(std::forward(args)...)); -} - /*! * \brief Per-thread profiling data */ @@ -78,7 +73,7 @@ class ProfilingThreadData { auto iter = tasks_.find(name); if (iter == tasks_.end()) { iter = tasks_.emplace(std::make_pair( - name, make_unique(name, domain))).first; + name, std::make_unique(name, domain))).first; } return iter->second.get(); } @@ -93,7 +88,8 @@ class ProfilingThreadData { // Per-thread so no lock necessary auto iter = events_.find(name); if (iter == events_.end()) { - iter = events_.emplace(std::make_pair(name, make_unique(name))).first; + iter = events_.emplace(std::make_pair(name, + std::make_unique(name))).first; } return iter->second.get(); } diff --git a/src/mxfeatures.cc b/src/libinfo.cc similarity index 75% rename from src/mxfeatures.cc rename to src/libinfo.cc index 7a435d7c81c9..44a834c85b16 100644 --- a/src/mxfeatures.cc +++ b/src/libinfo.cc @@ -19,12 +19,14 @@ /*! * Copyright (c) 2018 by Contributors - * \file mxfeatures.cc + * \file libinfo.cc + * \author larroy * \brief check MXNet features including compile time support */ -#include "mxnet/mxfeatures.h" +#include "mxnet/libinfo.h" #include +#include "mxnet/base.h" namespace mxnet { namespace features { @@ -108,5 +110,54 @@ bool is_enabled(const unsigned feat) { return featureSet.is_enabled(feat); } +LibInfo::LibInfo() { + for (size_t i = 0; i < MAX_FEATURES; ++i) { + m_lib_features[i].name = EnumNames::names[i].c_str(); + m_lib_features[i].enabled = is_enabled(i); + m_lib_features[i].index = i; + } +} + +LibInfo *LibInfo::getInstance() { + if (!m_inst) + m_inst = std::make_unique(); + return m_inst.get(); +} + +std::unique_ptr LibInfo::m_inst = nullptr; + +const std::vector EnumNames::names = { + "CUDA", + "CUDNN", + "NCCL", + "CUDA_RTC", + "TENSORRT", + "CPU_SSE", + "CPU_SSE2", + "CPU_SSE3", + "CPU_SSE4_1", + "CPU_SSE4_2", + "CPU_SSE4A", + "CPU_AVX", + "CPU_AVX2", + "OPENMP", + "SSE", + "F16C", + "JEMALLOC", + "BLAS_OPEN", + "BLAS_ATLAS", + "BLAS_MKL", + "BLAS_APPLE", + "LAPACK", + "MKLDNN", + "OPENCV", + "CAFFE", + "PROFILER", + "DIST_KVSTORE", + "CXX14", + "SIGNAL_HANDLER", + "DEBUG", +}; + } // namespace features } // namespace mxnet diff --git a/tests/cpp/misc/libinfo_test.cc b/tests/cpp/misc/libinfo_test.cc new file mode 100644 index 000000000000..57f8f8d764c3 --- /dev/null +++ b/tests/cpp/misc/libinfo_test.cc @@ -0,0 +1,33 @@ +/* + * 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. + */ + +#include +#include + +using namespace mxnet; +using namespace std; +using namespace mxnet::features; + +/* + * Test that enum and string values are in sync + */ +TEST(RuntimeTest, RuntimeTestAll) { + EXPECT_EQ(EnumNames::names.size(), MAX_FEATURES); + const auto& features = LibInfo::getInstance()->getFeatures(); +} diff --git a/tests/python/unittest/test_features.py b/tests/python/unittest/test_features.py deleted file mode 100644 index ff9118100e41..000000000000 --- a/tests/python/unittest/test_features.py +++ /dev/null @@ -1,40 +0,0 @@ -# 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. - -import mxnet as mx -import sys -from mxnet.mxfeatures import * -from mxnet.base import MXNetError -from nose.tools import * - -def test_runtime_features(): - for f in Feature: - res = has_feature(f.value) - ok_(type(res) is bool) - for f in features_enabled(): - ok_(type(f) is Feature) - ok_(type(features_enabled_str()) is str) - print("Features enabled: {}".format(features_enabled_str())) - -@raises(MXNetError) -def test_has_feature_2large(): - has_feature(sys.maxsize) - - -if __name__ == "__main__": - import nose - nose.runmodule() diff --git a/tests/python/unittest/test_libinfo.py b/tests/python/unittest/test_runtime.py similarity index 71% rename from tests/python/unittest/test_libinfo.py rename to tests/python/unittest/test_runtime.py index 66bf03111d4b..433301819252 100644 --- a/tests/python/unittest/test_libinfo.py +++ b/tests/python/unittest/test_runtime.py @@ -15,16 +15,22 @@ # specific language governing permissions and limitations # under the License. -import os import mxnet as mx -from mxnet import libinfo +import sys +from mxnet.runtime import * +from mxnet.base import MXNetError +from nose.tools import * -def test_include_path(): - incl_path = libinfo.find_include_path() - assert os.path.exists(incl_path) - assert os.path.isdir(incl_path) +def test_libinfo_features(): + features = libinfo_features() + print("Lib features: ") + for f in features: + print(f.name, f.enabled, f.index) + ok_(type(features) is list) + ok_(len(features) > 0) -if __name__ == '__main__': + +if __name__ == "__main__": import nose nose.runmodule()