跳到主要内容

使用 musify 对代码进行平台迁移

2024-05-28

1. 引言

近些年,NVIDIA GPU 及其 CUDA 生态占据着行业领先地位,国产厂商也在奋力追赶。基于现实的考量,追赶者们需要尊重业界现存大量 CUDA 代码的事实,因此我们推出了 musify 这一工具,旨在尽量将原有的 CUDA 代码便捷无痛地转化,迁移至 MUSA 平台上编译运行,从而达到间接兼容的效果。

2. 如何使用 musify

2.1. 安装依赖

musify 使用 python 编写,要运行 musify 首先需要系统中安装有 python3,然后假设用户使用 ubuntu 为例,执行以下命令

# 使得python默认指向python3
sudo apt install python-is-python3 -y
# 安装python依赖管理工具
sudo apt install pip -y
# 如果最后一行依赖库安装存在网络问题可以按需配置更新源,取消下一行的注释是一种可行的选择
# pip config set global.index-url https://pypi.mirrors.ustc.edu.cn/simple/
# 安装musify的依赖库
pip install ahocorapy

2.2. 帮助信息

和一般的命令行程序一样,musify 通过-h/--help 提供了帮助信息,有经验的用户可以直接参考帮助信息进行使用

$ musify-text -h
usage: musify-text [-h] [-t | -c | -i] [-d {c2m,m2c}] [-m [MAPPING ...]] [--clear-mapping] [-l {DEBUG,INFO,WARNING}] [srcs ...]

positional arguments:
srcs source files to be transformed

options:
-h, --help show this help message and exit
-t, --terminal print code to stdout
-c, --create write code to newly created file, default action
-i, --inplace modify code inplace
-d {c2m,m2c}, --direction {c2m,m2c}
convert direction
-m [MAPPING ...], --mapping [MAPPING ...]
api mapping
--clear-mapping clear default and previous mapping
-l {DEBUG,INFO,WARNING}, --log-level {DEBUG,INFO,WARNING}
lowest log level to display

如上信息,本工具的使用方式是先设置一系列的选项,最后跟着需要转化的代码文件列表

以下是对选项的详细解释:

-t, -c, -i是输出方式,使用时三选其一;分别为输出到屏幕,另外新建文件,直接修改原来的代码文件;默认行为是新建文件输出(但是如果代码有版本管理等保护,使用-i/--inplace直接原地修改比较方便)
-d设置方向c2m是cuda至musa,m2c是musa至cuda;默认为cuda至musa
-m指定替换使用的映射表,是映射表是json文件,内容为单层无嵌套的json object,其中每个name,value对分别表示相应的cuda和musa命名;一般不用指定使用默认即可,如果有特殊需求可自己生成映射表并指定,如果有多个映射表,使用如下格式-m a.json m b.json,即每个映射表前都有一个前置的-m
-m会添加新的映射表,不会覆盖原有的默认映射表,如果不想要默认映射表,需要使用--clear-mapping,然后在这个选项右边使用-m指定自己想要的映射表
-l指定日志等级,高于设置等级的日志都会被打印出来,默认为INFO
前面的选项顺序可以随意调整,如果使用默认值也可以不写,但是需要转化的代码文件必须跟在最后;同时为了防止存在以-开头的文件路径被识别为选项,在所有选项之后,文件路径之前,加上--

2.3. 使用示例

经过了如上的解释后,我们可以来到一个最简单的例子,使用默认映射表,默认转化方向(cuda 至 musa),只有输出方式改为了原地修改

如下命令修改了当前目录下的 a.cpp 和 b.cpp 两个文件

musify-text --inplace -- a.cpp b.cpp

但是一般而言,我们并不想手动列举需要转化的文件,一般一个项目有大量的代码文件需要转化,此时我们可以借助 shell 的``语法和一些第三方的文件搜索工具,比如

# 递归查找目录${DIR}下所有后缀名为cu,cuh,cpp或h的文件并转化
musify-text --inplace -- `find ${DIR} -name '*.cu' -name '*.cuh' -name '*.cpp' -name '*.h'`

同时我们可以推荐一些更加现代化的工具

# 安装此处使用的工具
sudo apt install ripgrep -y
# 如果没有自己编写kernel,cuda程序可以是纯C/C++代码,ripgrep对于cpp的后缀名自带预设,-tcpp就可以调用,rg --files则递归搜索指定目录下所有符合要求的文件
musify-text --inplace -- `rg --files -tcpp ${DIR}`
# ripgrep没有内置cuda的后缀模式,但是我们可以通过--type-add指定,然后再当场调用
musify-text --inplace -- `rg --files --type-add 'cuda:*.cu' --type-add 'cuda:*.cuh' -tcuda -tcpp ${DIR}`
# 如果嫌上面两种复杂,也可以使用-g选项直接指定后缀名,和前面的find作用基本一致
musify-text --inplace -- `rg --files -g '*.cu' -g '*.cuh' -g '*.cpp' -g '*.h' ${DIR}`

2.4. 排除标记

可能有不少读者注意到了 musify 的命令名称为 musify-text,其实这是因为它的算法是单纯的文本匹配。

这种实现方案的原因主要是发现语法分析会引入过重的依赖(尤其对 C++这种上下文相关文法),给用户的使用带来不便,纯粹文本匹配的工具更加小巧灵活。

当然这样带来了一定的转化可能不够智能的问题,于是我们加入了类似于 lcov 行覆盖率工具的排除标记功能,可以标记一定范围的代码不受转化影响,保持原样。

使用排除标记需要一定程度上修改原有代码,加入排除标记。

MUSIFY_EXCL_LINE
包含本标记的行会被排除
MUSIFY_EXCL_START
从包含本标记的行开始,直到MUSIFY_EXCL_STOP之间的行会被排除;当前行也会被排除
MUSIFY_EXCL_STOP
结束由MUSIFY_EXCL_START开启的排除;当前行不会被排除

其中 START 和 STOP 成对使用

// MUSIFY_EXCL_START
char *str_array[] = {
"cuInit",
"cuMalloc"
};
// MUSIFY_EXCL_STOP

LINE 单独使用

char str[] = "cuInit"; // MUSIFY_EXCL_LINE

3. 总结与展望

本文介绍了 musify 的设计意图,使用方法和当前的缺陷。可能有不少读者也意识到如果没有其他不同版本需要加以区分,单纯因为使用了文本匹配就将工具叫做 musify-text 是不充分的;事实上,介于语法分析过重,文本匹配不够智能,目前存在一个后续计划是引入词法分析进行一定的代码分析识别,准备使用 musify-lexer 作为命令名称。