Repository: microsoft/onnxruntime
File: onnxruntime/core/providers/qnn/builder/qnn_utils.cc (C++)

Code before:
                                           (d1 * input_strides[1]) +
                                           (d2 * input_strides[2]) +
                                           (d3 * input_strides[3]) +
                                           (d4 * input_strides[4]));
            const size_t dst_elem_index = ((d0 * output_strides[perm_inverse[0]]) +
                                           (d1 * output_strides[perm_inverse[1]]) +
                                           (d2 * output_strides[perm_inverse[2]]) +
                                           (d3 * output_strides[perm_inverse[3]]) +
                                           (d4 * output_strides[perm_inverse[4]]));

            const size_t src_byte_index = src_elem_index * elem_byte_size;
            const size_t dst_byte_index = dst_elem_index * elem_byte_size;
            assert(src_byte_index < input_buffer.size());
            assert(dst_byte_index < output_buffer.size());

            std::memcpy(&output_buffer[dst_byte_index], &input_buffer[src_byte_index], elem_byte_size);
          }
        }
      }
    }
  }

  return Status::OK();
}

// Use use_dummy_tensor flag when performing only QNN op validation and no real tensor data is required.
Status TwoDimensionTranspose(const QnnModelWrapper& qnn_model_wrapper,
                             std::vector<uint32_t>& data_shape,
                             const onnx::TensorProto& initializer,
                             std::vector<uint8_t>& transposed_data,
                             const logging::Logger& logger,
                             bool use_dummy_tensor) {
  ORT_RETURN_IF_NOT(data_shape.size() == 2, "Expected shape of rank 2");

  std::array<size_t, 2> perm = {1, 0};
  std::vector<uint32_t> output_shape(data_shape.size());
  ORT_RETURN_IF_ERROR((qnn::utils::PermuteShape<uint32_t, size_t>(data_shape, perm, output_shape)));

  auto onnx_type = static_cast<ONNX_NAMESPACE::TensorProto_DataType>(initializer.data_type());
  const size_t elem_byte_size = qnn::utils::GetElementSizeByType(onnx_type);
  ORT_RETURN_IF_NOT(elem_byte_size != 0, "Can't get element byte size from given ONNX type");

  std::vector<uint8_t> input_buffer;
  ORT_RETURN_IF_ERROR(qnn_model_wrapper.UnpackInitializerData(initializer, input_buffer));
  transposed_data.resize(input_buffer.size(), 0);

  if (use_dummy_tensor) {  // Only shape & dtype validation are needed, no need for real tensor
    LOGS(logger, VERBOSE) << "Only shape and dtype validation are required, so we can use dummy tensor to avoid heavy memcpy.";
    data_shape = std::move(output_shape);  // Update parameter with final transposed shape
    return Status::OK();
  }

Review comment (bug):
this `use_dummy_tensor` flag feels a bit unintuitive to me. what is the "dummy tensor"?

if you want to skip the population of `transposed_data`, perhaps make it optional instead? e.g., `std::vector<uint8_t>* transposed_data` and skip the population if it is nullptr.

Code after:
                                           (d1 * input_strides[1]) +
                                           (d2 * input_strides[2]) +
                                           (d3 * input_strides[3]) +
                                           (d4 * input_strides[4]));
            const size_t dst_elem_index = ((d0 * output_strides[perm_inverse[0]]) +
                                           (d1 * output_strides[perm_inverse[1]]) +
                                           (d2 * output_strides[perm_inverse[2]]) +
                                           (d3 * output_strides[perm_inverse[3]]) +
                                           (d4 * output_strides[perm_inverse[4]]));

            const size_t src_byte_index = src_elem_index * elem_byte_size;
            const size_t dst_byte_index = dst_elem_index * elem_byte_size;
            assert(src_byte_index < input_buffer.size());
            assert(dst_byte_index < output_buffer.size());

            std::memcpy(&output_buffer[dst_byte_index], &input_buffer[src_byte_index], elem_byte_size);
          }
        }
      }
    }
  }

  return Status::OK();
}

// Use skip_output_data_copy flag when performing only QNN op validation and no real tensor data is required.
Status TwoDimensionTranspose(const QnnModelWrapper& qnn_model_wrapper,
                             std::vector<uint32_t>& data_shape,
                             const onnx::TensorProto& initializer,
                             std::vector<uint8_t>& transposed_data,
                             const logging::Logger& logger,
                             bool skip_output_data_copy) {
  ORT_RETURN_IF_NOT(data_shape.size() == 2, "Expected shape of rank 2");

  std::array<size_t, 2> perm = {1, 0};
  std::vector<uint32_t> output_shape(data_shape.size());
  ORT_RETURN_IF_ERROR((qnn::utils::PermuteShape<uint32_t, size_t>(data_shape, perm, output_shape)));

  auto onnx_type = static_cast<ONNX_NAMESPACE::TensorProto_DataType>(initializer.data_type());
  const size_t elem_byte_size = qnn::utils::GetElementSizeByType(onnx_type);
  ORT_RETURN_IF_NOT(elem_byte_size != 0, "Can't get element byte size from given ONNX type");

  std::vector<uint8_t> input_buffer;
  ORT_RETURN_IF_ERROR(qnn_model_wrapper.UnpackInitializerData(initializer, input_buffer));
  transposed_data.resize(input_buffer.size(), 0);

  if (skip_output_data_copy) {  // Only shape & dtype validation are needed, no need for real tensor
    LOGS(logger, VERBOSE) << "Only shape and dtype validation are required, so we can use dummy tensor to avoid heavy memcpy.";
    data_shape = std::move(output_shape);  // Update parameter with final transposed shape
    return Status::OK();
  }