-
Notifications
You must be signed in to change notification settings - Fork 4.1k
Add type reduction support to Slice and Transpose #6547
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,55 +4,75 @@ | |
| #include "core/providers/cpu/tensor/slice.h" | ||
| #include "core/providers/cpu/tensor/utils.h" | ||
| #include "core/providers/common.h" | ||
| #include "core/providers/op_kernel_type_control.h" | ||
| #include <unordered_map> | ||
| #include <limits> | ||
|
|
||
| using namespace ::onnxruntime::common; | ||
| using namespace std; | ||
|
|
||
| namespace onnxruntime { | ||
| namespace op_kernel_type_control { | ||
| // we're using one set of types for all opsets | ||
| ORT_SPECIFY_OP_KERNEL_ARG_SUPPORTED_TYPES_ALL_OPSETS( | ||
| kCpuExecutionProvider, kOnnxDomain, Slice, Input, 0, | ||
| ORT_OP_KERNEL_TYPE_CTRL_ALL_TENSOR_DATA_TYPES); | ||
|
|
||
| ORT_SPECIFY_OP_KERNEL_ARG_SUPPORTED_TYPES_ALL_OPSETS( | ||
| kCpuExecutionProvider, kOnnxDomain, Slice, Input, 1, int32_t, int64_t); | ||
| } // namespace op_kernel_type_control | ||
|
|
||
| namespace { | ||
| using EnabledDataTypes = ORT_OP_KERNEL_ARG_ENABLED_TYPE_LIST_ALL_OPSETS(kCpuExecutionProvider, kOnnxDomain, | ||
| Slice, Input, 0); | ||
| using EnabledIndiciesTypes = ORT_OP_KERNEL_ARG_ENABLED_TYPE_LIST_ALL_OPSETS(kCpuExecutionProvider, kOnnxDomain, | ||
| Slice, Input, 1); | ||
|
|
||
| const std::vector<MLDataType> dataTypeConstraints = | ||
| BuildKernelDefConstraintsFunctorFromTypeList<EnabledDataTypes>{}(); | ||
|
|
||
| const std::vector<MLDataType> indicesTypeConstraints = | ||
| BuildKernelDefConstraintsFunctorFromTypeList<EnabledIndiciesTypes>{}(); | ||
|
|
||
| // std::clamp doesn't exist until C++17 so create a local version | ||
| template <typename T> | ||
| const T& clamp(const T& v, const T& lo, const T& hi) { | ||
| if (v < lo) return lo; | ||
| if (v > hi) return hi; | ||
| return v; | ||
| } | ||
| } // namespace | ||
|
|
||
| ONNX_CPU_OPERATOR_VERSIONED_KERNEL( | ||
| Slice, | ||
| 1, 9, | ||
| KernelDefBuilder().TypeConstraint("T", DataTypeImpl::AllTensorTypes()), | ||
| KernelDefBuilder().TypeConstraint("T", dataTypeConstraints), | ||
| Slice1); | ||
|
|
||
| ONNX_CPU_OPERATOR_VERSIONED_KERNEL( | ||
| Slice, | ||
| 10, 10, | ||
| KernelDefBuilder() | ||
| .TypeConstraint("T", DataTypeImpl::AllTensorTypes()) | ||
| .TypeConstraint("Tind", {DataTypeImpl::GetTensorType<int32_t>(), | ||
| DataTypeImpl::GetTensorType<int64_t>()}), | ||
| .TypeConstraint("T", dataTypeConstraints) | ||
| .TypeConstraint("Tind", indicesTypeConstraints), | ||
| Slice10); | ||
|
|
||
| ONNX_CPU_OPERATOR_VERSIONED_KERNEL( | ||
| Slice, | ||
| 11, | ||
| 12, | ||
| KernelDefBuilder() | ||
| .TypeConstraint("T", DataTypeImpl::AllTensorTypes()) | ||
| .TypeConstraint("Tind", {DataTypeImpl::GetTensorType<int32_t>(), | ||
| DataTypeImpl::GetTensorType<int64_t>()}), | ||
| .TypeConstraint("T", dataTypeConstraints) | ||
| .TypeConstraint("Tind", indicesTypeConstraints), | ||
| Slice10); | ||
|
|
||
| ONNX_CPU_OPERATOR_KERNEL( | ||
| Slice, | ||
| 13, | ||
| KernelDefBuilder() | ||
| .TypeConstraint("T", DataTypeImpl::AllTensorTypes()) | ||
| .TypeConstraint("Tind", {DataTypeImpl::GetTensorType<int32_t>(), | ||
| DataTypeImpl::GetTensorType<int64_t>()}), | ||
| .TypeConstraint("T", dataTypeConstraints) | ||
| .TypeConstraint("Tind", indicesTypeConstraints), | ||
| Slice10); | ||
| namespace { | ||
| // std::clamp doesn't exist until C++17 so create a local version | ||
| template <typename T> | ||
| const T& clamp(const T& v, const T& lo, const T& hi) { | ||
| if (v < lo) return lo; | ||
| if (v > hi) return hi; | ||
| return v; | ||
| } | ||
| } // namespace | ||
|
|
||
| // Check if it's possible to combine innermost dimensions so we copy larger blocks. | ||
| // Sets flattened_output_dims to nullptr if it is not. | ||
|
|
@@ -218,19 +238,21 @@ Status SliceBase::PrepareForCompute(const std::vector<int64_t>& raw_starts, | |
| } | ||
|
|
||
| // Slice V10 & DynamicSlice | ||
| void SliceBase::FillVectorsFromInput(const Tensor& start_tensor, | ||
| const Tensor& ends_tensor, | ||
| const Tensor* axes_tensor, | ||
| const Tensor* steps_tensor, | ||
| std::vector<int64_t>& input_starts, | ||
| std::vector<int64_t>& input_ends, | ||
| std::vector<int64_t>& input_axes, | ||
| std::vector<int64_t>& input_steps) { | ||
| ORT_ENFORCE(start_tensor.Shape().NumDimensions() == 1, "Starts must be a 1-D array"); | ||
| ORT_ENFORCE(ends_tensor.Shape().NumDimensions() == 1, "Ends must be a 1-D array"); | ||
| ORT_ENFORCE(start_tensor.Shape() == ends_tensor.Shape(), "Starts and ends shape mismatch"); | ||
| ORT_ENFORCE(nullptr == axes_tensor || start_tensor.Shape() == axes_tensor->Shape(), "Starts and axes shape mismatch"); | ||
| ORT_ENFORCE(nullptr == steps_tensor || start_tensor.Shape() == steps_tensor->Shape(), "Starts and steps shape mismatch"); | ||
| Status SliceBase::FillVectorsFromInput(const Tensor& start_tensor, | ||
|
skottmckay marked this conversation as resolved.
|
||
| const Tensor& ends_tensor, | ||
| const Tensor* axes_tensor, | ||
| const Tensor* steps_tensor, | ||
| std::vector<int64_t>& input_starts, | ||
| std::vector<int64_t>& input_ends, | ||
| std::vector<int64_t>& input_axes, | ||
| std::vector<int64_t>& input_steps) { | ||
| ORT_RETURN_IF_NOT(start_tensor.Shape().NumDimensions() == 1, "Starts must be a 1-D array"); | ||
| ORT_RETURN_IF_NOT(ends_tensor.Shape().NumDimensions() == 1, "Ends must be a 1-D array"); | ||
| ORT_RETURN_IF_NOT(start_tensor.Shape() == ends_tensor.Shape(), "Starts and ends shape mismatch"); | ||
| ORT_RETURN_IF_NOT(nullptr == axes_tensor || start_tensor.Shape() == axes_tensor->Shape(), | ||
| "Starts and axes shape mismatch"); | ||
| ORT_RETURN_IF_NOT(nullptr == steps_tensor || start_tensor.Shape() == steps_tensor->Shape(), | ||
| "Starts and steps shape mismatch"); | ||
|
|
||
| const auto& size = start_tensor.Shape().Size(); | ||
| input_starts.resize(size); | ||
|
|
@@ -241,7 +263,11 @@ void SliceBase::FillVectorsFromInput(const Tensor& start_tensor, | |
| if (nullptr != steps_tensor) | ||
| input_steps.resize(size); | ||
|
|
||
| if (start_tensor.IsDataType<int32_t>()) { | ||
| // check for type reduction of supported indices types | ||
| constexpr bool int32_enabled = op_kernel_type_control::HasType<EnabledIndiciesTypes, int32_t>(); | ||
| constexpr bool int64_enabled = op_kernel_type_control::HasType<EnabledIndiciesTypes, int64_t>(); | ||
|
|
||
| if (int32_enabled && start_tensor.IsDataType<int32_t>()) { | ||
| std::copy(start_tensor.Data<int32_t>(), start_tensor.Data<int32_t>() + size, input_starts.begin()); | ||
| std::copy(ends_tensor.Data<int32_t>(), ends_tensor.Data<int32_t>() + size, input_ends.begin()); | ||
| if (nullptr != axes_tensor) | ||
|
|
@@ -251,7 +277,7 @@ void SliceBase::FillVectorsFromInput(const Tensor& start_tensor, | |
| std::copy(steps_tensor->Data<int32_t>(), steps_tensor->Data<int32_t>() + size, input_steps.begin()); | ||
| } | ||
|
|
||
| else if (start_tensor.IsDataType<int64_t>()) { | ||
| else if (int64_enabled && start_tensor.IsDataType<int64_t>()) { | ||
| std::copy(start_tensor.Data<int64_t>(), start_tensor.Data<int64_t>() + size, input_starts.begin()); | ||
| std::copy(ends_tensor.Data<int64_t>(), ends_tensor.Data<int64_t>() + size, input_ends.begin()); | ||
| if (nullptr != axes_tensor) | ||
|
|
@@ -261,10 +287,13 @@ void SliceBase::FillVectorsFromInput(const Tensor& start_tensor, | |
| std::copy(steps_tensor->Data<int64_t>(), steps_tensor->Data<int64_t>() + size, input_steps.begin()); | ||
| } | ||
|
|
||
| // should not reach this as no kernel is registered for this condition to be triggered - just an additional safety check | ||
| else { | ||
| ORT_THROW("Data type for starts and ends inputs' need to be int32_t or int64_t, but instead got ", start_tensor.DataType()); | ||
| return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, | ||
| "Data type for starts and ends inputs' is not supported in this build. Got ", | ||
| start_tensor.DataType()); | ||
| } | ||
|
|
||
| return Status::OK(); | ||
| } | ||
|
|
||
| template <typename T> | ||
|
|
@@ -304,22 +333,40 @@ static Status SliceImpl(OpKernelContext* ctx, | |
| flattened_input_dims.back() = compute_metadata.p_flattened_output_dims_->back(); | ||
| TensorShape input_shape(std::move(flattened_input_dims)); | ||
|
|
||
| auto input_iterator = SliceIterator<T>(input_tensor, input_shape, compute_metadata.starts_, *compute_metadata.p_flattened_output_dims_, compute_metadata.steps_); | ||
| auto input_iterator = SliceIterator<T>(input_tensor, input_shape, compute_metadata.starts_, | ||
| *compute_metadata.p_flattened_output_dims_, compute_metadata.steps_); | ||
| create_output(input_iterator); | ||
| } else { | ||
| auto input_iterator = SliceIterator<T>(input_tensor, compute_metadata.starts_, compute_metadata.output_dims_, compute_metadata.steps_); | ||
| auto input_iterator = SliceIterator<T>(input_tensor, compute_metadata.starts_, compute_metadata.output_dims_, | ||
| compute_metadata.steps_); | ||
| create_output(input_iterator); | ||
| } | ||
|
|
||
| return Status::OK(); | ||
| } | ||
|
|
||
| template <typename EnabledTypes, typename T> | ||
| static inline bool CallSliceImplIfEnabled(OpKernelContext* ctx, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
wonder if this can be extended to call arbitrary enable-able code with a lambda... #Closed
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. generic lambdas were c++14 and we're currently stuck at c++11, so I didn't think doing it as a lambda was possible. In reply to: 569695618 [](ancestors = 569695618) |
||
| const Tensor& input_tensor, | ||
| SliceOp::PrepareForComputeMetadata& compute_metadata, | ||
| Status& status) { | ||
| constexpr bool enabled = op_kernel_type_control::HasTypeWithSameSize<EnabledTypes, T>(); | ||
| if (enabled) { | ||
| status = SliceImpl<T>(ctx, input_tensor, compute_metadata); | ||
| } | ||
|
|
||
| return enabled; | ||
| } | ||
|
|
||
| Status SliceBase::Compute(OpKernelContext* ctx) const { | ||
| const auto* input_tensor_ptr = ctx->Input<Tensor>(0); | ||
| ORT_ENFORCE(input_tensor_ptr != nullptr, "Missing input tensor to be processed"); | ||
| const auto& input_tensor = *input_tensor_ptr; | ||
| const auto& input_dimensions = input_tensor.Shape().GetDims(); | ||
| if (input_dimensions.empty()) return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT, "Cannot slice scalars"); | ||
|
|
||
| if (input_dimensions.empty()) { | ||
| return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT, "Cannot slice scalars"); | ||
| } | ||
|
|
||
| SliceOp::PrepareForComputeMetadata compute_metadata(input_dimensions); | ||
|
|
||
| // Slice V10 & DynamicSlice | ||
|
|
@@ -340,28 +387,38 @@ Status SliceBase::Compute(OpKernelContext* ctx) const { | |
|
|
||
| Status status = Status::OK(); | ||
|
|
||
| bool supported = false; | ||
| if (input_tensor.IsDataTypeString()) { | ||
| status = SliceImpl<std::string>(ctx, input_tensor, compute_metadata); | ||
| if (op_kernel_type_control::HasType<EnabledDataTypes, std::string>()) { | ||
| supported = true; | ||
| status = SliceImpl<std::string>(ctx, input_tensor, compute_metadata); | ||
| } | ||
| } else { | ||
| const auto element_size = input_tensor.DataType()->Size(); | ||
|
|
||
| // call SliceImpl | ||
| switch (element_size) { | ||
| case sizeof(uint32_t): | ||
| status = SliceImpl<uint32_t>(ctx, input_tensor, compute_metadata); | ||
| supported = CallSliceImplIfEnabled<EnabledDataTypes, uint32_t>(ctx, input_tensor, compute_metadata, status); | ||
| break; | ||
| case sizeof(uint64_t): | ||
| status = SliceImpl<uint64_t>(ctx, input_tensor, compute_metadata); | ||
| supported = CallSliceImplIfEnabled<EnabledDataTypes, uint64_t>(ctx, input_tensor, compute_metadata, status); | ||
| break; | ||
| case sizeof(uint16_t): | ||
| status = SliceImpl<uint16_t>(ctx, input_tensor, compute_metadata); | ||
| supported = CallSliceImplIfEnabled<EnabledDataTypes, uint16_t>(ctx, input_tensor, compute_metadata, status); | ||
| break; | ||
| case sizeof(uint8_t): | ||
| status = SliceImpl<uint8_t>(ctx, input_tensor, compute_metadata); | ||
| supported = CallSliceImplIfEnabled<EnabledDataTypes, uint8_t>(ctx, input_tensor, compute_metadata, status); | ||
| break; | ||
| default: | ||
| ORT_THROW("Unsupported input data type of ", input_tensor.DataType()); | ||
| // leave 'supported' as false | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| if (!supported) { | ||
| status = ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "Unsupported input data type of ", input_tensor.DataType()); | ||
| } | ||
|
|
||
| return status; | ||
| } | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.