CudaPAD





5.00/5 (31投票s)
CudaPAD 是一个用于 NVIDIA Cuda 内核的 PTX/SASS 查看器,可提供 Cuda 代码的即时视图。
引言
什么是 PTX 或 SASS? NVIDIA 的 PTX 是 NVIDIA GPU 的中间语言。它与纯 GPU 汇编(SASS)更紧密地关联,但稍有抽象。PTX 与特定硬件或硬件代的关系较小,因此在大多数情况下比汇编更有用。它抽象的一个项目是物理寄存器编号,这使得它比原始汇编更容易使用。PTX 指令通常会转换为一个或多个实际的 SASS 硬件指令。SASS 是硬核汇编。这是 GPU 实际运行的代码,并直接翻译成机器码。查看 SASS 代码比较困难,但它确实显示了 GPU 将做什么。如前所述,SASS 代码也直接处理寄存器,因此在寄存器存储位置上有更多控制权,但这是程序员需要跟踪的另一个项目,使得 SASS 更难处理。
在 Cuda 编程中,通常需要查看内核的 PTX/SASS 可能是什么样子,CudaPAD 有助于此。可能需要查看 PTX/SASS 进行调试、了解正在发生的情况、提高内核性能,或者仅仅出于好奇。要使用该应用程序,只需在左侧面板中键入或粘贴内核,右侧面板将显示相应的反汇编信息。内置了诸如 Cuda 到 PTX 代码的视觉匹配线、PTX 清理、WinDiff 和快速寄存器高亮显示等视觉辅助功能,以帮助轻松跟踪 PTX。还显示了其他即时信息,如寄存器计数、内存使用情况和错误信息。
对于任何代码片段,通常有几种方法可以执行相同的操作。有时,只需修改一两行即可产生不同的机器指令,并改善寄存器和内存使用情况。尽情享受,在左侧窗口中更改内核,观察右侧 PTX/SASS 的变化。
快速说明一下。CudaPAD 不运行任何代码。CudaPAD 仅用于查看 PTX、SASS 以及寄存器/内存使用情况。
背景
像我的大多数项目一样,这个项目源于个人需求。对于我开发的一些算法,GPU 效率至关重要。为此提供帮助的一种方法是理解底层机制并进行必要的调整。在创建此应用程序之前,我经常陷入一个循环:编写一个关键性能内核,然后反复使用命令行工具查看 PTX/SASS。反复执行此操作非常耗时,因此我决定构建一个快速的 C# 应用程序来自动化此过程。
它最初是一个简单的应用程序,接受左侧窗口中的内核,然后在右侧窗口中输出 PTX。这基本上是通过运行与以前相同的命令行工具(主要是 nvcc.exe)来实现的,但现在是自动化的后台操作。然而,我变得有些投入,在短时间内,我开始添加多个功能,包括自动重新编译、WinDiff、视觉代码线标记、编译错误以及寄存器/内存使用情况。
AMD 以前有一个类似的工具用于 Brooke++,这给了我在 2009 年首次构建它时拥有双窗口应用程序的想法。该工具有一个左侧窗口,可以添加 Brook+ 内核,还有一个右侧窗口,用于输出汇编。可以单击一个按钮来更新输出窗口。AMD 多年来曾推出过几个这样的工具,但后来被 AMD 的 CodeXL 取代了。
AMD 的 CodeXL 和 NVIDIA 的 NSight 此后取代了许多此类工具,但 CudaPAD 仍然在快速、即时地查看底层汇编和进行实验方面占有一席之地。CodeXL 和 NSight 都是专业级的免费工具,对于 GPU 开发人员来说是必不可少的。
使用 CudaPAD
要求
CudaPAD 使用简单。但在运行它之前,请确保满足这些系统要求
- Visual Studio 2017/2019(Express/Community 版本也可以)
- NVIDIA 的 Cuda 10
不需要专用 GPU,因为我们只编译代码而不运行任何东西。
如果满足要求,则只需启动可执行文件。当 CudaPAD 加载时,它会有一个示例内核。该示例提供了一个快速开始玩耍的地方,甚至可以作为新内核的起始框架。每当左侧的内核被编辑时,右侧的 PTX 或 SASS 都会更新。如果出现编译错误,它会在底部附近显示。
有几个功能可以启用/禁用。所有功能默认都开启(另请参阅 Features 部分)。
PTX/SASS 视图模式
在 PTX、SASS 或 SOURCE 视图之间切换下拉文本框。
PTX 视图 – 显示内核的 PTX 中间语言输出。PTX 接近 SASS 硬件指令,但级别稍高,并且与特定 GPU 代的关系较小。通常,PTX 指令直接转换为 SASS,但有时一个 PTX 指令对应多个 SASS 指令。
SASS 视图 – 这些是真正的汇编指令。这些类型的指令直接在 GPU 上执行。查看 SASS 时提供的视觉信息量比 PTX 少 – 例如,视觉代码线不显示。
原始代码视图 – 此视图主要用于调试 CudaPAD 本身。在后台,此应用程序不会在每次更改后重新编译。它仅在代码被修改时才重新编译,而不是注释或空格。原始代码是实际代码的精简版本。添加此功能的原因是我不希望在添加/编辑注释或添加/删除空格时不断编译。这既不节省资源,也会影响 WinDiff 功能。
在后台,CudaPAD 仅使用 Cuda 工具编译内核。然后,Cuda 编译器又会调用像 Visual Studio 这样的 C++ 编译器。因此,要运行此 CudaPAD,需要安装 Cuda,很可能还需要安装 Visual Studio 这样的 C++ 编译器。
启用/禁用功能
禁用自动编译对于在编译前进行多项更改很有用。这有助于在多次更改后显示差异输出中的更改。要进行手动编译,只需单击右上角的绿色“开始”按钮。
幕后
让我们看看这个应用程序是如何工作的。我将展示左侧窗口编辑时发生的情况。这会触发重新编译,然后更新右侧的 PTX/SASS 窗口。步骤如下
- 用户在左侧窗口中输入一些 Cuda 代码。
- 文本框的更改会触发一个短期计时器。如果在计时器完成之前用户输入了更多文本,则计时器会重置。此系统可防止每次击键都触发编译过程,并允许用户在自动启动之前完成输入。
- 当计时器完成时,会引发一个事件。在此事件中,我们会检查是否有任何需要重新编译的更改。显然,如果用户只是编辑一些注释或添加/删除空格,则无需重新编译。如果没有“代码”更改,则在此停止。在下拉框中,可以选择 CODE 来查看此清理后的代码的外观。
- 我们将 Cuda 文本框保存到一个文件。稍后 Cuda 编译器进行编译时需要用到它。
- 然后,我们在屏幕上清除所有行,因为很快就要绘制新行了。
- 然后,我们调用一个批处理文件来完成大部分编译工作。此批处理文件是根据 CudaPAD 中选择的选项生成的。如果用户选择了 sm_35 架构,则此选项将附加到 nvcc 行。如果用户选择优化级别为三,则附加 -O3。如果请求 SASS 输出,则附加 CuObjDump 命令。这是批处理文件
- 在临时文件夹中清理上次编译的残留物。
- 调用 NVIDIA 的 Cuda 编译器并附带一些选项
nvcc.exe -keep -cubin --generate-line-info ...
此命令将 Cuda 文件编译为 cubin 文件(设备代码)。我们还使用-keep
选项并保留 PTX 文件,以及--generate-line-info
以便我们知道源文件的行号,以便绘制行。 - 如果从下拉列表中选择了 SASS,则运行 CuObjDump.exe 将 cubin 设备文件反汇编为 SASS 代码。
- 最后,我们将这些命令的任何输出消息捕获到 info.txt 中。
- 接下来,我们填充包含寄存器和内存利用率信息的 info 文本框。
- 我们从批处理文件创建的输出日志 info.txt 文件中提取这些信息。
- 然后,我们使用 RegEx 获取全局、常量、堆栈和共享内存、字节计数、寄存器溢出信息、寄存器使用情况和一般日志信息。
- 然后,将此信息格式化并显示在信息窗口中。
- 接下来,从 rtcof.dat 文件中捕获任何错误/警告,然后格式化并放入错误窗口。
- 然后,我们从输出的 data.ptx(来自 nvcc.exe)中获取文本,并使用
diff
算法将其与窗口中已有的 PTX 进行比较。diff
函数的最终结果是新的 PTX,其中包含更改的部分(以注释的形式)。我选择将更改信息放在注释中,以便如果文本被复制到另一个程序中,它仍然可以运行。 - 接下来,我们存储 PTX/SASS 窗口的滚动条位置和插入符号位置。这很必要,因为在我们用文本重新填充输出窗口之后,我们需要恢复它们。
- 接下来,我们获取 PTX 的行信息并存储起来。行号稍后将用于绘制连接线。行信息的形式是“
.loc # ## #
”语句。然后从 PTX 中删除任何行信息,以免显示。 - 对 PTX 进行一些清理,使其看起来整洁漂亮。
- 接下来,我们绘制视觉代码线。
- 之前,我们保存了输出 PTX 文件中指定位置的行号信息。例如:在 PTX 的第 45 行,我们可能有
.loc 1 20 1
。这里的20
是源行,因此会从源文件的第 20 行绘制一条线到 PTX 窗口的第 45 行。 - 接下来,我们获取每行的缩进。这是通过计算每个单词之前的空白(空格/制表符)来完成的。这是必需的,以便线条从代码开始的位置开始或结束,而不是仅仅从行首开始。
- 使用文本框的高度/宽度加上每个窗口的当前滚动位置以及每行的缩进和行号,然后绘制线条。
- 之前,我们保存了输出 PTX 文件中指定位置的行号信息。例如:在 PTX 的第 45 行,我们可能有
- 最后,我们恢复滚动位置和插入符号位置。
特点
视觉代码线
这些线条将 Cuda 源代码与 PTX 输出匹配起来。它们帮助程序员快速识别哪些 Cuda 代码与哪个 PTX 匹配。此功能可以通过单击 PTX 窗口顶部的线条图标来启用或禁用。
自动汇编刷新
在需要时,应用程序将自动重新生成 PTX 代码。它不会在源窗口中的每次文本更改时执行此操作,而是在重要内容发生更改时执行。许多不影响输出的项(例如注释或空格)会从源文本中剥离。可以通过单击 PTX 窗口顶部的自动更新图标来启用或禁用自动更新功能。
内置的 Diff 工具
每次输出窗口更新时,它都会在 PTX 输出更改时自动运行一个差异算法。注释的添加方式不会影响代码的可运行性。我选择将 diff
信息放在注释中,以防用户想复制和粘贴代码。我采用了一种系统,对删除的行使用 //
风格的注释,对新行使用 /*new*/
注释。//
注释会禁用整行,而 /*new*/
则不会。
单点多重高亮(2016 年新增)
只需单击 PTX 窗口中的任何寄存器或单词,它就会高亮显示该项的所有实例。 单击另一个,它将以不同的颜色高亮显示这些实例。 单击任何高亮显示的项,它将取消高亮显示该项的所有实例。 只需三次单击即可实现以下目标:
语法高亮和输出格式化
Jacob Slusser 的 ScintillaNET 文本框控件具有便捷的文本高亮功能,在查看代码时具有视觉帮助。最初,这只是一个普通的文本框,然后迁移到另一个第三方控件,最后迁移到 ScintillaNET 控件。这使得代码更加丰富多彩,外观也更整洁。
除了文本高亮之外,输出窗口中的文本还经过格式化,使其稍微整洁一些。编译器信息和头信息等内容已被移除
- 删除不必要的注释
- 删除不必要的 id: 注释
- 删除空的“//”注释
- 缩短 __cudaparam_
- 缩短标签
- 删除 .loc 15 行(例如,“.loc 3 3431 3”)
- 删除寄存器前的“%”(2016 年 1 月新增)
- 删除“// Inline”行(2016 年 1 月新增)
- 删除 .file 1 "C:\\....."(2016 年 1 月新增)
高亮显示和清理后的输出格式化示例如下
在线错误/警告搜索
遇到错误时,通常需要快速在线搜索。我发现我经常打开浏览器,然后将错误复制粘贴到搜索框中。这效率不高,所以我添加了一个在线搜索功能。当时,我认为这是同类产品中的佼佼者,但自 2009 年发布以来,我看到其他 IDE 也具有此功能。
关注点
我在此过程中玩得很开心。这可能是投入如此多时间的原因。
让代码行工作让我很兴奋。我相信视觉代码线可能是我在 2009 年构建它时最早的同类产品之一,但不确定。这是我当时的一个疯狂想法,不确定是否能实现。在屏幕上绘制移动的线条并不容易,正如我所发现的那样,总有一些副作用。绘制样条线很容易,但所有杂项(如清理)都更困难。另一个困难的部分是计算文本框中的位置。每绘制一条样条线,都必须知道文本框的行高和行号。我不是图形开发人员,所以我很高兴它能正常工作!视觉线条的效果比预期的要好,而且玩起来很有趣。
当时,我构思了许多不同的“线条”想法来帮助分解汇编,但其他想法尚未实现
注意:这些其他功能尚未添加到 CudaPAD。(至少目前还没有)
- 绘制曲线,显示跳转。向上跳转颜色较浅,向下跳转颜色较深。
- 单击寄存器,它会显示寄存器影响的行。实际使用寄存器的位置显示为深色。它影响的寄存器显示为灰色。它在两个指令后影响的寄存器显示为浅灰色。这类似于 Excel 的跟踪前任/跟踪后继功能。
- 我一直想实现的另一个功能是显示使用的寄存器。这有助于理解内核在哪里达到了寄存器使用的上限,并且经常限制了内核。当寄存器最后一次使用时,它会在该指令之后释放。
查看 PTX/SASS 的优点
以下是查看 PTX 的一些优点...
- 好奇心 - 这是我最常使用的。有时我只想了解底层发生了什么,以及小的改动如何影响代码。这对于学习 PTX/SASS 和 Cuda 编译器来说是一个非常有用的工具。
- 软件错误 - 试图弄清楚恼人的错误。是编译器错误还是我的代码问题?有时查看机器指令有助于理解意外的结果。
- 更改一两行通常会产生不同的结果。 当存在需要性能优化的内核时,尝试用不同的方法来做同一件事可以产生更有效的代码。一个想到的例子是,我发现使用 union(联合体)时,PTX 总是会产生本地内存。这已经是很久以前的事了,现在可能不再是真的了,但这里有一个例子
local .align 4 .b8 someLocMem[4]; .... st.local.s32 [someLocMem], someIntReg; <--very expensive ld.local.f32 someFloatReg, [someLocMem]; <--very expensive
然而,当使用类似 -
"int strangeInt = *(int*) &somefloat;” the output looks like this: mov.b32 someFloatReg, someIntReg;
在 CudaPAD 中可以轻松发现这一点,因为反馈快速且有视觉标记。 - 代码什么都没做? 过去有几次,我意识到我的内核出了一个 bug,因为当我更改或删除一些代码时,PTX 输出没有任何变化。我心想,怎么会这样?PTX 可能不显示的原因是编译器经常会简化掉无用的代码。正如我所发现的,这比预期的要常见,因为我遇到过几次。这通常是由 bug 引起的,但也可能只是无用的代码。在大多数情况下,被优化掉的代码应该被删除或修复。注意到这一点有助于找到程序中的一些隐藏错误。
郑重提示,尽量不要过度优化。优化在某些经常运行的函数中有其作用,但优化会使代码可读性降低,变得笨拙,并且更难维护。此外,时间应该只花在性能提升会有很大影响的代码上。关于这个主题还有很多内容,我在此不再赘述。
视频 (2016 年更新)
下面是一个快速的教程视频。视频中子菜单选项显示不正确,但我解释了我点击的内容,所以希望你仍然可以跟上。
CudaPAD 在 2016 年 GPU 技术大会上获得了海报展示。更好的是,它还被评为前 20 名!在会议上,我于 2016 年 4 月 4 日向大约 100-150 人进行了简短的演讲。
愿望清单
以下是我的一些愿望清单项目,它们可能会或可能不会在将来添加
- 使用桥模式将实现代码与接口代码隔离。虽然 GUI 和代码目前在不同文件中有所分离,但它们并非真正可分离。将它们分开通常是很好的做法。
- 添加执行代码进行计时。目前 PTX 可以从视觉上查看,但不能进行基准测试。
- 添加每行寄存器使用计数器。基本上,这需要跟踪每个 PTX 行使用的变量数量。GPU 有固定数量的寄存器,了解寄存器压力最高的地方可以帮助程序员平衡代码。这是我添加到我的 AMD GPU 编译器 ASM4GCN 中的内容,但尚未在此处添加。
- 为 PTX 添加跳转线,这样可以轻松看到跳转语句指向何处。
特别感谢...
- Diff 功能 - 这是一个很好的即插即用 C# 文件,提供高质量的 diff 功能。最初由 Eugene Myers 于 1986 年创建;由 Matthias Herte 转换为 C#。大部分未编辑的源代码在 Diff.cs 文件中。
- ScintillaNET - 这个不错的工具为该项目提供了文本高亮功能。它是一个 Windows Forms 控件、包装器和绑定,用于通用的 Scintilla 源代码编辑组件。它确实为这个项目增添了很多活力。
- nVidia - 2016 年,CudaPAD 在 2016 年 GPU 技术大会上获得了 CudaPAD 海报的展示机会。此外,它还被评为荣誉提名(前 20 名)。我在一个巨大的投影屏幕上向大约 100-150 人的观众进行了演示。这是一次美好的经历 - 我有过的最好的经历之一。
历史
- 2008 年 12 月 – 最初构建,自 2020 年以来基本未更改。
- 2009 年 8 月 - 在 CSUEB 期间,与一个小组项目一起构建了 Cudapad.com 网站 - 该网站上线一年后过期。
- 2013 年 1 月 – 将代码文本框更改为使用 ScintillaNET 以获得更好的语法高亮
- 2014 年 11 月 – 更新以支持 NVidia Cuda 6.0/6.5
- 2015 年 6 月 – 代码公开发布;改为 MIT 许可证;更新以支持 Cuda 6.5/7.0
- 2016 年 1 月 – 添加了单点多重高亮搜索功能;更新以支持 Cuda 7.0/7.5。
- 2017 年 1 月 – 经验证可与 Cuda 8.0 配合使用
- 2019 年 6 月 - 更新以支持 Cuda 10 和 Visual Studio 2017/2019