循环和数据切分#
在前面的介绍中,经过Tiling配置后,Tensor的输入和输出会在核内按照Tiling配置方案进行切分处理。例如,如果原始Tensor为(1, 32, 1, 256),Tiling方案配置为(1, 4, 1, 64)后,核内将按照(1, 4, 1, 64)进行切分和循环处理。
如果数据量增大,Tensor配置变为(32, 32, 1, 256),第一个轴(shape[0]或Batch轴)可以通过pypto.loop增加循环逻辑,使框架能够将多个batch展开并行处理。
基础循环结构#
SHAPE = (32, 32, 1, 256)
@pypto.frontend.jit
def add_kernel(
input0: pypto.Tensor(SHAPE, pypto.DT_FP32),
input1: pypto.Tensor(SHAPE, pypto.DT_FP32),
out: pypto.Tensor(SHAPE, pypto.DT_FP32),
val: int
):
pypto.set_vec_tile_shapes(1, 4, 1, 64)
#calculate the loop parameters
b, n, s, d = SHAPE
tile_b = 1
b_loop = b // tile_b
for idx in pypto.loop(b_loop):
b_offset = idx * tile_b
b_offset_end = (idx + 1) * tile_b
t0_sub = input0[b_offset:b_offset_end, ...]
t1_sub = input1[b_offset:b_offset_end, ...]
t3_sub = t0_sub + t1_sub
out[b_offset:b_offset_end, ...] = t3_sub + val
循环使用的pypto.loop的全接口入参如下:
for idx in pypto.loop(start, end, step, name="label", idx_name="idx_label", submit_before_loop=False)
参数说明:
start, end, step:可选参数,支持灵活配置循环范围。
name, idx_name:循环标识和索引变量名称,用于调试。
submit_before_loop:控制循环执行顺序。
也可以按需简化成:
for idx in pypto.loop(start, end, step) #不带loop的名字标签
for idx in pypto.loop(start, end) # 默认step=1
for idx in pypto.loop(end) # 默认start=0, step=1
slicing语法糖的完整样例请参考:loop.py。
使用view/assemble接口处理循环结构#
上述示例展示了如何在循环中使用Python的切片操作 [:, :, :] 来提取小块数据进行计算。计算完成后,数据会被输出。此外,也可以利用pypto.view接口提取小块数据进行计算,计算完成后,再通过pypto.assemble将数据组装并输出。
SHAPE = (32, 32, 1, 256)
@pypto.frontend.jit
def add_kernel(
input0: pypto.Tensor(SHAPE, pypto.DT_FP32),
input1: pypto.Tensor(SHAPE, pypto.DT_FP32),
out: pypto.Tensor(SHAPE, pypto.DT_FP32),
):
pypto.set_vec_tile_shapes(1, 4, 1, 64)
#calculate the loop parameters
b, n, s, d = SHAPE
tile_b = 1
b_loop = b // tile_b
for idx in pypto.loop(b_loop):
b_offset = idx * tile_b
t0_sub = pypto.view(input0, [tile_b, n, s, d], [b_offset, 0, 0, 0])
t1_sub = pypto.view(input1, [tile_b, n, s, d], [b_offset, 0, 0, 0])
t3_sub = t0_sub + t1_sub
pypto.assemble(t3_sub, [b_offset, 0, 0, 0], out)
view/assemble接口完整样例请参考:add_scalar_loop_view_assemble.py。
数据依赖与循环顺序#
默认情况下,pypto.loop会展开并分发到多核并行处理,适用于无数据依赖的场景。如果循环之间存在数据依赖(如前一个循环的输出是下一个循环的输入),需设置submit_before_loop = True,确保每个循环迭代的结果写回Tensor后,再启动下一个循环:
for idx in pypto.loop(0, b_loop, 1, name="LOOP_L0_bIdx", idx_name="idx", submit_before_loop=True):