kernel_band_pipeline.bend source
kernel_band_pipeline.bend on the hub · documented module
# Independent row bands keep their output owners. Fork distributes existing# core/halo owners; only read-only weights and bias need affine clones.import Baseimport ./tensor_band.bend as Bandsimport ./tensor_band_window.bend as BandWindowimport ./kernel_window.bend as KernelWindowimport ./kernel_pipeline.bend as Pipelineimport ./kernel_gemm_buffer.bend as GemmBufferimport ./kernel_tile.bend as Tileimport ./storage_buffer.bend as Storageimport ./storage_clone.bend as Cloneimport ./traversal_partition.bend as Partitionimport ./math_activation.bend as Activationstype Context is Data: Context{window: BandWindow.Geometry,channels: U32,inner: U32,parallel: Bool,activation: Activations.Activation}# A leaf assembles the rows its outputs read into one contiguous window whose# first row is a stride multiple, then runs the dense leaf on it: packing a# window is packing a shorter image with output rows shifted by first / stride.def windowed(~custom: F32 -> F32,activation: Activations.Activation,+geometry: BandWindow.Geometry,+channels: U32,+inner: U32,+first: U32,+rows: U32,+rows_read: BandWindow.RowWindow, core: Storage.Buffer<F32>,above: Storage.Buffer<F32>,below: Storage.Buffer<F32>,weights: Storage.Buffer<Tile.Tile8>,bias: Storage.Buffer<F32>) -> Bands.Bands(~F32): BandWindow.Geometry{Bands.Geometry{batch,+ci,+height,+width},+kernel,+stride,+padding,+output_height,+output_width} = geometry BandWindow.RowWindow{BandWindow.Rows{+output_first,+output_rows},+above_range,+below_range,+window} = rows_read BandWindow.Rows{+window_first,+window_rows} = window +positions = (output_rows * output_width : U32) Partition.Leaf{Pipeline.blocked(~custom,activation, Pipeline.Work{channels,window_rows,width,kernel,stride,padding,output_width,positions,inner}, KernelWindow.start(((output_first - first / height * output_height) * output_width : U32),stride,output_width,(window_first - first / height * height : U32)), BandWindow.assemble(~F32,0.0,BandWindow.input_geometry(geometry),window,BandWindow.Rows{first,rows},above_range,below_range,core,above,below),weights,bias),output_first,output_rows,Unit{}}def leaf(~custom: F32 -> F32,context: Context,+first: U32,+rows: U32,core: Storage.Buffer<F32>,halo: BandWindow.Halo<F32>, weights: Storage.Buffer<Tile.Tile8>,bias: Storage.Buffer<F32>) -> Bands.Bands(~F32): Context{+geometry,+channels,+inner,parallel,activation} = context BandWindow.Halo{above,below,+rows_read} = halo windowed(~custom,activation,geometry,channels,inner,first,rows,rows_read,core,above,below,weights,bias)def parallel(context: Context) -> Bool: Context{window,channels,inner,parallel,activation} = context paralleldef input_geometry(context: Context) -> Bands.Geometry: Context{window,channels,inner,parallel,activation} = context BandWindow.input_geometry(window)def parallel_plan(+context: Context,plan: Bands.Plan) -> Bool: parallel(context) && Bands.same_image(input_geometry(context),plan)def Parameters() -> Type: Storage.Buffer<Tile.Tile8> & Storage.Buffer<F32>def shared(weights: Storage.Buffer<Tile.Tile8> & Storage.Buffer<Tile.Tile8>,bias: Storage.Buffer<F32> & Storage.Buffer<F32>) -> Parameters() & Parameters(): (left_weights,right_weights) = weights (left_bias,right_bias) = bias ((left_weights,left_bias),(right_weights,right_bias))# Each half of a split owns its copy of the read-only weights and bias.def share(parameters: Parameters()) -> Parameters() & Parameters(): (weights,bias) = parameters shared(Clone.clone(~Tile.Tile8,weights),Clone.clone(~F32,bias))def band(~custom: F32 -> F32,context: Context,first: U32,rows: U32,core: Storage.Buffer<F32>,halo: BandWindow.Halo<F32>,parameters: Parameters()) -> Bands.Bands(~F32): (weights,bias) = parameters +first = first +rows = rows leaf(~custom,context,first,rows,core,halo,weights,bias)# Every core leaf with its halo, through the shared band recursion. Trees of# another shape keep the cores.def run(~custom: F32 -> F32,plan: Bands.Plan,mode: Bool,+context: Context,bands: Bands.Bands(~F32),halos: BandWindow.Halos(~F32), weights: Storage.Buffer<Tile.Tile8>,bias: Storage.Buffer<F32>) -> Bands.Bands(~F32): Bands.each(~Bands.Paired<Storage.Buffer<F32>,BandWindow.Halo<F32>>,~Storage.Buffer<F32>,~Parameters(),~Context,~share, ~Bands.paired(~Storage.Buffer<F32>,~BandWindow.Halo<F32>,~Parameters(),~Context,~band(~custom)), ~Bands.left_of(~Storage.Buffer<F32>,~BandWindow.Halo<F32>), plan,mode,parallel(context),input_geometry(context),context,Bands.zip(~Storage.Buffer<F32>,~BandWindow.Halo<F32>,bands,halos),(weights,bias))def prepared(~custom: F32 -> F32,+context: Context,+plan: Bands.Plan,weights: Storage.Buffer<Tile.Tile8>,bias: Storage.Buffer<F32>, owners: Bands.Bands(~F32) & BandWindow.Halos(~F32)) -> Bands.Bands(~F32): (bands,halos) = owners run(~custom,plan,parallel_plan(context,plan),context,bands,halos,weights,bias)def planned(~custom: F32 -> F32,+context: Context,weights: Storage.Buffer<Tile.Tile8>,bias: Storage.Buffer<F32>, owners: Bands.Bands(~F32) & Bands.Plan) -> Bands.Bands(~F32): Context{window,channels,inner,parallel,activation} = context (bands,+plan) = owners prepared(~custom,context,plan,weights,bias,BandWindow.prepare(~F32,window,0.0,plan,bands))# The weights arrive in the order the kernel reads them.def execute(~custom: F32 -> F32,context: Context,bands: Bands.Bands(~F32),weights: Storage.Buffer<Tile.Tile8>,bias: Storage.Buffer<F32>) -> Bands.Bands(~F32): planned(~custom,context,weights,bias,Bands.plan(~F32,bands))