Repository: cupy/cupy

Issue:
Avoid creating handles in `cupy.show_config()` ?
`cupy.show_config()` creates several handles just to retrieve a version number.
Here is a memory usage observed in nvidia-smi:

```py
>>> import cupy  # "No running processes found"
>>> cupy.empty(1)  # 443 MiB ... (CUDA context)
>>> cupy.show_config()  # 797MiB
```

When introducing this feature we thought this overhead is negligible (see https://github.com/cupy/cupy/pull/2578#issuecomment-551012218 and reverted `detailed` feature: https://github.com/cupy/cupy/pull/2578/commits/4cf3a11fdd6cca249e0ee2b717b8f9be025bd853), but I think it is not small enough to ignore in recent hardware.

Note that it seems the overhead size depends on the device architecture.

* A100: 443 MiB -> 797 MiB (overhead: 354 MiB)
* V100: 335 MiB -> 545 MiB (overhead: 210 MiB)
* P100: 273 MiB -> 395 MiB (overhead: 122 MiB)




Discussion:

Resolving patch:
diff --git a/cupy/__init__.py b/cupy/__init__.py
--- a/cupy/__init__.py
+++ b/cupy/__init__.py
@@ -862,9 +862,9 @@ def get_default_pinned_memory_pool():
     return _default_pinned_memory_pool
 
 
-def show_config():
+def show_config(*, _full=False):
     """Prints the current runtime configuration to standard output."""
-    _sys.stdout.write(str(_cupyx.get_runtime_info()))
+    _sys.stdout.write(str(_cupyx.get_runtime_info(full=_full)))
     _sys.stdout.flush()
 
 
diff --git a/cupyx/_runtime.py b/cupyx/_runtime.py
--- a/cupyx/_runtime.py
+++ b/cupyx/_runtime.py
@@ -100,7 +100,7 @@ def _get_cupy_package_root(self):
         return os.path.dirname(cupy_path)
 
 
-class _RuntimeInfo(object):
+class _RuntimeInfo:
 
     cupy_version = None
     cuda_path = None
@@ -136,7 +136,7 @@ class _RuntimeInfo(object):
     numpy_version = None
     scipy_version = None
 
-    def __init__(self):
+    def __init__(self, *, full=True):
         self.cupy_version = cupy.__version__
 
         if not is_hip:
@@ -153,10 +153,13 @@ def __init__(self):
             cupy.cuda.runtime.runtimeGetVersion,
             cupy.cuda.runtime.CUDARuntimeError)
 
-        self.cublas_version = _eval_or_error(
-            lambda: cupy.cuda.cublas.getVersion(
-                cupy.cuda.device.get_cublas_handle()),
-            cupy.cuda.cublas.CUBLASError)
+        if full:
+            self.cublas_version = _eval_or_error(
+                lambda: cupy.cuda.cublas.getVersion(
+                    cupy.cuda.device.get_cublas_handle()),
+                cupy.cuda.cublas.CUBLASError)
+        else:
+            self.cublas_version = '(available)'
         self.cufft_version = _eval_or_error(
             cupy.cuda.cufft.getVersion,
             cupy.cuda.cufft.CuFFTError)
@@ -166,10 +169,14 @@ def __init__(self):
         self.cusolver_version = _eval_or_error(
             cupy.cuda.cusolver._getVersion,
             cupy.cuda.cusolver.CUSOLVERError)
-        self.cusparse_version = _eval_or_error(
-            lambda: cupy.cuda.cusparse.getVersion(
-                cupy.cuda.device.get_cusparse_handle()),
-            cupy.cuda.cusparse.CuSparseError)
+        if full:
+            self.cusparse_version = _eval_or_error(
+                lambda: cupy.cuda.cusparse.getVersion(
+                    cupy.cuda.device.get_cusparse_handle()),
+                cupy.cuda.cusparse.CuSparseError)
+        else:
+            self.cusparse_version = '(available)'
+
         self.nvrtc_version = _eval_or_error(
             cupy.cuda.nvrtc.getVersion,
             cupy.cuda.nvrtc.NVRTCError)
@@ -273,8 +280,8 @@ def __str__(self):
         return s.getvalue()
 
 
-def get_runtime_info():
-    return _RuntimeInfo()
+def get_runtime_info(*, full=True):
+    return _RuntimeInfo(full=full)
 
 
 def get_install_info():


Test patch:
diff --git a/tests/cupy_tests/test_init.py b/tests/cupy_tests/test_init.py
--- a/tests/cupy_tests/test_init.py
+++ b/tests/cupy_tests/test_init.py
@@ -107,7 +107,14 @@ class TestShowConfig(unittest.TestCase):
     def test_show_config(self):
         with mock.patch('sys.stdout.write') as write_func:
             cupy.show_config()
-        write_func.assert_called_once_with(str(cupyx.get_runtime_info()))
+        write_func.assert_called_once_with(
+            str(cupyx.get_runtime_info(full=False)))
+
+    def test_show_config_with_handles(self):
+        with mock.patch('sys.stdout.write') as write_func:
+            cupy.show_config(_full=True)
+        write_func.assert_called_once_with(
+            str(cupyx.get_runtime_info(full=True)))
 
 
 class TestAliases(unittest.TestCase):