From 01f195bbbeb7cb06129f56ab94196f17bb0f6fbd Mon Sep 17 00:00:00 2001 From: Pedro Larroy Date: Mon, 18 Feb 2019 20:29:47 +0100 Subject: [PATCH] =?UTF-8?q?Refine=20runtime=20feature=20discovery=20python?= =?UTF-8?q?=20API=20and=20add=20documentation=20to=20=E2=80=A6=20(#14130)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Refine runtime feature discovery python API and add documentation to Python API docs * Fix lint * Provide is_enabled method to check if feature is present from string * Refine docs, add is_enabled * Fix encoding * Fix doc * Address CR suggestions * remove index as per CR suggestion * Fix lint * runtime * Fix doc * Add license --- docs/api/python/index.md | 11 +++++ docs/api/python/libinfo/libinfo.md | 69 +++++++++++++++++++++++++++ include/mxnet/c_api.h | 1 - python/mxnet/runtime.py | 59 +++++++++++++++++++---- src/libinfo.cc | 1 - tests/python/unittest/test_runtime.py | 22 ++++++--- 6 files changed, 145 insertions(+), 18 deletions(-) create mode 100644 docs/api/python/libinfo/libinfo.md diff --git a/docs/api/python/index.md b/docs/api/python/index.md index 6130078ee796..2e23b162ee77 100644 --- a/docs/api/python/index.md +++ b/docs/api/python/index.md @@ -174,6 +174,17 @@ Code examples are placed throughout the API documentation and these can be run a rtc/rtc.md ``` +## Run-Time Feature detection / Library Info + +```eval_rst +.. toctree:: + :maxdepth: 1 + + libinfo/libinfo.md +``` + + + ## Symbol API ```eval_rst diff --git a/docs/api/python/libinfo/libinfo.md b/docs/api/python/libinfo/libinfo.md new file mode 100644 index 000000000000..531e1ced3c99 --- /dev/null +++ b/docs/api/python/libinfo/libinfo.md @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + +# Run-Time Feature detection / Library info + +```eval_rst +.. currentmodule:: mxnet.runtime +``` + +## Overview + +The libinfo functionality allows to check for compile-time features supported by the library. + +### Example usage + +``` +In [1]: import mxnet as mx + ...: import mxnet.runtime + ...: fs = mx.runtime.Features() + +In [2]: fs +Out[2]: [✖ 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] + +In [3]: fs['CUDA'].enabled +Out[3]: False + +In [4]: fs.is_enabled('CPU_SSE') +Out[4]: True + +In [5]: fs.is_enabled('CUDA') +Out[5]: False + +In [6]: +``` + + +```eval_rst +.. autosummary:: + :nosignatures: + + Features + Feature + feature_list +``` + +## API Reference + + + +```eval_rst +.. automodule:: mxnet.runtime + :members: +``` + + diff --git a/include/mxnet/c_api.h b/include/mxnet/c_api.h index e5e57c10faaa..13ee903407b3 100644 --- a/include/mxnet/c_api.h +++ b/include/mxnet/c_api.h @@ -141,7 +141,6 @@ struct MXCallbackList { struct LibFeature { const char* name; - uint32_t index; bool enabled; }; diff --git a/python/mxnet/runtime.py b/python/mxnet/runtime.py index afb393281420..7ef5e1943072 100644 --- a/python/mxnet/runtime.py +++ b/python/mxnet/runtime.py @@ -1,3 +1,5 @@ +# coding: utf-8 + # 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 @@ -15,34 +17,71 @@ # 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 +import collections from .base import _LIB, check_call -class LibFeature(ctypes.Structure): +class Feature(ctypes.Structure): """ Compile time feature description """ _fields_ = [ - ("name", ctypes.c_char_p), - ("index", ctypes.c_uint32), + ("_name", ctypes.c_char_p), ("enabled", ctypes.c_bool) ] -def libinfo_features(): + @property + def name(self): + return self._name.decode() + + def __repr__(self): + if self.enabled: + return "✔ {}".format(self.name) + else: + return "✖ {}".format(self.name) + +def feature_list(): """ 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 + :return: list of class LibFeature indicating which features are available and enabled """ - lib_features = ctypes.POINTER(LibFeature)() + lib_features_c_array = ctypes.POINTER(Feature)() 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 + check_call(_LIB.MXLibInfoFeatures(ctypes.byref(lib_features_c_array), ctypes.byref(lib_features_size))) + features = [lib_features_c_array[i] for i in range(lib_features_size.value)] + return features + +class Features(collections.OrderedDict): + """ + OrderedDict of name to Feature + """ + def __init__(self): + super(Features, self).__init__([(f.name, f) for f in feature_list()]) + + def __repr__(self): + return str(list(self.values())) + + def is_enabled(self, feature_name): + """ + Check for a particular feature by name + + Parameters + ---------- + :param x: str The name of a valid feature as string for example 'CUDA' + + Returns + ------- + :return: bool True if it's enabled, False if it's disabled, RuntimeError if the feature is not known + """ + feature_name = feature_name.upper() + if feature_name not in self: + raise RuntimeError("Feature '{}' is unknown, known features are: {}".format( + feature_name, list(self.keys()))) + return self[feature_name].enabled diff --git a/src/libinfo.cc b/src/libinfo.cc index 44a834c85b16..2af61eac9eca 100644 --- a/src/libinfo.cc +++ b/src/libinfo.cc @@ -114,7 +114,6 @@ 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; } } diff --git a/tests/python/unittest/test_runtime.py b/tests/python/unittest/test_runtime.py index 433301819252..5b06d0c3c36c 100644 --- a/tests/python/unittest/test_runtime.py +++ b/tests/python/unittest/test_runtime.py @@ -21,14 +21,24 @@ from mxnet.base import MXNetError from nose.tools import * -def test_libinfo_features(): - features = libinfo_features() - print("Lib features: ") +def test_features(): + features = Features() + print(features) + ok_('CUDA' in features) + ok_(len(features) >= 30) + +def test_is_enabled(): + features = Features() for f in features: - print(f.name, f.enabled, f.index) - ok_(type(features) is list) - ok_(len(features) > 0) + if features[f].enabled: + ok_(features.is_enabled(f)) + else: + ok_(not features.is_enabled(f)) +@raises(RuntimeError) +def test_is_enabled_not_existing(): + features = Features() + features.is_enabled('this girl is on fire') if __name__ == "__main__":