编译与执行#

PyPTO通过函数定义在NPU硬件上构建可编译的计算图结构,并利用@pypto.frontend.jit装饰器实现即时编译(JIT),从而充分发挥NPU的并行计算能力,从而提升算子执行效率。

Kernel函数定义#

在执行JIT编译前,需要定义Kernel函数,获取输入输出Tensor、配置Tiling信息并实现计算逻辑。

  • 基础函数定义:

    def add_kernel(input: pypto.Tensor, out: pypto.Tensor):
        # Tiling setting
        pypto.set_vec_tile_shapes(1, 4, 1, 64)
        out[:] = input + 1
    
  • 多输入输出函数定义:

    def add_kernel(input0: pypto.Tensor, input1: pypto.Tensor, out: pypto.Tensor):
         # Tiling setting
         pypto.set_vec_tile_shapes(1, 4, 1, 64)
         out[:] = input0 + input1
    

JIT编译#

当通过PyPTO函数完成kernel的计算流及数据流的编写,可以加上pypto.frontend.jit的装饰器, 标记该函数为JIT编译目标,触发PyPTO的编译流程。

@pypto.frontend.jit
def add_kernel(
    input0: pypto.Tensor((1, 4, 1, 64), pypto.DT_FP32),
    input1: pypto.Tensor((1, 4, 1, 64), pypto.DT_FP32),
    out: pypto.Tensor((1, 4, 1, 64), pypto.DT_FP32),
):
     # Tiling setting
     pypto.set_vec_tile_shapes(1, 4, 1, 64)
     out[:] = input0 + input1

JIT编译流程为:

  • 首次调用时,函数以“记录模式”执行,操作被记录并优化为计算图,经编译器生成针对NPU的优化代码后缓存二进制文件;

  • 后续调用时,函数直接调用缓存的二进制文件在NPU上执行,无需重复编译。

完整样例请参考:hello_world

条件编译#

JIT装饰器支持参数配置,可根据配置支持不同的条件编译:

@pypto.frontend.jit(
    host_options={},
    pass_options={},
    runtime_options={},
    verify_options={},
    debug_options={}
)
def advanced_function(input0, input1):
    # 实现自定义计算逻辑
    pass

JIT配置选项说明如下:

  • codegen_options:代码生成设置。

  • host_options:主机端选项。

  • pass_options:编译器PASS传递选项。

  • runtime_options:运行时执行选项。

  • verify_options: 精度检验工具选项。

  • debug_options: 性能数据采集功能配置选项。

除了使用JIT装饰器启用不同配置外, 还可以直接在代码中调用pypto.set_codegen_options、pypto.set_host_options、pypto.set_pass_options、pypto.set_runtime_options,pypto.set_verify_options, pypto.set_debug_options接口进行配置,例如:

pypto.set_codegen_options(support_dynamic_aligned=True)

建议优先使用JIT入参配置各类选项,因为JIT配置选项提供了配置的便利性,同时避免在计算函数内部出现与数据流和计算不相关的代码。

定义多个JIT函数#

您可以定义多个JIT函数并将它们一起使用:

def add_core(input0: pypto.Tensor, input1: pypto.Tensor, output: pypto.Tensor, val: int, add1_flag: bool = False):
    pypto.set_vec_tile_shapes(1, 4, 1, 64)
    if add1_flag:
        t3 = input0 + input1
        output[:] = t3 + val
    else:
        output[:] = input0 + input1

@pypto.frontend.jit
def add_kernel_true(
    input0: pypto.Tensor([pypto.DYNAMIC, 4, 1, 64], pypto.DT_FP32),
    input1: pypto.Tensor([pypto.DYNAMIC, 4, 1, 64], pypto.DT_FP32),
    output: pypto.Tensor([pypto.DYNAMIC, 4, 1, 64], pypto.DT_FP32),
    val: int
):
    add_core(input0, input1, output, val, True)


@pypto.frontend.jit
def add_kernel_false(
    input0: pypto.Tensor([pypto.DYNAMIC, 4, 1, 64], pypto.DT_FP32),
    input1: pypto.Tensor([pypto.DYNAMIC, 4, 1, 64], pypto.DT_FP32),
    output: pypto.Tensor([pypto.DYNAMIC, 4, 1, 64], pypto.DT_FP32),
    val: int
):
    add_core(input0, input1, output, val, False)


#使用这两个函数
def add_add1flag_false(input_data0, input_data1, val=0):
    output_data = torch.empty_like(input_data0)
    add_kernel_false(input_data0, input_data1, output_data, val)
    return output_data

def add_add1flag_true(input_data0, input_data1, val=0):
    output_data = torch.empty_like(input_data0)
    add_kernel_true(input_data0, input_data1, output_data, val)
    return output_data

add_add1flag_false(input_data0, input_data1, val)
add_add1flag_true(input_data0, input_data1, val)

完整样例请参考:multi_jit.py