自动调优流程
具体操作
本章节以模板库example/00_basic_matmul为例,介绍如何利用msKPP工具提供的Python接口实现自动调优功能。

在运行过程中出现任何异常,可通过设置环境变量的方式来查看debug日志以及保留中间文件,便于问题定位。
export MSKPP_LOG_LEVEL=0
- 完成算子Kernel开发后,Kernel函数的定义与实现在basic_matmul.cpp文件中,如下所示:
// basic_matmul.cpp // ... template <class LayoutA, class LayoutB, class LayoutC> ACT_GLOBAL void BasicMatmul( GemmCoord problemShape, GM_ADDR gmA, LayoutA layoutA, GM_ADDR gmB, LayoutB layoutB, GM_ADDR gmC, LayoutC layoutC ) { // Kernel 实现 } // ...
- 参考附录,在examples/00_basic_matmul目录中创建Python脚本文件basic_matmul_autotune.py与编译脚本文件jit_build.sh。
按照如下要求,定义算子Kernel函数的Python接口:在Python脚本中定义basic_matmul函数,其入参需与C++代码中的Kernel函数保持一致。
# basic_matmul_autotune.py import mskpp def get_kernel(): kernel_file = "./basic_matmul.cpp" kernel_name = "BasicMatmul" build_script = "./jit_build.sh" # kernel compile script config = mskpp.KernelInvokeConfig(kernel_file, kernel_name) gen_file = mskpp.Launcher(config).code_gen() kernel = mskpp.compile(build_script=build_script, launch_src_file=gen_file) return kernel def basic_matmul(problem_shape, a, layout_a, b, layout_b, c, layout_c): # This function's input arguments must exactly match the kernel function. kernel = get_kernel() blockdim = 20 # use the correct aic number that matches your hardware return kernel[blockdim](problem_shape, a, layout_a, b, layout_b, c, layout_c, device_id=1) # invoke the kernel
- 参考如下代码实现,构造Kernel入参,实现basic_matmul函数的正常运行。
- 若算子Kernel函数入参是GM_ADDR,则构造入参需使用numpy.array类型。
- 若算子Kernel函数入参是C++结构体对象,则需借助ctypes.Structure在Python中构建一个相同的结构体。
# basic_matmul_autotune.py import numpy as np from ctypes import Structure, c_uint32, c_int32, c_int64 class GemmCoord(Structure): _fields_ = [("m", c_uint32), ("n", c_uint32), ("k", c_uint32)] def __init__(self, m, n, k): super().__init__() self.m = (c_uint32)(m) self.n = (c_uint32)(n) self.k = (c_uint32)(k) @staticmethod def get_namespace(): return "Catlass::" class RowMajor(Structure): _fields_ = [("shape", c_int32 * 2), ("stride", c_int64 * 2)] def __init__(self, rows : int = 0, cols : int = 0, ldm : int = None): super().__init__() self.shape = (c_int32 * 2)(rows, cols) if ldm is None: self.stride = (c_int64 * 2)(cols, 1) else: self.stride = (c_int64 * 2)((c_int64)(ldm), 1) @staticmethod def get_namespace(): return "Catlass::layout::" if __name__ == "__main__": # 创建kernel输入输出 m = 256 n = 512 k = 1024 problem_shape = GemmCoord(m, n, k) layout_a = RowMajor(m, k) layout_b = RowMajor(k, n) layout_c = RowMajor(m, n) a = np.random.randint(1, 2, [m, k]).astype(np.half) b = np.random.randint(1, 2, [k, n]).astype(np.half) c = np.zeros([m, n]).astype(np.half) # 调用kernel basic_matmul(problem_shape, a, layout_a, b, layout_b, c, layout_c) # 精度比对 golden = np.matmul(a, b) is_equal = np.array_equal(c, golden) result = "success" if is_equal else "failed" print("compare {}.".format(result))
- 运行Python脚本,获得如下提示,说明算子Kernel已可正常通过Python接口拉起。
$ python3 basic_matmul_autotune.py compare success.
- 在算子代码程序basic_matmul.cpp中标识需调优的参数。
在模板参数的声明代码行末尾使用// tunable标记,用于替换"="号后的代码内容。
using L1TileShape = GemmShape<128, 256, 256>; // tunable using L0TileShape = GemmShape<128, 256, 64>; // tunable
除tunable标识的方法之外,还可以通过换行,在需要整行替换的代码行末尾使用// tunable: 别名(L0Shape)方式标记。其中,别名用于搜索空间索引。using L0TileShape = MatmulShape<128, 256, 64>; // tunable: L0Shape
- 通过autotune接口的configs入参定义参数搜索空间,每一类参数组合会替换算子Kernel代码中被标记的代码行,然后进行编译、运行并完成Kernel性能采集。搜索空间定义示例可参考如下所示。
- configs数量需小于等于16。
- 参数替换需合理,不能造成编译或运行错误。
- 参数替换原则如下(以configs中的第一行为例):
- 先替换// tunable: L0Shape方式标记的参数,将标记代码行(MatmulShape<128, 256, 64>)整行替换为configs中的value字符串(MatmulShape<128, 256, 64>)。
- 再替换// tunable方式标记的代码行,将"="号后的MatmulShape<128, 256, 256>替换为configs中value字符串MatmulShape<64, 64, 64>。
- 不同作用域中,可能会有两个同名的变量被声明。若两个变量均符合匹配规则时,仅第一个变量会被修改。
- 若其中一个config未匹配成功,该config对应的任务会停止并报错。但其他匹配成功的config将会成功进行参数替换。
@mskpp.autotune(configs=[ # add and try your own config here for a better kernel performance {'L1TileShape': 'GemmShape<128, 256, 256>', 'L0TileShape': 'GemmShape<128, 256, 64>'}, #0 the same config as in basic_matmul.cpp {'L1TileShape': 'GemmShape<128, 256, 128>', 'L0TileShape': 'GemmShape<128, 256, 64>'}, {'L1TileShape': 'GemmShape<128, 128, 256>', 'L0TileShape': 'GemmShape<128, 128, 64>'}, {'L1TileShape': 'GemmShape<64, 128, 128>', 'L0TileShape': 'GemmShape<64, 128, 128>'}, {'L1TileShape': 'GemmShape<64, 128, 256>', 'L0TileShape': 'GemmShape<64, 128, 128>'}, {'L1TileShape': 'GemmShape<64, 128, 512>', 'L0TileShape': 'GemmShape<64, 128, 128>'}, {'L1TileShape': 'GemmShape<64, 64, 128>', 'L0TileShape': 'GemmShape<64, 64, 128>'}, {'L1TileShape': 'GemmShape<64, 64, 256>', 'L0TileShape': 'GemmShape<64, 64, 128>'}, {'L1TileShape': 'GemmShape<64, 64, 512>', 'L0TileShape': 'GemmShape<64, 64, 128>'}, {'L1TileShape': 'GemmShape<128, 128, 128>', 'L0TileShape': 'GemmShape<128, 128, 128>'}, {'L1TileShape': 'GemmShape<128, 128, 256>', 'L0TileShape': 'GemmShape<128, 128, 128>'}, {'L1TileShape': 'GemmShape<128, 128, 512>', 'L0TileShape': 'GemmShape<128, 128, 128>'}, ], warmup=1000, repeat=10, device_ids=[0]) # set kernel warmup 1000us
- 执行basic_matmul_autotune.py文件运行算子,获得每种参数组合的耗时及最佳调优参数集合。以下仅展示可能的一种命令行输出结果。
# python3 basic_matmul_autotune.py No.0: 22.562μs, {'L1TileShape': 'GemmShape<128, 256, 256>', 'L0TileShape': 'GemmShape<128, 256, 64>'} No.1: 22.109μs, {'L1TileShape': 'GemmShape<128, 256, 128>', 'L0TileShape': 'GemmShape<128, 256, 64>'} No.2: 17.778μs, {'L1TileShape': 'GemmShape<128, 128, 256>', 'L0TileShape': 'GemmShape<128, 128, 64>'} No.3: 15.378μs, {'L1TileShape': 'GemmShape<64, 128, 128>', 'L0TileShape': 'GemmShape<64, 128, 128>'} No.4: 14.982μs, {'L1TileShape': 'GemmShape<64, 128, 256>', 'L0TileShape': 'GemmShape<64, 128, 128>'} No.5: 15.671μs, {'L1TileShape': 'GemmShape<64, 128, 512>', 'L0TileShape': 'GemmShape<64, 128, 128>'} No.6: 19.592μs, {'L1TileShape': 'GemmShape<64, 64, 128>', 'L0TileShape': 'GemmShape<64, 64, 128>'} No.7: 18.340μs, {'L1TileShape': 'GemmShape<64, 64, 256>', 'L0TileShape': 'GemmShape<64, 64, 128>'} No.8: 18.541μs, {'L1TileShape': 'GemmShape<64, 64, 512>', 'L0TileShape': 'GemmShape<64, 64, 128>'} No.9: 20.652μs, {'L1TileShape': 'GemmShape<128, 128, 128>', 'L0TileShape': 'GemmShape<128, 128, 128>'} No.10: 17.728μs, {'L1TileShape': 'GemmShape<128, 128, 256>', 'L0TileShape': 'GemmShape<128, 128, 128>'} No.11: 17.637μs, {'L1TileShape': 'GemmShape<128, 128, 512>', 'L0TileShape': 'GemmShape<128, 128, 128>'} Best config: No.4 compare success.
通过对比得知,No.4为最佳调优参数集合。
父主题: 自动调优