nod-ai / iree-amd-aie

IREE plugin repository for the AMD AIE accelerator
Apache License 2.0
48 stars 24 forks source link

Compilation flags for dispatch formation for deeplabv3 #515

Open MaheshRavishankar opened 1 week ago

MaheshRavishankar commented 1 week ago

The DeeplabV3 i8 model compilation by default might not create the efficient dispatches. Some flags to try

To start with we need --iree-flow-enable-aggressive-fusion --iree-opt-data-tiling=off

To fuse padding with consumer convolutions we would need to add --iree-flow-enable-fuse-padding-into-linalg-consumer-ops

To enable conversion of NCHW convolutions to NHWC we would need

yzhang93 commented 1 week ago

@MaheshRavishankar With these flags, it now generates 83 dispatches. Most dispatches look reasonable to me, but there are still 7 standalone transpose dispatches, like this:

builtin.module {
      func.func @tf2onnx$async_dispatch_4_transpose_32x67081_f32() {
        %c14795072 = arith.constant 14795072 : index
        %c0 = arith.constant 0 : index
        %0 = hal.interface.binding.subspan set(0) binding(0) type(storage_buffer) alignment(64) offset(%c14795072) flags(ReadOnly) : !flow.dispatch.tensor<readonly:tensor<67081x32xf32>>
        %1 = hal.interface.binding.subspan set(0) binding(1) type(storage_buffer) alignment(64) offset(%c0) : !flow.dispatch.tensor<writeonly:tensor<32x67081xf32>>
        %2 = flow.dispatch.tensor.load %0, offsets = [0, 0], sizes = [67081, 32], strides = [1, 1] : !flow.dispatch.tensor<readonly:tensor<67081x32xf32>> -> tensor<67081x32xf32>
        %3 = tensor.empty() : tensor<32x67081xf32>
        %4 = linalg.generic {indexing_maps = [affine_map<(d0, d1) -> (d1, d0)>, affine_map<(d0, d1) -> (d0, d1)>], iterator_types = ["parallel", "parallel"]} ins(%2 : tensor<67081x32xf32>) outs(%3 : tensor<32x67081xf32>) {
        ^bb0(%in: f32, %out: f32):
          linalg.yield %in : f32
        } -> tensor<32x67081xf32>
        flow.dispatch.tensor.store %4, %1, offsets = [0, 0], sizes = [32, 67081], strides = [1, 1] : tensor<32x67081xf32> -> !flow.dispatch.tensor<writeonly:tensor<32x67081xf32>>
        return
      }
    }
yzhang93 commented 1 week ago

All the dispatches can be found here https://github.com/nod-ai/npu-benchmark/blob/main/processed_dispatches.zip

MaheshRavishankar commented 1 week ago

Can you provide the dump of the IR after iree-flow-form-dispatch-regions . It will easy to see what those transpose are.

yzhang93 commented 1 week ago

Can you provide the dump of the IR after iree-flow-form-dispatch-regions . It will easy to see what those transpose are.

Yes, here is the dump IR https://gist.github.com/yzhang93/456640440608e48550308bf87245523c

MaheshRavishankar commented 1 week ago

There are some obvious things here that could help more

  1. %11 = flow.dispatch.region -> (tensor<1x32x259x259xf32>) {
    %237 = linalg.generic {indexing_maps = [affine_map<(d0, d1, d2, d3) -> (d2, d3, d0, d1)>, affine_map<(d0, d1, d2, d3) -> (d0, d1, d2, d3)>], iterator_types = ["parallel", "parallel", "parallel", "parallel"]} ins(%expanded_112 : tensor<259x259x1x32xf32>) outs(%10 : tensor<1x32x259x259xf32>) {
    ^bb0(%in: f32, %out: f32):
      linalg.yield %in : f32
    } -> tensor<1x32x259x259xf32>
    flow.return %237 : tensor<1x32x259x259xf32>
    }
    %12 = tensor.empty() : tensor<1x32x257x257xf32>
    %13 = linalg.fill ins(%cst_14 : f32) outs(%12 : tensor<1x32x257x257xf32>) -> tensor<1x32x257x257xf32>
    %14 = flow.dispatch.region -> (tensor<1x32x257x257xf32>) {
    %237 = linalg.depthwise_conv_2d_nchw_chw {dilations = dense<1> : vector<2xi64>, strides = dense<1> : vector<2xi64>} ins(%11, %cst_27 : tensor<1x32x259x259xf32>, tensor<32x3x3xf32>) outs(%13 : tensor<1x32x257x257xf32>) -> tensor<1x32x257x257xf32>
    flow.return %237 : tensor<1x32x257x257xf32>
    }

    This probably requires the depthwise convs also to be converted to nhwc, then the transpose should fold away

  2. %14 = flow.dispatch.region -> (tensor<1x32x257x257xf32>) {
    %237 = linalg.depthwise_conv_2d_nchw_chw {dilations = dense<1> : vector<2xi64>, strides = dense<1> : vector<2xi64>} ins(%11, %cst_27 : tensor<1x32x259x259xf32>, tensor<32x3x3xf32>) outs(%13 : tensor<1x32x257x257xf32>) -> tensor<1x32x257x257xf32>
    flow.return %237 : tensor<1x32x257x257xf32>
    }
    %15 = tensor.empty() : tensor<257x257x1x32xf32>
    %16 = flow.dispatch.region -> (tensor<257x257x1x32xf32>) {
    %237 = linalg.generic {indexing_maps = [affine_map<(d0, d1, d2, d3) -> (d0, d1, d2, d3)>, affine_map<(d0, d1, d2, d3) -> (d2, d3, d0, d1)>], iterator_types = ["parallel", "parallel", "parallel", "parallel"]} ins(%14 : tensor<1x32x257x257xf32>) outs(%15 : tensor<257x257x1x32xf32>) {
    ^bb0(%in: f32, %out: f32):
      %238 = arith.cmpf ult, %in, %cst_14 : f32
      %239 = arith.select %238, %cst_14, %in : f32
      %240 = arith.cmpf ugt, %239, %cst_2 : f32
      %241 = arith.select %240, %cst_2, %239 : f32
      linalg.yield %241 : f32
    } -> tensor<257x257x1x32xf32>
    flow.return %237 : tensor<257x257x1x32xf32>
    }

    These two should be the same dispatch. Dont know why it isnt. Something is going wrong with iree-flow-form-dispatch-regions pass here.

  3. %52 = flow.dispatch.region -> (tensor<129x129x1x144xf32>) {
    %237 = linalg.generic {indexing_maps = [affine_map<(d0, d1, d2, d3) -> (d0, d1, d2, d3)>, affine_map<(d0, d1, d2, d3) -> (d2, d3, d0, d1)>], iterator_types = ["parallel", "parallel", "parallel", "parallel"]} ins(%50 : tensor<1x144x129x129xf32>) outs(%51 : tensor<129x129x1x144xf32>) {
    ^bb0(%in: f32, %out: f32):
      %238 = arith.cmpf ult, %in, %cst_14 : f32
      %239 = arith.select %238, %cst_14, %in : f32
      %240 = arith.cmpf ugt, %239, %cst_2 : f32
      %241 = arith.select %240, %cst_2, %239 : f32
      linalg.yield %241 : f32
    } -> tensor<129x129x1x144xf32>
    flow.return %237 : tensor<129x129x1x144xf32>
    }
    %collapsed_125 = tensor.collapse_shape %52 [[0], [1], [2, 3]] : tensor<129x129x1x144xf32> into tensor<129x129x144xf32>
    %expanded_126 = tensor.expand_shape %collapsed_125 [[0, 1], [2], [3]] output_shape [1, 129, 129, 144] : tensor<129x129x144xf32> into tensor<1x129x129x144xf32>
    %53 = flow.dispatch.region -> (tensor<1x129x129x144xf32>) {
    %237 = linalg.generic {indexing_maps = [affine_map<(d0, d1, d2, d3) -> (d0, d1, d2, d3)>, affine_map<(d0, d1, d2, d3) -> (d0, d1, d2, d3)>], iterator_types = ["parallel", "parallel", "parallel", "parallel"]} ins(%expanded_126 : tensor<1x129x129x144xf32>) outs(%41 : tensor<1x129x129x144xf32>) {
    ^bb0(%in: f32, %out: f32):
      %238 = arith.divf %in, %cst_10 : f32
      %239 = math.round %238 : f32
      %240 = arith.addf %239, %cst_14 : f32
      %241 = arith.cmpf ult, %240, %cst_16 : f32
      %242 = arith.cmpf ugt, %240, %cst_15 : f32
      %243 = arith.select %241, %cst_16, %240 : f32
      %244 = arith.select %242, %cst_15, %243 : f32
      %245 = arith.fptosi %244 : f32 to i8
      %246 = arith.extsi %245 : i8 to i32
      %247 = arith.sitofp %246 : i32 to f32
      %248 = arith.mulf %247, %cst_10 : f32
      linalg.yield %248 : f32
    } -> tensor<1x129x129x144xf32>
    flow.return %237 : tensor<1x129x129x144xf32>
    }

    The collapse_shape -> expand_shape must be folded away. But that isnt happening for some reason. if that happens the two dispatches will become one dispatch.

Fixing these 3 things must get us into much better shape.

yzhang93 commented 1 week ago

@MaheshRavishankar I tried to pad the conv ops, and this is the IR afterwards https://gist.github.com/yzhang93/d0b09b559800f74314eb2d95c0aa2b7d.

Here I modify the codes to not only pad the intrinsic dimensions (OW, OC, IC), but also the OH dimension. OH dimension has to be padded in order to distribute inputs evenly to 4 AIE cores.

After padding I noticed some issues

newling commented 1 week ago

The collapse_shape -> expand_shape must be folded away. But that isn't happening for some reason. if that happens the two dispatches will become one dispatch.

@MaheshRavishankar Well it's not just a simple fold, it goes from tensor<129x129x1x144xf32> to tensor<1x129x129x144xf32>. To get them to merge, there needs to be some bubbling up or pushing down of the elementwise operation through the reshape operations I think. This kind of optimization can get complicated, because it isn't a local optimization. Is there a pass in IREE that is responsible for this? I'm thinking some optimization where you switch between "bubble up" and "push down" phases, trying to get "math" ops and "reshape" ops to separate into isolated clusters.

This separation of conv and elementwise ops still appears in the quantized model.