scikit-cuda icon indicating copy to clipboard operation
scikit-cuda copied to clipboard

cublas._get_cublas_version() on Windows

Open coreyjkelly opened this issue 4 years ago • 5 comments

I'm running on the following config:

  • Windows 10 Home 64-bit
  • Python 3.7.2 64-bit
  • CUDA 9.2 downloaded from Nvidia
  • PyCUDA 2019.1.1
  • scikit-cuda 0.5.3

and I've been digging around trying to prevent this warning:

import skcuda.linalg
> UserWarning: creating CUBLAS context to get version number

Since there is currently no implementation of utils.get_soname() that works on Windows, cublas._get_cublas_version() always displays this warning and creates a temporary context to get the CUBLAS version. I'll admit I'm no expert here, so excuse any misunderstanding on my part. Can we not often infer the CUBLAS version from the .dll file name? This won't always work, but for most Windows installations, it would at least be a fallback, and I assume better than creating a temporary context. When performing this check in the header of cublas.py:

elif sys.platform == 'win32':
    if sys.maxsize > 2**32:
        _libcublas_libname_list = ['cublas.dll'] + \
            ['cublas64_%s.dll' % v for v in _win32_version_list]
    else:
        _libcublas_libname_list = ['cublas.dll'] + \
            ['cublas32_%s.dll' % v for v in _win32_version_list]

we could just store the matching version to _cublas_version. It this is already defined when _get_cublas_version() is called, we can just return it.

coreyjkelly avatar Aug 19 '19 01:08 coreyjkelly

In principle, yes. Given that I wasn't always confident that the cublas .so/.dll file names always accurately reflected the version in the past (on Linux, the library search mechanism typically turns up a symlink to the library before the actual library itself), I felt it was better to ensure getting the correct version even if it meant creating a context to call cublasGetVersion(). Falling back to checking the file name seem reasonable at this point, though.

For Windows, I'm wondering whether we can use pywin32 to get the .dll version similar to what is currently done for .so files. Since I don't have a Windows system with access to an NVIDIA GPU, can you perhaps try installing pywin32 and see whether you can extract the version of the cublas dll? I believe the function you need is win32api.GetFileVersionInfo().

lebedov avatar Aug 19 '19 12:08 lebedov

As an aside, the context creation required to get the version from the CUBLAS API isn't typically something to worry about as it only happens once for each import of skcuda.cublas at the most.

lebedov avatar Aug 19 '19 12:08 lebedov

Works well for the two versions I've got installed!

from win32api import GetFileVersionInfo, LOWORD

dllFile0910 = """C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v9.1/bin/cublas64_91.dll"""
dllFile1010 = """C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v10.1/bin/cublas64_10.dll"""

for f in [dllFile0910, dllFile1010]:
    info = GetFileVersionInfo(f, '\\')

    print(LOWORD(info['FileVersionLS']))

# 9010
# 10010

coreyjkelly avatar Aug 19 '19 17:08 coreyjkelly

Please try the improved-cublas-get-version branch (a130358) - I added support for calling pywin32 on Windows, as well as trying to get the version from the shared library file name before falling back to an API call.

lebedov avatar Aug 22 '19 01:08 lebedov

cublas._get_cublas_version

cublas_path = utils.readlink(utils.find_lib_path('cublas'))

Doesn't resolve. From the header, we can use:

cublas_path = utils.readlink(utils.find_lib_path(_libcublas_libname))

I expect _libcublas_libname must be defined if we've made it this far?

More significantly, the regex parsing the filenames

major, minor = re.search(r'[\D\.]+\.+(\d+)\.(\d+)', os.path.basename(cublas_path)).groups()

doesn't match, for example, cublas64_92.dll. We can get most of the way there with

version_string = re.search(r'[\_]+(\d+)+[\.]', os.path.basename(cublas_path)).groups()[0]

but things get ugly because of the inconsistent naming of versions 10.0 and 10.1...

if version_string == "100":             
    major, minor = ("10", "0")          
elif version_string == "10":            
    major, minor = ("10", "1")          
else:                                   
    major, minor = tuple(version_string)

Do we know how version 10.2 will be named? Maybe not best for forward compatibility. Might be better to directly reference _win32_version_list from the header with a dict to map version strings to (major, minor) tuples? Not sure what your preference is.

coreyjkelly avatar Aug 22 '19 04:08 coreyjkelly