本文发自 http://www.binss.me/blog/install-nvvp-on-macos-monterey/,转载请注明出处。

最近在学习 cuda ,一个很有用的工具是 Ndivia Visual Profiler (后简称 nvvp),能够将 kernel 在 GPU 中执行时间以图形化的形式展现出来,有助于分析调度情况。然而 MacOS 上,安装它却不是一件轻松的事,折腾了半天,在此记录下过程。

安装

根据 官方文档,对于 macOS version 11 (Big Sur) 及之后的系统通过以下步骤安装:

  1. Make sure that required java is in your JAVA_HOME. For example: /Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home
  2. Run nvvp executable.

    ./nvvp

  3. If you get the error when loading the JVM library, for example:

    JavaVM: Failed to load JVM: /Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/lib/libserver.dylib

    JavaVM FATAL: Failed to load the jvm library.

    Then create symlink for libjvm.dylib and run nvvp again

    sudo ln -s /Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/lib/server/libjvm.dylib /Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/lib/libserver.dylib

    ./nvvp

  4. If you get the error when attempting use the lib.dylib library, for example: 'lib.dylib' cannot be allowed to run because its origin cannot be verified

    Then go to 'System Preferences' → 'Security & Privacy' → 'General' and click the 'Allow…' button for the blocked 'lib.dylib'.

    Run nvvp again:

    ./nvvp

显然,这个软件依赖于 JDK 8,距离当前的版本 JDK 18 不能说是天差地别,只能说是完全不兼容。所幸的是,网站上给了个 Zulu 8.23.0.3 build 1.8.0_144 的链接。于是我下载后,通过 JAVA_HOME 指向相应目录,java -version 验证其生效了。结果启动 nvvp ,按照步骤里说的在 System Preferences 中允许各种 lib 执行后,最终报错退出:

CheckForInstalledJavaRuntimes: Please visit http://www.java.com for information on installing java.

于是我开始怀疑 macOS 下的 java 程序运行并不只是根据 JAVA_HOME ,还需要一些别的依赖。一番 google ,找到了比较 “科学” 的安装方法来安装 JDK 8:

brew tap adoptopenjdk/openjdk
brew install --cask adoptopenjdk8

安装完后,根据 /usr/libexec/java_home 发现其安装在了 /Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home 下,发现目录结构和下载的 Zulu 8.23.0.3 build 1.8.0_144 差不多,为了避免小版本不一致导致的问题,我将目录下的文件删除,替换为了 Zulu 8.23.0.3 build 1.8.0_144 目录的文件。

这时候启动 nvvp ,报了新错误:

JavaVM: Failed to load JVM: /Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/lib/libserver.dylib

根据 google 结果,用 libjvm.dylib 替代:

ln -s /Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/server/libjvm.dylib /Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/lib/libserver.dylib

再次启动 nvvp ,在 System Preferences 中允许各种 lib 执行后,终于成功启动了。

使用

由于 cuda 环境跑在生产环境不方便 remote profiling,选择先通过 nvprof 生成 timeline 文件,然后通过 nvvp 加载的方式。

nvprof -o stream_sum.nvvp -f --csv ./stream_sum

将 stream_sum.nvvp 搞到本地,通过 nvvp 加载:

cmd + i 打开 import 界面,选择 nvperf - single process ,在 Timeline data 选择刚刚的 stream_sum.nvvp 即可:

自定义统计

从上图发现,我们关心的 kernel 执行部分由于跟 alloc memoery 等操作相比耗时太短,在 timeline 上几乎看不到,为了能够看得更清楚,可以在写程序时候圈定要统计的范围,比如对于 c / cpp api :

#include <cuda_profiler_api.h>

cudaProfilerStart();
// Run kernel
cudaProfilerStop();

并在统计时加上 --profile-from-start off 参数,如:

nvprof -o stream_sum.nvvp -f --csv --profile-from-start off  ./stream_sum

那么仅有 cudaProfilerStart 到 cudaProfilerStop 的内容被统计,timeline 更加直观: