GPU dashboardでGPUクロックを確認できるようにする

GPUリソースの状況はGPU dash boardを使用すれば簡単に計測できるぞ

前回は温度計測をできるようにしましたよね

今度はクロック数に加えて、GPUメモリなどと他の情報と合わせて確認できる方法を紹介するぞ

環境構築と動作方法

環境構築や動作確認方法は下記の記事を参考にしてください。

GPUクロックの確認のための修正

Rapidsのdockerコンテナに入り、下記のコードを編集します。GPUクロックの項目を追加します。

/opt/conda/envs/rapids/lib/python3.6/site-packages/jupyterlab_nvdashboard/server.py

routes = {
    "/GPU-clock": apps.gpu.gpu_clock,
    "/GPU-Utilization": apps.gpu.gpu,
    "/GPU-Memory": apps.gpu.gpu_mem,
    "/GPU-Resources": apps.gpu.gpu_resource_timeline,
    "/PCIe-Throughput": apps.gpu.pci,
    "/NVLink-Throughput": apps.gpu.nvlink,
    "/NVLink-Timeline": apps.gpu.nvlink_timeline,
    # "/CPU-Utilization": apps.cpu.cpu,
    "/Machine-Resources": apps.cpu.resource_timeline,
}

下記のコードを編集して、GPUクロックを取得できるようにします。

/opt/conda/envs/rapids/lib/python3.6/site-packages/jupyterlab_nvdashboard/apps/gpu.py

pynvmlを使用してGPUクロックの情報を取得しています。pynvmlはPython経由でNVIDIA Management Libraryから情報を取得するライブラリです。これによって簡単にGPUに関する情報を取得できます。

  • pynvml.nvmlDeviceGetMaxClockInfoでGPUの最大クロックを取得しています。表示の際に最大値を設定している方が見やすくなるため設定します。
  • pynvml.nvmlDeviceGetClockInfoでGPUのクロックを取得しています。
def gpu_clock(doc):
    max_clock = pynvml.nvmlDeviceGetMaxClockInfo(gpu_handles[0], pynvml.NVML_CLOCK_SM)
    fig = figure(title="GPU Clock [MHz]", sizing_mode="stretch_both", x_range=[0, max_clock])

    def get_utilization():
        return [
            pynvml.nvmlDeviceGetClockInfo(gpu_handles[i], pynvml.NVML_CLOCK_SM)
            for i in range(ngpus)
        ]

    gpu = get_utilization()
    y = list(range(len(gpu)))
    source = ColumnDataSource({"right": y, "gpu_clock": gpu})
    mapper = LinearColorMapper(palette=all_palettes["RdYlBu"][4])

    fig.hbar(
        source=source,
        y="right",
        right="gpu_clock",
        height=0.8,
        color={"field": "gpu_clock", "transform": mapper},
    )

    fig.toolbar_location = None

    doc.title = "GPU clock [MHz]"
    doc.add_root(fig)

    def cb():
        source.data.update({"gpu_clock": get_utilization()})

    doc.add_periodic_callback(cb, 200)

docker内で下記のコマンドを実行します。

python -m jupyterlab_nvdashboard.server 8889

動作すれば下記のようになっていることが確認できます。

他のリソースと並行して確認

GPUメモリなど、他のリソースと平行して確認できるように設定します。コードは長くなるので一部、抜粋しながら説明します。

計測するための指標として”gpu-clock-total”を追加します。デフォルトではGPUの使用率、メモリの使用率、PCIEのスループットがあります。

    item_dict = {
        "time": [],
        "gpu-total": [],
        "memory-total": [],
        "gpu-clock-total": [],
        "rx-total": [],
        "tx-total": [],
    }

複数のGPUのため、GPU使用率、メモリ、クロックは分けて設定します。

    for i in range(ngpus):
        item_dict["gpu-" + str(i)] = []
        item_dict["memory-" + str(i)] = []
        item_dict["gpu-clock-" + str(i)] = []

クロックの情報を取得して図に追加します。複数GPUがある場合に色を変えて情報を追加しています。

    max_clock = pynvml.nvmlDeviceGetMaxClockInfo(gpu_handles[0], pynvml.NVML_CLOCK_SM)
    clock_fig = figure(
        title="GPU clock [MHz]",
        sizing_mode="stretch_both",
        x_axis_type="datetime",
        x_range=x_range,
        y_range=[0, max_clock],
        tools=tools,
    )
    clock_fig.yaxis.formatter = NumeralTickFormatter(format="0.0 hz")
    for i in range(ngpus):
        clock_fig.line(source=source, x="time", y="gpu-clock-" + str(i), color=_get_color(i))

情報を更新するための処理です。クロック処理のみ抜粋しています。

GPU数分だけクロックの情報を取得してクロック情報を更新します。

    def cb():
        :
        clock_tot = 0
        :
        for i in range(ngpus):
            :
            clock = pynvml.nvmlDeviceGetClockInfo(gpu_handles[i], pynvml.NVML_CLOCK_SM)
            :
            clock_tot += clock
            :
            src_dict["gpu-clock-" + str(i)] = [clock]
        :
        src_dict["gpu-clock-total"] = [clock_tot]
        
        source.stream(src_dict, 1000)

        last_time = now

コードの全体は下記です。

def gpu_resource_timeline(doc):

    memory_list = [
        pynvml.nvmlDeviceGetMemoryInfo(handle).total / (1024 * 1024)
        for handle in gpu_handles
    ]
    gpu_mem_max = max(memory_list) * (1024 * 1024)
    gpu_mem_sum = sum(memory_list)

    # Shared X Range for all plots
    x_range = DataRange1d(follow="end", follow_interval=20000, range_padding=0)
    tools = "reset,xpan,xwheel_zoom"

    item_dict = {
        "time": [],
        "gpu-total": [],
        "memory-total": [],
        "gpu-clock-total": [],
        "rx-total": [],
        "tx-total": [],
    }
    for i in range(ngpus):
        item_dict["gpu-" + str(i)] = []
        item_dict["memory-" + str(i)] = []
        item_dict["gpu-clock-" + str(i)] = []

    source = ColumnDataSource(item_dict)

    def _get_color(ind):
        color_list = [
            "blue",
            "red",
            "green",
            "black",
            "brown",
            "cyan",
            "orange",
            "pink",
            "purple",
            "gold",
        ]
        return color_list[ind % len(color_list)]

    memory_fig = figure(
        title="Memory Utilization (per Device) [B]",
        sizing_mode="stretch_both",
        x_axis_type="datetime",
        y_range=[0, gpu_mem_max],
        x_range=x_range,
        tools=tools,
    )
    for i in range(ngpus):
        memory_fig.line(
            source=source, x="time", y="memory-" + str(i), color=_get_color(i)
        )
    memory_fig.yaxis.formatter = NumeralTickFormatter(format="0.0 b")

    gpu_fig = figure(
        title="GPU Utilization (per Device) [%]",
        sizing_mode="stretch_both",
        x_axis_type="datetime",
        y_range=[0, 100],
        x_range=x_range,
        tools=tools,
    )
    for i in range(ngpus):
        gpu_fig.line(source=source, x="time", y="gpu-" + str(i), color=_get_color(i))

    max_clock = pynvml.nvmlDeviceGetMaxClockInfo(gpu_handles[0], pynvml.NVML_CLOCK_SM)
    clock_fig = figure(
        title="GPU clock [MHz]",
        sizing_mode="stretch_both",
        x_axis_type="datetime",
        x_range=x_range,
        y_range=[0, max_clock],
        tools=tools,
    )
    clock_fig.yaxis.formatter = NumeralTickFormatter(format="0.0 hz")
    for i in range(ngpus):
        clock_fig.line(source=source, x="time", y="gpu-clock-" + str(i), color=_get_color(i))

    tot_fig = figure(
        title="Total Utilization [%]",
        sizing_mode="stretch_both",
        x_axis_type="datetime",
        y_range=[0, 100],
        x_range=x_range,
        tools=tools,
    )
    tot_fig.line(
        source=source, x="time", y="gpu-total", color="blue", legend="Total-GPU"
    )
    tot_fig.line(
        source=source, x="time", y="memory-total", color="red", legend="Total-Memory"
    )
    tot_fig.legend.location = "top_left"

    pci_fig = figure(
        title="Total PCI Throughput [B/s]",
        sizing_mode="stretch_both",
        x_axis_type="datetime",
        x_range=x_range,
        tools=tools,
    )
    pci_fig.line(source=source, x="time", y="tx-total", color="blue", legend="TX")
    pci_fig.line(source=source, x="time", y="rx-total", color="red", legend="RX")
    pci_fig.yaxis.formatter = NumeralTickFormatter(format="0.0 b")
    pci_fig.legend.location = "top_left"

    doc.title = "Resource Timeline"
    doc.add_root(
        column(gpu_fig, memory_fig, tot_fig, pci_fig, clock_fig, sizing_mode="stretch_both")
    )

    last_time = time.time()

    def cb():
        nonlocal last_time
        now = time.time()
        src_dict = {"time": [now * 1000]}
        gpu_tot = 0
        mem_tot = 0
        clock_tot = 0
        tx_tot = 0
        rx_tot = 0
        for i in range(ngpus):
            gpu = pynvml.nvmlDeviceGetUtilizationRates(gpu_handles[i]).gpu
            mem = pynvml.nvmlDeviceGetMemoryInfo(gpu_handles[i]).used
            clock = pynvml.nvmlDeviceGetClockInfo(gpu_handles[i], pynvml.NVML_CLOCK_SM)
            tx = (
                pynvml.nvmlDeviceGetPcieThroughput(
                    gpu_handles[i], pynvml.NVML_PCIE_UTIL_TX_BYTES
                )
                * 1024
            )
            rx = (
                pynvml.nvmlDeviceGetPcieThroughput(
                    gpu_handles[i], pynvml.NVML_PCIE_UTIL_RX_BYTES
                )
                * 1024
            )
            gpu_tot += gpu
            mem_tot += mem / (1024 * 1024)
            clock_tot += clock
            rx_tot += rx
            tx_tot += tx
            src_dict["gpu-" + str(i)] = [gpu]
            src_dict["memory-" + str(i)] = [mem]
            src_dict["gpu-clock-" + str(i)] = [clock]
        src_dict["gpu-total"] = [gpu_tot / ngpus]
        src_dict["memory-total"] = [(mem_tot / gpu_mem_sum) * 100]
        src_dict["gpu-clock-total"] = [clock_tot]
        src_dict["tx-total"] = [tx_tot]
        src_dict["rx-total"] = [rx_tot]

        source.stream(src_dict, 1000)

        last_time = now

    doc.add_periodic_callback(cb, 200)

同様に動作確認します。GPUクロックが一番下に加えられています。

pyhvmlを使用すれば簡単に情報を取得できるので、GPU dashboardを組み合わせると可視化が簡単にできるぞ

Close Bitnami banner
Bitnami