| 105 | } |
| 106 | |
| 107 | SmallVector<TensorPtr> apply_on_physical_tensor( |
| 108 | const OpDef& def, const SmallVector<TensorPtr>& inputs, |
| 109 | SmallVector<LogicalTensorDesc>& output_descs, const bool& validated) { |
| 110 | auto&& op_def = def.cast_final_safe<Concat>(); |
| 111 | int axis = op_def.axis >= 0 ? op_def.axis : op_def.axis + inputs[0]->layout().ndim; |
| 112 | |
| 113 | CompNode& oup_cn = output_descs[0].comp_node; |
| 114 | TensorLayout& oup_layout = output_descs[0].layout; |
| 115 | if (validated) { |
| 116 | if (op_def.comp_node.valid()) { |
| 117 | mgb_assert(op_def.comp_node == oup_cn, "Concat compnode infer error"); |
| 118 | } |
| 119 | } else { |
| 120 | // prepare inputs and output layout |
| 121 | oup_cn = inputs[0]->comp_node(); |
| 122 | SmallVector<const TensorLayout*> inputs_holder(inputs.size()); |
| 123 | for (size_t i = 0; i < inputs.size(); ++i) { |
| 124 | inputs_holder[i] = &inputs[i]->layout(); |
| 125 | } |
| 126 | oup_layout = concat_layout_deduce(inputs_holder, axis); |
| 127 | } |
| 128 | auto oup = Tensor::make(oup_layout, oup_cn); |
| 129 | // because the dnn concat is very slow, we copy the slice code from |
| 130 | // src/opr/impl/tensor_manip.cpp |
| 131 | auto&& out = oup->dev_tensor(); |
| 132 | size_t end = 0; |
| 133 | for (auto&& input : inputs) { |
| 134 | auto&& in = input->dev_tensor(); |
| 135 | auto begin = end; |
| 136 | end = begin + in.shape().shape[axis]; |
| 137 | if (!in.layout().is_empty()) { |
| 138 | out.sub(Slice(begin, end).apply(out.layout(), axis)) |
| 139 | .copy_from_fixlayout(in); |
| 140 | } |
| 141 | } |
| 142 | return {oup}; |
| 143 | } |
| 144 | |
| 145 | OP_TRAIT_REG(Concat, Concat) |
| 146 | .apply_on_var_node(apply_on_var_node) |
nothing calls this directly
no test coverage detected