0. 前言

cuTLASS 使用到了 cmake,之前没有接触过,先学习一下他的目录结构和编译过程。

2023-03-16 11:25:11,在学习一个工具时,Getting Started 或许是最快的入门方式。

0.1 Cmake 简介

对于 C++ 程序,手动编写 Makefile 非常麻烦。cmake 用于自动编写 Makefile。通过读取 CMakeLists.txt 文件,可以自动生成 make 文件。cmake 中 macro 和 function 的使用,使得 cmake 更像是一个脚本语言。

0.2 安装

官方给出了建议的环境,cmake 没有安装,apt-get install 安装的是 3.12 版本,不符合要求。手动安装一下 3.20 cmake,教程

# get and build CMake
wget https://github.com/Kitware/CMake/releases/download/v3.24.0/cmake-3.24.0.tar.gz
tar -zvxf cmake-3.24.0.tar.gz
cd cmake-3.24.0
./bootstrap
make -j8

# add path
export PATH=$PATH:$CUDA_INSTALL_PATH/bin:~/cmake-3.24.0/bin

注意有个 BUG, Could NOT find OpenSSL,apt-get install libssl-dev 即可

0.3 基本特性

cmake 命令不区分大小写,但是变量区分大小写。

1. 简介

1.1 带有 cmake 的项目

介绍最基本的,带有 cmake 的项目的整体结构

1.1.1 目录结构

|-- bin  #存放可执行文件
|-- build  #目录用于存放编译生成的文件
|  |-- dependencies  #存放 external libraries or dependencies that are required by the project being built
|  |-- CMakeFiles
|-- CMakeLists.txt
|-- include  #统一存放头文件
|  |-- hello.h
|  |-- gpgpu.h
|-- lib
|-- README.md
|-- src
|  |-- CMakeLists.txt
|  |-- main.cpp
|  |-- model1
|  |  |-- CMakeLists.txt
|  |  |-- gpgpu.cpp
|  |  |-- model.cpp
|  |-- model2
|  |  |-- CMakeLists.txt
|  |  |-- hello.cpp
|  |  |-- model.cpp

对于大一点的项目可能还会需要 util 目录,library 目录夹或者 tool 目录

src: 这个 example 包含了 2 models,main.cpp 依赖于 2 基础 models,另外,注意到他们都包含 CMakeLists.txt 文件

1.1.2 理解 CMakeLists.txt

注意 cmake 文件中不区分大小写

In a CMake-based project, each directory containing C++ source code should contain a CMakeLists.txt file. This file describes the rules for building the code in that directory.

2023-03-16 14:19:11,每个 dir 下都要有一个 CMakeLists.txt

最基础的包含以下一些信息,

# 规定该CMakeLists.txt适用的cmake最小版本,这里是 3.12,自己手动安装了 3.20 版本
cmake_minimum_required(VERSION 3.12.4 FATAL_ERROR)

# 项目名称,也就是 cutlass
project(CUTLASS VERSION 2.9.0 LANGUAGES CXX)

# 定义生成的可执行文件(程序)的名称,假设为 gemm
# 这里没找到 cutlass 对应的,cutlass 中有一个 function(cutlass_add_executable_tests NAME TARGET)
add_executable (gemm gemm.cxx)

# 指定头文件搜索路径,根目录下 include
include_directories (include)

1.2 cmake 编译过程

1.2.1 build, 编译并运行

build 目录用于存放编译生成的文件,一般的编译过程:

$ mkdir build && cd build
$ cmake .. -DCUTLASS_NVCC_ARCHS=75  # compile for NVIDIA Turing GPU architecture

1.2.2 问题,可执行程序在哪个目录生成?

看起来似乎是和 Makefile 文件同一目录,而 cutlass 中,执行 make 操作之后,会对程序进行编译,然后直接运行。由于在 sim 上运行需要把 gpgpusim.config 放到程序运行的目录下,所以我们需要知道是在哪个目录运行的。

cmake 设置 library and executable 文件的存放路径:

set(LIBRARY_OUTPUT_PATH path)
set(EXECUTABLE_OUTPUT_PATH path)

如果子目录中的某个CMakeLists.txt中设置了 set(…),就以当前文件中设置的路径为主,否则以父目录中设置的路径为主

如果都没设置呢?从简单的程序来看,在 build 目录下 cmake .. XXXX,在 build 目录下生成 Makefile,执行 make & make install,可执行程序就在当前(build) 目录下。

1.2.3 cmake 文件变量赋值

cutlass 编译 example 报错,不确定问题是不是出在变量的传递。

2023-03-14 15:49:24,似乎是编译 example 的方式不对。

1.2.4 制定 cuda 编译器

使用 CMAKE_CUDA_COMPILER 这个内建变量可以做到。

可以通过命令 cmake -DCMAKE_CUDA_COMPILER="xxx" 来修改

2. 命令行参数,编译选项 command line option

2.1 -D

原来 -D 是一个传参选项,怪不得在 CMakeList 里面直接搜索 -DCUTLASS_NVCC_ARCHS,没有找到这个命令。

-D 的作用就是定义变量的默认值,比如 -DCUTLASS_NVCC_ARCHS=75,也就是定义了 CUTLASS_NVCC_ARCHS 的值

此外,CUTLASS_NVCC_ARCHS 的属性应该得是一个 CACHE STRING,否则可能无法被改变

set(CUTLASS_NVCC_ARCHS ${CUTLASS_NVCC_ARCHS_SUPPORTED} CACHE STRING "The SM architectures requested.")
set(CUTLASS_LIBRARY_KERNELS "" CACHE STRING "Comma delimited list of kernel name filters. If unspecified, only the largest tile size is enabled. If 'all' is specified, all kernels are enabled.")

那么,尝试修改 CMAKE_CUDA_ARCHITECTURES,找到给其赋值,并且属性为 CACHE STRING 的 TCNN_CUDA_ARCHITECTURES,使用命令行实现:

cmake . -B build -DTCNN_CUDA_ARCHITECTURES="75"

2.3 cmake_progress_start

2.4 -B 指定构建目录的路径 -S 指定源代码目录

/home/wmhu/cmake-3.24.0/bin/cmake -S/home/wmhu/work/tiny-cuda-nn -B/home/wmhu/work/tiny-cuda-nn/build --check-build-system CMakeFiles/Makefile.cmake 0

--check-build-system 选项用于检查构建系统是否可用,以及检查是否需要重新构建

0 是一个可选的参数,用于设置生成Makefile时的调试级别。

3. Command 命令

有点像函数,但是英文是 command

3.1 set 系列

3.1.1 Set Normal Value

set (<variable> <value>... [PARENT_SCOPE]) set(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}")

If the PARENT_SCOPE option is given the variable will be set in the scope above the current scope. Each new directory or function() command creates a new scope. A scope can also be created with the block() command.

目前还没有太明白 scope 的概念。

3.1.2 Set Cache Entry

set(<variable> <value>... CACHE <type> <docstring> [FORCE])

FORCE option to overwrite existing entries

3.2 add 系列

3.2.1 添加编译选项 add_compilie_options

对于 C/C++ 代码,一般来说,编译选项命名为 FLAG。可以通过 add_compilie_options 命令设置编译选项,也可以通过 set 命令修改 CMAKE_CXX_FLAGS 或者 CMAKE_C_FLAGS

  • add_compile_options 添加的选项是针对所有编译器,包括 C 和 C++
  • set 命令设置的 CMAKE_CXX_FLAGS 或者 CMAKE_C_FLAGS 变量分别针对 C 和 C++

e.g.

#判断编译器类型,如果是gcc编译器,则在编译选项中加入c++11支持
if(CMAKE_COMPILER_IS_GNUCXX)
    set(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}")
    message(STATUS "optional:-std=c++11")
endif(CMAKE_COMPILER_IS_GNUCXX)

add_compile_options(<option> ...)
# 例子
add_compile_options(-Wall -Wextra -pedantic -Werror -g)

3.3 message

这个函数的功能是打印

message(STATUS "Obtained CUDA architectures from CMake variable TCNN_CUDA_ARCHITECTURES=${TCNN_CUDA_ARCHITECTURES}")

3.4 function

function(<name> [<arg1> ...])
  <commands>
endfunction()

NOTE: A function opens a new scope: see set(var PARENT_SCOPE) for details.

function 伴随 scope 的概念,或许可以用局部变量和全局变量去理解。

3.5 list

list 有很多子命令,包括 APPEND, INSERT 等,用于修改变量。

if (MSVC)
	list(APPEND CUDA_NVCC_FLAGS "-Xcompiler=/bigobj")
else()
	list(APPEND CUDA_NVCC_FLAGS "-Xcompiler=-Wno-float-conversion")
	list(APPEND CUDA_NVCC_FLAGS "-Xcompiler=-fno-strict-aliasing")
	list(APPEND CUDA_NVCC_FLAGS "-Xcudafe=--diag_suppress=unrecognized_gcc_pragma")

4. 属性 Property

在 CMake 中,Properties(属性)是一种用于设置目标属性的机制。一个目标可以有多个属性,每个属性有一个名称和一个值。

例如,一个目标可以有一个名为“CXX_STANDARD”的属性,其值为“11”。属性可以影响编译和链接过程中的行为,包括编译器和链接器选项、编译器和链接器特性,以及构建目标的方式。可以使用 set_target_properties() 命令来设置目标属性。

set_target_properties(MyTarget PROPERTIES CXX_STANDARD 11)

5. 模块和包管理 module, package

Object file and link

在目录 mlp_learning_an_image.dir 生成 .o 文件之后,同一个目录下有 link.txt 文件,这个文件就是用来进行链接的。

解析一下

/usr/bin/c++ -O3 -DNDEBUG CMakeFiles/mlp_learning_an_image.dir/mlp_learning_an_image.cu.o CMakeFiles/mlp_learning_an_image.dir/__/dependencies/stbi/stbi_wrapper.cpp.o -o ../mlp_learning_an_image   -L/usr/local/cuda/targets/x86_64-linux/lib  ../libtiny-cuda-nn.a -lcuda ../dependencies/fmt/libfmt.a -lcudadevrt -lcudart_static -lrt -lpthread -ldl 
# ../libtiny-cuda-nn.a 这个是 tiny-cuda-nn 那一堆编译生成的,也会被链接进来

nvcc -o mlp_learning_an_image CMakeFiles/mlp_learning_an_image.dir/mlp_learning_an_image.cu.o CMakeFiles/mlp_learning_an_image.dir/__/dependencies/stbi/stbi_wrapper.cpp.o -L/usr/local/cuda/targets/x86_64-linux/lib  ../libtiny-cuda-nn.a -lcuda ../dependencies/fmt/libfmt.a -lcudadevrt -lcudart_static -lrt -lpthread -ldl

/usr/bin/ar qc libtiny-cuda-nn.a "CMakeFiles/tiny-cuda-nn.dir/src/common.cu.o"...ar 命令用于创建和管理 static lib, qc 是其 opti

Reference

https://zhuanlan.zhihu.com/p/93895403