昇腾社区首页
中文
注册

自动调优流程

自动调优作业流程请参见图1,具体操作请参见具体操作
图1 自动调优作业流程示意图

功能约束

  • 单Device仅支持使用单个msKPP工具进行自动调优,且不推荐同时运行其他算子程序。
  • 需确保先import mskpp再import acl,否则需要在运行前设置环境变量。
    export LD_PRELOAD=${INSTALL_DIR}/lib64/libmspti.so

具体操作

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

在运行过程中出现任何异常,可通过设置环境变量的方式来查看debug日志以及保留中间文件,便于问题定位。

export MSKPP_LOG_LEVEL=0
  1. 完成算子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 实现
    }
    // ...
  2. examples/autotune目录中创建Python脚本文件basic_matmul_autotune.py。
    按照如下要求,定义算子kernel函数的Python接口:
    • 在Python脚本中定义basic_matmul函数,其入参需与C++代码中的kernel函数保持一致。
    • mskpp.compile中的build_script参数可直接使用模板库examples/autotune/helper目录下的jit_build.sh。
    # basic_matmul_autotune.py 
    import mskpp
       
    def get_kernel():
        kernel_file = "../00_basic_matmul/basic_matmul.cpp"        
        kernel_name = "BasicMatmul"
        build_script = "./helper/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):   # 入参需与C++代码中的kernel函数保持一致
        # This function's input arguments must exactly match the kernel function.
        kernel = get_kernel()
        blockdim = 20
        return kernel[blockdim](problem_shape, a, layout_a, b, layout_b, c, layout_c, ) # invoke the kernel
  3. 参考如下代码实现,构造kernel入参,实现basic_matmul函数的正常运行。
    • 若算子kernel函数入参是GM_ADDR,则构造入参需使用numpy.array类型。
    • 若算子kernel函数入参是C++结构体对象,则需借助ctypes.Structure在Python中构建一个相同的结构体。针对模板库提供的结构体对象,如果在examples/autotune/helper/act_type.py中已进行Python实现,则可直接使用,比如GemmCoord、RowMajor、ColumnMajor等。
       import numpy as np
       import mskpp
       from helper.act_type import GemmCoord, RowMajor
       
       if __name__ == "__main__":
       
           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)
       
           basic_matmul(problem_shape, a, layout_a, b, layout_b, c, layout_c)
       
           # check if the output tensor c is consistent with the golden data
           golden = np.matmul(a, b)
           is_equal = np.array_equal(c, golden)
           result = "success" if is_equal else "failed"
           print("compare {}.".format(result))
  4. 运行Python脚本,获得如下提示,说明算子Kernel已可正常通过Python接口拉起。
    $ python3 basic_matmul_autotune.py
    compare success.
  5. 在算子代码程序basic_matmul.cpp中标识需调优的参数。

    在模板参数的声明代码行末尾使用//tunable标记,用于替换"="号后的代码内容,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
  6. 通过autotune接口的configs入参定义参数搜索空间,每一类参数组合会替换算子kernel代码中被标记的代码行,然后进行编译、运行并完成kernel性能采集。搜索空间定义示例可参考如下所示。
    • configs数量需小于等于16。
    • 参数替换需合理,不能造成编译或运行错误。
    • 参数替换原则如下(以configs中的第一行为例):
      1. 先替换// tunable: L0Shape方式标记的参数,将标记代码行(MatmulShape<128, 256, 64>)整行替换为configs中的value字符串(MatmulShape<128, 256, 64>)。
      2. 再替换// 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<64, 64, 64>', 'L0TileShape': 'GemmShape<64, 64, 64>'},
        {'L1TileShape': 'GemmShape<64, 64, 128>', 'L0TileShape': 'GemmShape<64, 64, 64>'},
        {'L1TileShape': 'GemmShape<64, 128, 128>', 'L0TileShape': 'GemmShape<64, 128, 64>'},
        {'L1TileShape': 'GemmShape<128, 128, 128>', 'L0TileShape': 'GemmShape<128, 128, 64>'},
        {'L1TileShape': 'GemmShape<128, 64, 128>', 'L0TileShape': 'GemmShape<128, 64, 64>'},
    ], warmup=1000, repeat=10, device_ids=[0]) # set kernel warmup 1000us
  7. 执行basic_matmul_autotune.py文件运行算子,获得每种参数组合的耗时及最佳调优参数集合。下图展示了可能的一种命令行输出结果。
    # python3 basic_matmul_autotune.py
    No.1: 24.532μs, {'L1TileShape': 'GemmShape<64, 64, 128>', 'L0TileShape': 'GemmShape<64, 64, 64>'}
    No.0: 27.693μs, {'L1TileShape': 'GemmShape<64, 64, 64>', 'L0TileShape': 'GemmShape<64, 64, 64>'}
    No.2: 16.986μs, {'L1TileShape': 'GemmShape<64, 128, 128>', 'L0TileShape': 'GemmShape<64, 128, 64>'}
    No.3: 20.192μs, {'L1TileShape': 'GemmShape<128, 128, 128>', 'L0TileShape': 'GemmShape<128, 128, 64>'}
    No.4: 21.540μs, {'L1TileShape': 'GemmShape<128, 64, 128>', 'L0TileShape': 'GemmShape<128, 64, 64>'}
    Best config: No.2
    compare success.

    通过对比得知,No.2为最佳调优参数集合。