From ab709a0ea0f7f5c8e02c315afffc300e09c783a8 Mon Sep 17 00:00:00 2001 From: Kurtis Charnock Date: Fri, 29 Nov 2019 11:43:05 +0000 Subject: COMPMID-2727: Add support for split sizes in NESplit Signed-off-by: Kurtis Charnock Change-Id: I14cb2711f3a02bd5f50976cb78fe5865e2062891 Reviewed-on: https://eu-gerrit-1.euhpc.arm.com/c/VisualCompute/ComputeLibrary/+/214133 Tested-by: bsgcomp Reviewed-by: Georgios Pinitas Reviewed-on: https://review.mlplatform.org/c/2433 Comments-Addressed: Arm Jenkins Tested-by: Arm Jenkins --- arm_compute/runtime/NEON/functions/NESplit.h | 37 ++--------- src/runtime/NEON/functions/NESplit.cpp | 96 +--------------------------- tests/validation/NEON/Split.cpp | 73 ++++++++++++++++++++- 3 files changed, 77 insertions(+), 129 deletions(-) diff --git a/arm_compute/runtime/NEON/functions/NESplit.h b/arm_compute/runtime/NEON/functions/NESplit.h index e4d62048e6..69aef793d5 100644 --- a/arm_compute/runtime/NEON/functions/NESplit.h +++ b/arm_compute/runtime/NEON/functions/NESplit.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2019 ARM Limited. + * Copyright (c) 2018-2020 ARM Limited. * * SPDX-License-Identifier: MIT * @@ -24,8 +24,10 @@ #ifndef ARM_COMPUTE_NESPLIT_H #define ARM_COMPUTE_NESPLIT_H +#include "arm_compute/core/ITensor.h" #include "arm_compute/core/Types.h" +#include "arm_compute/runtime/CPP/functions/CPPSplit.h" #include "arm_compute/runtime/IFunction.h" #include "arm_compute/runtime/NEON/functions/NESlice.h" @@ -34,43 +36,12 @@ namespace arm_compute { -// Forward declarations -class ITensor; - /** Basic function to split a tensor along a given axis */ -class NESplit : public IFunction +class NESplit : public CPPSplit { public: - /** Default constructor */ - NESplit(); - /** Initialise the kernel's input and outputs. - * - * @param[in] input The input tensor. Data types supported: All - * @param[out] outputs A vector containing the output tensors. Data types supported: Same as @p input. - * The output tensors should match the input tensor dimensions for all shape dimensions apart - * from the split dimension. - * @param[in] axis Axis on which to split the input. - */ - void configure(const ITensor *input, const std::vector &outputs, unsigned int axis); - /** Static function to check if given info will lead to a valid configuration of @ref NESplit - * - * @param[in] input The input tensor info. Data types supported: All - * @param[in] outputs A vector containing the output tensors' info. Data types supported: Same as @p input. - * The output tensors should match the input tensor dimensions for all shape dimensions apart - * from the split dimension - * @param[in] axis Axis on which to split the input. - * - * @return a status - */ - static Status validate(const ITensorInfo *input, const std::vector &outputs, unsigned int axis); - // Inherited methods overridden: void run() override; - -private: - std::vector _outputs_vector; - std::vector _slice_functions; - unsigned int _num_outputs; }; } // namespace arm_compute #endif /* ARM_COMPUTE_NESPLIT_H */ diff --git a/src/runtime/NEON/functions/NESplit.cpp b/src/runtime/NEON/functions/NESplit.cpp index 0373ab6f88..d512fe5c09 100644 --- a/src/runtime/NEON/functions/NESplit.cpp +++ b/src/runtime/NEON/functions/NESplit.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2019 ARM Limited. + * Copyright (c) 2018-2020 ARM Limited. * * SPDX-License-Identifier: MIT * @@ -34,100 +34,6 @@ namespace arm_compute { -NESplit::NESplit() - : _outputs_vector(), _slice_functions(), _num_outputs(0) -{ -} - -void NESplit::configure(const ITensor *input, const std::vector &outputs, unsigned int axis) -{ - // Create Slice functions - _num_outputs = outputs.size(); - _slice_functions.resize(_num_outputs); - - // Get output shape - const TensorShape output_shape = arm_compute::misc::shape_calculator::compute_split_shape(input->info(), axis, _num_outputs); - - // Extract output tensor info - std::vector outputs_info; - for(auto &output : outputs) - { - ARM_COMPUTE_ERROR_ON_NULLPTR(output); - outputs_info.emplace_back(output->info()); - } - - // Validate - ARM_COMPUTE_ERROR_THROW_ON(NESplit::validate(input->info(), outputs_info, axis)); - - const size_t axis_split_step = output_shape[axis]; - unsigned int axis_offset = 0; - - // Start/End coordinates - Coordinates start_coords; - Coordinates end_coords; - for(unsigned int d = 0; d < output_shape.num_dimensions(); ++d) - { - end_coords.set(d, -1); - } - - for(unsigned int i = 0; i < _num_outputs; i++) - { - // Update coordinate on axis - start_coords.set(axis, axis_offset); - end_coords.set(axis, axis_offset + axis_split_step); - - // Configure slice function - _slice_functions[i].configure(input, outputs[i], start_coords, end_coords); - - // Set valid region from shape - outputs[i]->info()->set_valid_region(ValidRegion(Coordinates(), output_shape)); - - // Update axis offset - axis_offset += axis_split_step; - } -} - -Status NESplit::validate(const ITensorInfo *input, const std::vector &outputs, unsigned int axis) -{ - ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(input); - ARM_COMPUTE_RETURN_ERROR_ON(axis >= input->num_dimensions()); - ARM_COMPUTE_RETURN_ERROR_ON(outputs.size() < 2); - - // Get output shape - const TensorShape output_shape = arm_compute::misc::shape_calculator::compute_split_shape(input, axis, outputs.size()); - ARM_COMPUTE_RETURN_ERROR_ON(output_shape.total_size() == 0); - - const size_t axis_split_step = output_shape[axis]; - unsigned int axis_offset = 0; - - // Start/End coordinates - Coordinates start_coords; - Coordinates end_coords; - for(unsigned int d = 0; d < output_shape.num_dimensions(); ++d) - { - end_coords.set(d, -1); - } - - // Validate output tensors - for(const auto &output : outputs) - { - ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(output); - - // Output auto inizialitation if not yet initialized - TensorInfo tmp_output_info = *output->clone(); - auto_init_if_empty(tmp_output_info, input->clone()->set_is_resizable(true).set_tensor_shape(output_shape)); - - // Update coordinate on axis - start_coords.set(axis, axis_offset); - end_coords.set(axis, axis_offset + axis_split_step); - - ARM_COMPUTE_RETURN_ON_ERROR(NESlice::validate(input, output, start_coords, end_coords)); - axis_offset += axis_split_step; - } - - return Status{}; -} - void NESplit::run() { for(unsigned i = 0; i < _num_outputs; ++i) diff --git a/tests/validation/NEON/Split.cpp b/tests/validation/NEON/Split.cpp index 91b3b9a047..5ad19a69ac 100644 --- a/tests/validation/NEON/Split.cpp +++ b/tests/validation/NEON/Split.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2019 ARM Limited. + * Copyright (c) 2018-2020 ARM Limited. * * SPDX-License-Identifier: MIT * @@ -65,6 +65,29 @@ DATA_TEST_CASE(Validate, framework::DatasetMode::ALL, zip(zip(zip( const Status status = NESplit::validate(&input_info.clone()->set_is_resizable(false), outputs_info_ptr, axis); ARM_COMPUTE_EXPECT(bool(status) == expected, framework::LogLevel::ERRORS); } + +DATA_TEST_CASE(ValidateSplitShapes, framework::DatasetMode::ALL, zip(zip(zip( + framework::dataset::make("InputInfo", { TensorInfo(TensorShape(27U, 3U, 16U, 2U), 1, DataType::F32), + TensorInfo(TensorShape(27U, 3U, 16U, 2U), 1, DataType::F32) + }), + framework::dataset::make("Axis", { 2, 2 })), + framework::dataset::make("Splits", { std::vector{TensorInfo(TensorShape(27U, 3U, 4U, 2U), 1, DataType::F32), + TensorInfo(TensorShape(27U, 3U, 4U, 2U), 1, DataType::F32), + TensorInfo(TensorShape(27U, 3U, 8U, 2U), 1, DataType::F32)}, + std::vector{TensorInfo(TensorShape(27U, 3U, 3U, 2U), 1, DataType::F32), + TensorInfo(TensorShape(27U, 3U, 13U, 2U), 1, DataType::F32)} })), + framework::dataset::make("Expected", { true, true })), + input_info, axis, splits, expected) +{ + std::vector outputs_info_ptr; + + for(auto &split : splits) + { + outputs_info_ptr.emplace_back(const_cast(&split)); + } + const Status status = NESplit::validate(&input_info.clone()->set_is_resizable(false), outputs_info_ptr, axis); + ARM_COMPUTE_EXPECT(bool(status) == expected, framework::LogLevel::ERRORS); +} // clang-format on // *INDENT-ON* @@ -95,9 +118,45 @@ DATA_TEST_CASE(Configuration, } } +DATA_TEST_CASE(ConfigurationSplitShapes, + framework::DatasetMode::ALL, + combine(datasets::SmallSplitShapesDataset(), framework::dataset::make("DataType", { DataType::F16, DataType::F32 })), + shape, axis, split_shapes, data_type) +{ + // Create tensors + Tensor src = create_tensor(shape, data_type); + std::vector dsts; + + for(const auto &split_shape : split_shapes) + { + Tensor dst = create_tensor(split_shape, data_type); + dsts.push_back(std::move(dst)); + } + + std::vector dsts_ptrs; + for(auto &dst : dsts) + { + dsts_ptrs.emplace_back(&dst); + } + + // Create and Configure function + NESplit split; + split.configure(&src, dsts_ptrs, axis); + + // Validate valid regions + for(auto &dst : dsts) + { + const ValidRegion valid_region = shape_to_valid_region(dst.info()->tensor_shape()); + validate(dst.info()->valid_region(), valid_region); + } +} + template using NESplitFixture = SplitFixture; +template +using NESplitShapesFixture = SplitShapesFixture; + TEST_SUITE(Float) #ifdef __ARM_FEATURE_FP16_VECTOR_ARITHMETIC TEST_SUITE(FP16) @@ -151,6 +210,18 @@ FIXTURE_DATA_TEST_CASE(RunLarge, validate(Accessor(_target[i]), _reference[i]); } } + +FIXTURE_DATA_TEST_CASE(RunSmallSplitShapes, + NESplitShapesFixture, + framework::DatasetMode::PRECOMMIT, + combine(datasets::SmallSplitShapesDataset(), framework::dataset::make("DataType", DataType::F32))) +{ + // Validate outputs + for(unsigned int i = 0; i < _target.size(); ++i) + { + validate(Accessor(_target[i]), _reference[i]); + } +} TEST_SUITE_END() // FP32 TEST_SUITE_END() // Float -- cgit v1.2.1