4. MUSA开发环境搭建及示例演示
请前往 MUSA SDK 下载 MUSA 最新版本。依赖环境如下:
Ubuntu 20.04.x LTS x86_64, Kernel version 5.4.0-42-generic
4.1. MUSA开发环境搭建
4.1.1. 下载安装包
目前安装包包含以下内容。
Category | Name |
|---|---|
Package | musa_xorg_ubuntu2004_x86_20221125.deb |
musa_toolkits_install_rc1.2.0.tar.gz | |
mudnn.tar | |
mttensorflow_whl.tar | |
Release Note | CUDA_Compatible_Release_Note_rc1.2.0.md |
Installation Guide | Installation guide for Cuda compatible rc1.2.0.md |
API DOCs | MUSA_API_rc1.2.0.pdf |
MUSA_Compiler_Collection_rc1.2.0.pdf | |
MUSA_Runtime_API_rc1.2.0.pdf | |
Math-X_muBLAS_API_rc0.3.0.pdf | |
Math-X_muFFT_API_rc0.2.0.pdf | |
EULA | 摩尔线程软件用户使用许可.docx |
4.1.2. 安装教程
4.1.2.1. 通用安装
-
下载软件包
> 敬请期待后续版本更新 -
安装KMD&UMD
1)确保系统中已安装lightdm,安装命令如下:
apt install lightdm (安装过程中提示configure lightdm,请务必选择lightdm,不要选择gdm3)
2)cd ~/pkg
3)dpkg -i musa_xorg_ubuntu2004_x86_20221125.deb
4)modprobe mtgpu -
安装MUSA Toolkits
1)cd ~/pkg
2)tar -zxvf musa_toolkits_install_rc1.2.0.tar.gz
3)rm -rf /usr/local/musa*
4)cd musa_toolkits_install
5)./install.sh
NOTE: 如果需要支持AI的应用,则根据需求安装4和5
-
安装muDNN
1)tar -xvf mudnn.tar
2)cd mudnn && ./install_mudnn.sh -i -
安装mtTensorFlow
1)tar -xvf mttensorflow_whl.tar && cd mttensorflow_whl
2)pip install tensorflow-2.8.2-cp38-cp38-linux_x86_64.whl
(如果之前已安装TensorFlow,请先执行pip uninstall tensorflow)
3)pip install mt_tf_plugin-0.0.1-cp38-cp38-linux_x86_64.whl
(如果之前已安装TensorFlow,请先执行pip uninstall mt-tf-plugin) -
添加
MUSA Toolkit的环境变量$ vim ~/.bashrc# 在其中加入以下内容export MUSA_INSTALL_PATH=/usr/local/musaexport PATH=$MUSA_INSTALL_PATH/bin:$PATHexport LD_LIBRARY_PATH=$MUSA_INSTALL_PATH/lib:$LD_LIBRARY_PATH# 完成以上内容$ source ~/.bashrc
4.2. MUSA示例演示
4.2.1. 案例代码
下面以一个简单的musa example:vector_add.mu参考,实现了浮点的vector相加。具体的代码如下:
#include <stdio.h>
#include <musa_runtime.h>
// musa kernel vector Add, C = A + B
__global__ void vectorAdd(const float *A, const float *B, float *C, int numElements) {
int i = blockDim.x * blockIdx.x + threadIdx.x;
if (i < numElements) {
C[i] = A[i] + B[i];
}
}
int main(void) {
musaError_t err = musaSuccess;
int numElements = 50000;
size_t size = numElements * sizeof(float);
float *h_A = (float *)malloc(size);
float *h_B = (float *)malloc(size);
float *h_C = (float *)malloc(size);
if (h_A == NULL || h_B == NULL || h_C == NULL) {
fprintf(stderr, "Failed to allocate host vectors!\n");
exit(EXIT_FAILURE);
}
for (int i = 0; i < numElements; ++i) {
h_A[i] = rand()/(float)RAND_MAX;
h_B[i] = rand()/(float)RAND_MAX;
}
// Allocate the device input vector A
float *d_A = NULL;
err = musaMalloc((void **)&d_A, size);
if (err != musaSuccess) {
fprintf(stderr, "Failed to allocate device vector A (error code %s)!\n", musaGetErrorString(err));
exit(EXIT_FAILURE);
}
// Allocate the device input vector B
float *d_B = NULL;
err = musaMalloc((void **)&d_B, size);
if (err != musaSuccess) {
fprintf(stderr, "Failed to allocate device vector B (error code %s)!\n", musaGetErrorString(err));
exit(EXIT_FAILURE);
}
// Allocate the device output vector C
float *d_C = NULL;
err = musaMalloc((void **)&d_C, size);
if (err != musaSuccess) {
fprintf(stderr, "Failed to allocate device vector C (error code %s)!\n", musaGetErrorString(err));
exit(EXIT_FAILURE);
}
// Copy the host input vectors A and B in host memory to the device input vectors in
// device memory
printf("Copy input data from the host memory to the MUSA device\n");
err = musaMemcpy(d_A, h_A, size, musaMemcpyHostToDevice);
if (err != musaSuccess) {
fprintf(stderr, "Failed to copy vector A from host to device (error code %s)!\n", musaGetErrorString(err));
exit(EXIT_FAILURE);
}
err = musaMemcpy(d_B, h_B, size, musaMemcpyHostToDevice);
if (err != musaSuccess) {
fprintf(stderr, "Failed to copy vector B from host to device (error code %s)!\n", musaGetErrorString(err));
exit(EXIT_FAILURE);
}
// Launch the Vector Add MUSA Kernel
int threadsPerBlock = 256;
int blocksPerGrid =(numElements + threadsPerBlock - 1) / threadsPerBlock;
printf("MUSA kernel launch with %d blocks of %d threads\n", blocksPerGrid, threadsPerBlock);
vectorAdd<<<blocksPerGrid, threadsPerBlock>>>(d_A, d_B, d_C, numElements);
err = musaGetLastError();
if (err != musaSuccess) {
fprintf(stderr, "Failed to launch vectorAdd kernel (error code %s)!\n", musaGetErrorString(err));
exit(EXIT_FAILURE);
}
// Copy the device result vector in device memory to the host result vector
// in host memory.
printf("Copy output data from the MUSA device to the host memory\n");
err = musaMemcpy(h_C, d_C, size, musaMemcpyDeviceToHost);
if (err != musaSuccess) {
fprintf(stderr, "Failed to copy vector C from device to host (error code %s)!\n", musaGetErrorString(err));
exit(EXIT_FAILURE);
}
// Verify that the result vector is correct
for (int i = 0; i < numElements; ++i) {
if (fabs(h_A[i] + h_B[i] - h_C[i]) > 1e-5) {
fprintf(stderr, "Result verification failed at element %d!\n", i);
exit(EXIT_FAILURE);
}
}
printf("Test PASSED\n");
// Free device global memory
err = musaFree(d_A);
if (err != musaSuccess) {
fprintf(stderr, "Failed to free device vector A (error code %s)!\n", musaGetErrorString(err));
exit(EXIT_FAILURE);
}
err = musaFree(d_B);
if (err != musaSuccess) {
fprintf(stderr, "Failed to free device vector B (error code %s)!\n", musaGetErrorString(err));
exit(EXIT_FAILURE);
}
err = musaFree(d_C);
if (err != musaSuccess) {
fprintf(stderr, "Failed to free device vector C (error code %s)!\n", musaGetErrorString(err));
exit(EXIT_FAILURE);
}
// Free host memory
free(h_A);
free(h_B);
free(h_C);
printf("Done\n");
return 0;
}
4.2.2. 编译并运行
$ mcc vectorAdd.mu -o vectorAdd -lmusart
$ ./vectorAdd
Test PASSED
Done
4.3. 使用CMake构建MUSA项目
cmake是一个构建软件工程的工具,可以在不同的平台上使用统一的脚本生成相应平台对应的Makefile或者build.ninja文件。自cmake版本2.8之后,cmake官方新增了CUDA的Modules,使得可以使用find_package(CUDA)来获取构建CUDA项目所需要用到的cmake宏,变量以及函数。从cmake版本3.9之后,官方将CUDA加入了cmake的内置语言,从而可以使用project(PROJECT_NAME LANGUAGES CXX CUDA)或者enable_language(CUDA)来让使用CUDA语言。
MUSA沿用了Modules的方式,也提供了相似的cmake模块供使用。保留了和CUDA几乎完全一致的使用方式,以达到用户尽可能方便地迁移CUDA项目的目的。在做项目迁移时,若项目使用cmake工具构建,则绝大多数情况下可以仅做文本替换,将CMakelist.txt中的CUDA替换成MUSA,CU前缀替换成MU前缀。
4.3.1. 使用MUSA模块
下面从一个最简单的CMakeList.txt入手来了解MUSA模块的使用。
cmake_minimum_required(VERSION 3.10)
set(PROJECT_NAME demo)
project(${PROJECT_NAME} LANGUAGES CXX)
# 载入MUSA模块
list(APPEND CMAKE_MODULE_PATH /usr/local/musa/cmake)
find_package(MUSA REQUIRED)
# 添加可执行文件
musa_add_executable(${PROJECT_NAME} vector_add.mu)
这里假设整个项目仅有一个MUSA代码文件,如上一节使用的vector_add.mu。
- 第一步需要使用: find_package(MUSA REQUIRED),来载入MUSA模块。这里有一点需要注意,由于该模块暂时未被cmake官方收录,仅安装在了MUSA Toolkit的安装目录中,因此需要在载入模块之前将模块的安装目录更新到cmake的MODULE搜索路径中: list(APPEND CMAKE_MODULE_PATH /usr/local/musa/cmake)。
- 载入MUSA模块后会添加各种有用的函数,宏以及变量,4.3.2小节将做详细介绍。其中最常用的musa_add_executable,添加目标可执行文件。
4.3.2. MUSA模块提供的物件
4.3.2.1. 输出变量
当MUSA模块加载完毕,如下变量将被设置,可供后续使用。
- MUSA_FOUND : 若MUSA模块加载成功则为true。
- MUSA_VERSION_MAJOR : MUSA的大版本。
- MUSA_VERSION_MINOR : MUSA的小版本。
- MUSA_VERSION, MUSA_VERSION_STRING : MUSA_VERSION_MAJOR.MUSA_VERSION_MINOR。
- MUSA_TOOLKIT_ROOT_DIR : MUSA的安装目录,默认为/usr/local/musa
- MUSA_INCLUDE_DIRS : MUSA头文件目录,默认为/usr/local/musa/include
- MUSA_LIBRARIES : MUSA运行时库
- MUSA_MUFFT_LIBRARIES : muFFT数学库
- MUSA_MUBLAS_LIBRARIES : muBLAS数学库,对数学库的使用建议采用MUSAToolkit模块的方式,见4.3.3小节。
4.3.2.2. 函数宏
cmake中使用函数宏,其名字不区分大小写。
-
MUSA_ADD_EXECUTABLE : 添加目标可执行文件。
musa_add_executable(${PROJECT_NAME} file1.mu file2.cu main.cpp)# 如上可添加多个编译单元。若编译单元文件后缀名为.mu或者.cu,则会自动被识别成MUSA代码,# 也即该编译单元内含有device端运行的kernel代码,从而被mcc编译。对于其他的后缀名如.c,# .cc,.cpp等,则仅被当成常规的c代码被host端的c或c++编译器编译。最后这些编译单元的obj产物# 会被一起链接成为最终的可执行文件。值得注意的是,这个宏会自动添加MUSA相关的头文件以及# 自动链接MUSA运行时库,用户无需额外添加。set_source_files_properties(file2.c PROPERTIES MUSA_SOURCE_PROPERTY_FORMAT OBJ)musa_add_executable(${PROJECT_NAME} file1.mu file2.c main.cpp)# 有时候编译文件虽然后缀名不是.mu或.cu,但是文件里面也写有kernel代码,如上面的file2.c。# 这时候需要对该文件做标识,让cmake把此文件当成MUSA代码用mcc来编译。要使用# set_source_files_properties(... PROPERTIES MUSA_SOURCE_PROPERTY_FORMAT OBJ) 来标识。# 需注意set_source_files_properties要放在musa_add_executable语句之前。 -
MUSA_ADD_LIBRARY : 添加目标库文件,细节与添加可执行文件相同。
musa_add_library(${LIB_NAME} SHARED file1.mu file2.cu)# SHARED标识编译动态链接库,STATIC标识编译静态链接库。set_source_files_properties(file2.c PROPERTIES MUSA_SOURCE_PROPERTY_FORMAT OBJ)musa_add_library(${LIB_NAME} SHARED file1.mu file2.c)# 指定非.mu 后缀文件为MUSA代码。# 需注意set_source_files_properties要放在musa_add_library语句之前。 -
MUSA_COMPILE : 编译所有输入的MUSA代码,并返回输出obj文件列表,供add_library或者add_executable使用。
musa_compile(MUSA_OBJ file1.mu file2.cu)add_excutable(${PROJECT_NAME} main.cpp ${MUSA_OBJ})# file1.mu file2.cu 包含MUSA代码,并提供c++接口。# main.cpp 仅调用了上面提供的c++接口。# musa_compile将输入的文件用mcc编译成obj文件,# 输出obj文件列表返回到第一个参数MUSA_OBJ。# 后面的所有c++代码均用host端的c++编译器编译。 -
MUSA_INCLUDE_DIRECTORIES : 为MUSA代码编译添加头文件目录,将传递给mcc编译器。
musa_include_directories(${CMAKE_CURRENT_SOURCE_DIR}/internal)musa_add_executable(${PROJECT_NAME} main.mu)# 假设项目目录结构如下,其中main.mu包含inc.h# project# |-- main.mu# |-- CMakeList.txt# |-- internal# |-- inc.h# 需注意musa_include_directories要放在musa_add_executable语句之前。 -
MUSA_ADD_MUFFT_TO_TARGET : 将muFFT数学库链接到目标。
musa_add_executable(${PROJECT_NAME} file1.mu main.cpp)musa_add_mufft_to_target(${PROJECT_NAME})# 等价于添加链接目标-lmufft。# 需注意musa_add_mufft_to_target要放在musa_add_executable语句之后。 -
MUSA_ADD_MUBLAS_TO_TARGET : 将muBLAS数学库链接到目标。
musa_add_executable(${PROJECT_NAME} file1.mu main.cpp)musa_add_mublas_to_target(${PROJECT_NAME})# 等价于添加链接目标-lmublas。# 需注意musa_add_mublas_to_target要放在musa_add_executable语句之后。
4.3.2.3. 输入变量
MUSA模块中提供了一些输入变量,通过这些变量可以控制MUSA_ADD_EXECUTABLE, MUSA_ADD_LIBRARY和MUSA_COMPILE的一些细节行为, 这些变量的设定需要放在那三个函数宏的使用之前。
-
MUSA_VERBOSE_BUILD : 构建MUSA代码的时候是否打印信息,默认OFF不打印。
set(MUSA_VERBOSE_BUILD ON)musa_add_executable(${PROJECT_NAME} file1.mu main.cpp)# 需注意set(MUSA_VERBOSE_BUILD)要放在musa_add_executable语句之前。 -
MUSA_SOURCE_PROPERTY_FORMAT : 编译文件的格式属性,设置后文件会被强制当成MUSA代码文件。
set_source_files_properties(file2.c PROPERTIES MUSA_SOURCE_PROPERTY_FORMAT OBJ)musa_add_executable(${PROJECT_NAME} file1.mu file2.c main.cpp)# 有时候编译文件虽然后缀名不是.mu或.cu,但是文件里面也写有kernel代码,如上面的file2.c。# 这时候需要对该文件做标识,让cmake把此文件当成MUSA代码用mcc来编译。要使用# set_source_files_properties(... PROPERTIES MUSA_SOURCE_PROPERTY_FORMAT OBJ) 来标识。# 需注意set_source_files_properties要放在musa_add_executable语句之前。set_source_files_properties(file2.c PROPERTIES MUSA_SOURCE_PROPERTY_FORMAT OBJ)musa_add_library(${LIB_NAME} SHARED file1.mu file2.c)# 需注意set_source_files_properties要放在musa_add_library语句之前。 -
MUSA_MCC_FLAGS, MUSA_MCC_FLAGS_CONFIG : 给mcc编译器传入额外的编译选项,CONFIG可以是Debug, MinSizeRel, Release或RelWithDebInfo。默认为空。
set(MUSA_MCC_FLAGS --offload-arch=mp_21)musa_add_executable(${PROJECT_NAME} file1.mu file2.c main.cpp)# 针对quyuan1对应的mp_21架构编译。# 需注意set(MUSA_MCC_FLAGS)要放在musa_add_executable语句之前。 -
MUSA_GENERATED_OUTPUT_DIR : 设置mcc编译生成MUSA目标obj文件的存放路径,默认为CMAKE_CURRENT_BINARY_DIR。
-
MUSA_LINK_LIBRARIES_KEYWORD : 设置cmake链接时的传递属性,可以为PRIVATE,PUBLIC,INTERFACE,默认为空。
musa_add_library(${LIB_NAME} SHARED file1.mu)target_link_libraries(${LIB_NAME} lib)# musa_add_library或musa_add_executable执行过程默认链接了MUSA运行时库,# 并且默认链接传递属性为空,后面target_link_libraries给目标链接其他库的时候# 也需要保持一致,这里不给传递属性。set(MUSA_LINK_LIBRARIES_KEYWORD PRIVATE)musa_add_library(${LIB_NAME} SHARED file1.mu)target_link_libraries(${LIB_NAME} PRIVATE lib)# 有时候用户需要设置目标的链接传递属性, 比方说PRIVATE,# 使用target_link_libraries(... PRIVATE ...)链接其他库。# cmake要求每个目标的所有库的链接传递属性都保持一致,# 因此对MUSA运行时库的链接传递属性也需要设置为PRIVATE,# 使用set(MUSA_LINK_LIBRARIES_KEYWORD PRIVATE)进行设置。# 注意该语句需要放在musa_add_library之前。
4.3.3. 使用MUSAToolkit模块
cmake版本3.17之后,官方新增加了CUDAToolkit模块。其用途是,有些工程并没有包含任何device端的kernel代码,但是调用了NVIDIA官方提供的数学库或图像处理库等现成的c或c++接口,从而使用GPU进行加速。这种情况下整个工程内全部代码都是c或c++语言编写,无任何CUDA代码,故无需使用nvcc编译器编译工程。那么整个项目仅需要使用host端的c或c++编译器编译,最后链接的时候把运行时库以及数学库等添加上即可。CUDAToolkit模块则提供了所有使用GPU加速可能使用到的库目标。
为了保证对CUDA使用的最佳兼容,MUSAToolkit中也包含了cmake的MUSAToolkit模块。基本使用方式如下:
cmake_minimum_required(VERSION 3.10)
set(PROJECT_NAME demo)
project(${PROJECT_NAME} LANGUAGES CXX)
# 载入MUSAToolkit模块
list(APPEND CMAKE_MODULE_PATH /usr/local/musa/cmake)
find_package(MUSAToolkit REQUIRED)
# 添加可执行文件
add_executable(${PROJECT_NAME} main.cpp)
# 为目标链接运行时库及数学库
target_link_libraries(${PROJECT_NAME}
MUSA::musart
MUSA::mufft
)
示例项目中调用了傅里叶变换库进行加速。
- 第一步需要使用: find_package(MUSAToolkit REQUIRED),来载入MUSAToolkit模块。同样的由于该模块暂时未被cmake官方收录,仅安装在了MUSA Toolkit的安装目录中,因此需要在载入模块之前将模块的安装目录更新到cmake的MODULE搜索路径中: list(APPEND CMAKE_MODULE_PATH /usr/local/musa/cmake)。
- 模块载入之后,将提供若干库目标以及变量供使用,这个例子中用到了运行时库和傅里叶变换库,故给目标添加链接库MUSA::musart和MUSA::mufft。需要留意,这里模块提供的目标MUSA::已经包含了所需的头文件路径,会自动传递给要编译的目标,故无需再给编译目标添加MUSA相关的头文件目录。
4.3.4. MUSAToolkit模块提供的物件
和MUSA模块不同,MUSAToolkit模块没有提供复杂的函数宏,而是仅提供了若干输出变量以及库目标。
4.3.4.1. 输出变量
当MUSAToolkit模块加载完毕,如下变量将被设置,可供后续使用。
- MUSAToolkit_FOUND : 若MUSAToolkit_FOUND模块加载成功则为true。
- MUSAToolkit_VERSION_MAJOR : MUSAToolkit的大版本。
- MUSAToolkit_VERSION_MINOR : MUSAToolkit的小版本。
- MUSAToolkit_VERSION_PATCH : MUSAToolkit的patch版本。
- MUSAToolkit_VERSION : _MAJOR._MINOR._PATCH。
- MUSAToolkit_BIN_DIR : MUSAToolkit工具链可执行文件目录,默认为/usr/local/musa/bin。
- MUSAToolkit_INCLUDE_DIRS : MUSAToolkit头文件目录,默认为/usr/local/musa/bin/include。
- MUSAToolkit_LIBRARY_DIR : MUSAToolkit链接库存放目录,默认为/usr/local/musa/bin/lib。
- MUSAToolkit_TARGET_DIR : MUSAToolkit的根目录,默认为/usr/local/musa。
- MUSAToolkit_MCC_EXECUTABLE : MUSA编译器文件。
4.3.4.2. 库目标
使用target_link_libraries为目标添加如下需要的库。
- MUSA::musart : MUSA运行时库,包含如musaMalloc,musaFree等api。
- MUSA::musa_driver : MUSA驱动库,包含如muMemAlloc,muMemFree等api。
- MUSA::mublas : 线性代数库。
- MUSA::mufft : 傅里叶变换库。
- MUSA::murand : 随机数生成库。
- MUSA::musolver : 求解器相关库。
- MUSA::musparse : 稀疏矩阵相关库。
- MUSA::mupti : 性能分析库。
- MUSA::muppc, MUSA::muppial, MUSA::muppicc, MUSA::muppicom, MUSA::muppidei, MUSA::muppif, MUSA::muppig, MUSA::muppim, MUSA::muppist, MUSA::muppisu, MUSA::muppitc, MUSA::mupps : 二维图像及信号处理。

