Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The NVML does not return the correct strut for latest CUDA. #48

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

fostiropoulos
Copy link

@fostiropoulos fostiropoulos commented Jul 27, 2023

For my current system

| NVIDIA-SMI 535.54.03              Driver Version: 535.54.03    CUDA Version: 12.2     |

The processes running on cuda are read incorrectly. There is an additional size_t element when reading the strut that is not documented but when ignored causes the response to be malformed.

The following code illustrates the error:

from collections import namedtuple
from ctypes import *
import time

import pynvml
import ray
from pynvml.nvml import (
    _nvmlGetFunctionPointer,
    _PrintableStructure,
    nvmlDeviceGetComputeRunningProcesses,
)
import torch
import struct

Process = namedtuple(
    "Process", ["pid", "usedGpuMemory", "gpuInstanceId", "computeInstanceId"]
)


def _remote_fn():
    torch.randn(100).to("cuda")
    time.sleep(10)


def run_bug():
    pynvml.nvmlInit()
    handle = pynvml.nvmlDeviceGetHandleByIndex(0)
    # this initializes and creates an additional process (for added difficulty)
    a = torch.randn(100).to("cuda")
    for i in range(10):
        (
            ray.remote(
                num_gpus=0.001,
                num_cpus=0.001,
                max_calls=1,
                max_retries=0,
            )(_remote_fn)
            .options(name="x")
            .remote()
        )
    # wait for all processes to be allocated
    time.sleep(4)
    procs = nvmlDeviceGetComputeRunningProcesses(handle)

    class c_nvmlProcessInfo_t(_PrintableStructure):
        _fields_ = [
            ("pid", c_uint),
            ("usedGpuMemory", c_ulonglong),
            ("gpuInstanceId", c_uint),
            ("computeInstanceId", c_uint),
            ("index", c_ssize_t),
        ]
        _fmt_ = {
            "usedGpuMemory": "%d B",
        }

    pynvml.nvml.c_nvmlProcessInfo_t = c_nvmlProcessInfo_t

    procs_fixed = nvmlDeviceGetComputeRunningProcesses(handle)

    def _parse_result(procs):
        return "\n".join(str(p) for p in procs)

    return _parse_result(procs), _parse_result(procs_fixed)


def get_procs_analysis():
    pynvml.nvmlInit()
    handle = pynvml.nvmlDeviceGetHandleByIndex(0)
    fn = _nvmlGetFunctionPointer("nvmlDeviceGetComputeRunningProcesses_v3")
    # Reference
    # https://docs.nvidia.com/deploy/nvml-api/structnvmlProcessInfo__t.html#structnvmlProcessInfo__t
    # although the order differs (e.g. pid, usedGpuMemory, gpuInstanceId, computeInstanceId)
    # NOTE, I am not sure which is which for the last 2 as they are identical on my system
    # and not possible to debug.
    expr = "IQIIn"
    byte_size_proc = struct.calcsize(expr)
    # ORIGINAL
    proc_array = c_ubyte * (byte_size_proc * 100)  # enough for 100 processes
    c_procs = proc_array()

    # make the call again
    c_count = c_uint(100)

    ret = fn(handle, byref(c_count), c_procs)
    return_bytes = bytes(c_procs)

    def _parse_bytes(idx):
        args = struct.unpack(
            expr,
            return_bytes[idx * byte_size_proc : (idx + 1) * byte_size_proc],
        )
        return Process(*args[:-1])

    formatted = []
    for i in range(100):
        p = _parse_bytes(i)
        if p.pid == 0 and p.usedGpuMemory == 0 and p.gpuInstanceId == 0:
            break
        formatted.append(str(p))

    return '\n'.join(formatted)


if __name__ == "__main__":
    procs, procs_fixed = run_bug()
    print(procs)
    print(procs_fixed)
    print(get_procs_analysis())

Output:

{'pid': 1183425, 'usedGpuMemory': 251658240, 'gpuInstanceId': 4294967295, 'computeInstanceId': 4294967295}
{'pid': 0, 'usedGpuMemory': 1280123, 'gpuInstanceId': 251658240, 'computeInstanceId': 0}
{'pid': 4294967295, 'usedGpuMemory': 0, 'gpuInstanceId': 1283310, 'computeInstanceId': 0}
{'pid': 251658240, 'usedGpuMemory': None, 'gpuInstanceId': 0, 'computeInstanceId': 0}
{'pid': 1283307, 'usedGpuMemory': 251658240, 'gpuInstanceId': 4294967295, 'computeInstanceId': 4294967295}
{'pid': 0, 'usedGpuMemory': 1283309, 'gpuInstanceId': 251658240, 'computeInstanceId': 0}
{'pid': 4294967295, 'usedGpuMemory': 0, 'gpuInstanceId': 1283311, 'computeInstanceId': 0}
{'pid': 251658240, 'usedGpuMemory': None, 'gpuInstanceId': 0, 'computeInstanceId': 0}
{'pid': 1283308, 'usedGpuMemory': 251658240, 'gpuInstanceId': 4294967295, 'computeInstanceId': 4294967295}
{'pid': 0, 'usedGpuMemory': 1283306, 'gpuInstanceId': 251658240, 'computeInstanceId': 0}
{'pid': 4294967295, 'usedGpuMemory': 0, 'gpuInstanceId': 1283313, 'computeInstanceId': 0}
{'pid': 251658240, 'usedGpuMemory': None, 'gpuInstanceId': 0, 'computeInstanceId': 0}

Expected:

{'pid': 1183425, 'usedGpuMemory': 251658240, 'gpuInstanceId': 4294967295, 'computeInstanceId': 4294967295, 'index': 0}
{'pid': 1280123, 'usedGpuMemory': 251658240, 'gpuInstanceId': 4294967295, 'computeInstanceId': 4294967295, 'index': 0}
{'pid': 1283310, 'usedGpuMemory': 251658240, 'gpuInstanceId': 4294967295, 'computeInstanceId': 4294967295, 'index': 0}
{'pid': 1283307, 'usedGpuMemory': 251658240, 'gpuInstanceId': 4294967295, 'computeInstanceId': 4294967295, 'index': 0}
{'pid': 1283309, 'usedGpuMemory': 251658240, 'gpuInstanceId': 4294967295, 'computeInstanceId': 4294967295, 'index': 0}
{'pid': 1283311, 'usedGpuMemory': 251658240, 'gpuInstanceId': 4294967295, 'computeInstanceId': 4294967295, 'index': 0}
{'pid': 1283308, 'usedGpuMemory': 251658240, 'gpuInstanceId': 4294967295, 'computeInstanceId': 4294967295, 'index': 0}
{'pid': 1283306, 'usedGpuMemory': 251658240, 'gpuInstanceId': 4294967295, 'computeInstanceId': 4294967295, 'index': 0}
{'pid': 1283313, 'usedGpuMemory': 251658240, 'gpuInstanceId': 4294967295, 'computeInstanceId': 4294967295, 'index': 0}
{'pid': 1283312, 'usedGpuMemory': 251658240, 'gpuInstanceId': 4294967295, 'computeInstanceId': 4294967295, 'index': 0}
{'pid': 1283315, 'usedGpuMemory': 251658240, 'gpuInstanceId': 4294967295, 'computeInstanceId': 4294967295, 'index': 0}
{'pid': 1283314, 'usedGpuMemory': 251658240, 'gpuInstanceId': 4294967295, 'computeInstanceId': 4294967295, 'index': 0}

@fostiropoulos
Copy link
Author

fostiropoulos commented Aug 2, 2023

Having tried the same code in different machine with different GPUs the error seems to be related to the GPU model. The code that produces a bug is from RTX 2080 with nvlink, while the same code does not produce an error for V100.

@wence-
Copy link

wence- commented Aug 2, 2023

Thanks. This is due to an inadvertent ABI break in the 535 driver, which will fixed in the next patch release.

@fostiropoulos
Copy link
Author

Thanks for clarifying. Should there be a check somewhere and an error raised for this particular version or documentation of it?

@erikhuck
Copy link

@wence- Patch release for what? nvidia? cuda? Or can the pynvml package be updated to fix this?

@erikhuck
Copy link

this may be the same problem as my issue here: #50

@fostiropoulos
Copy link
Author

@erikhuck how I solved it was to uninstall driver version 535 and install the preceding release until the newer version is released.

| NVIDIA-SMI 525.125.06   Driver Version: 525.125.06   CUDA Version: 12.0     |

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants