From 1e0208a66ddea1be2d0e715591598c6704660811 Mon Sep 17 00:00:00 2001 From: Michele Di Giorgio Date: Fri, 22 Jan 2021 15:42:59 +0000 Subject: Make CLArithmeticAddition kernel and function state-less Resolves COMPMID-4006 Change-Id: Iddc32b0b250142aac9a4a7b9dc0eef462d196025 Signed-off-by: Michele Di Giorgio Reviewed-on: https://review.mlplatform.org/c/ml/ComputeLibrary/+/4913 Tested-by: Arm Jenkins Comments-Addressed: Arm Jenkins Reviewed-by: Sang-Hoon Park --- Android.bp | 3 +- .../runtime/CL/functions/CLElementwiseOperations.h | 117 +---- arm_compute/runtime/CL/functions/CLLogicalAnd.h | 6 +- arm_compute/runtime/CL/functions/CLLogicalOr.h | 6 +- arm_compute/runtime/CL/functions/CLPReluLayer.h | 6 +- docs/00_introduction.dox | 2 +- src/core/CL/CLKernels.h | 1 - .../CL/kernels/CLElementwiseOperationKernel.cpp | 516 --------------------- src/core/CL/kernels/CLElementwiseOperationKernel.h | 256 ---------- src/core/KernelTypes.h | 5 +- src/core/gpu/cl/kernels/ClElementwiseKernel.cpp | 505 ++++++++++++++++++++ src/core/gpu/cl/kernels/ClElementwiseKernel.h | 219 +++++++++ .../CL/functions/CLElementwiseOperations.cpp | 64 +-- src/runtime/CL/functions/CLLogicalAnd.cpp | 10 +- src/runtime/CL/functions/CLLogicalOr.cpp | 10 +- src/runtime/CL/functions/CLPReluLayer.cpp | 8 +- src/runtime/NEON/functions/NELogical.cpp | 12 +- src/runtime/gpu/cl/operators/ClAdd.cpp | 47 ++ src/runtime/gpu/cl/operators/ClAdd.h | 100 ++++ tests/validation/reference/Logical.cpp | 20 +- 20 files changed, 961 insertions(+), 952 deletions(-) delete mode 100644 src/core/CL/kernels/CLElementwiseOperationKernel.cpp delete mode 100644 src/core/CL/kernels/CLElementwiseOperationKernel.h create mode 100644 src/core/gpu/cl/kernels/ClElementwiseKernel.cpp create mode 100644 src/core/gpu/cl/kernels/ClElementwiseKernel.h create mode 100644 src/runtime/gpu/cl/operators/ClAdd.cpp create mode 100644 src/runtime/gpu/cl/operators/ClAdd.h diff --git a/Android.bp b/Android.bp index 8a596cd87b..6937ab5ea6 100644 --- a/Android.bp +++ b/Android.bp @@ -110,7 +110,6 @@ cc_library_static { "src/core/CL/kernels/CLDilateKernel.cpp", "src/core/CL/kernels/CLDirectConvolutionLayerKernel.cpp", "src/core/CL/kernels/CLElementWiseUnaryLayerKernel.cpp", - "src/core/CL/kernels/CLElementwiseOperationKernel.cpp", "src/core/CL/kernels/CLErodeKernel.cpp", "src/core/CL/kernels/CLFFTDigitReverseKernel.cpp", "src/core/CL/kernels/CLFFTRadixStageKernel.cpp", @@ -441,6 +440,7 @@ cc_library_static { "src/core/gpu/cl/kernels/ClActivationKernel.cpp", "src/core/gpu/cl/kernels/ClBatchConcatenateKernel.cpp", "src/core/gpu/cl/kernels/ClDepthConcatenateKernel.cpp", + "src/core/gpu/cl/kernels/ClElementwiseKernel.cpp", "src/core/gpu/cl/kernels/ClFloorKernel.cpp", "src/core/gpu/cl/kernels/ClHeightConcatenateKernel.cpp", "src/core/gpu/cl/kernels/ClWidthConcatenate2TensorsKernel.cpp", @@ -795,6 +795,7 @@ cc_library_static { "src/runtime/cpu/operators/CpuReshape.cpp", "src/runtime/cpu/operators/CpuSub.cpp", "src/runtime/gpu/cl/operators/ClActivation.cpp", + "src/runtime/gpu/cl/operators/ClAdd.cpp", "src/runtime/gpu/cl/operators/ClConcatenate.cpp", "src/runtime/gpu/cl/operators/ClFloor.cpp", "utils/CommonGraphOptions.cpp", diff --git a/arm_compute/runtime/CL/functions/CLElementwiseOperations.h b/arm_compute/runtime/CL/functions/CLElementwiseOperations.h index 55c5fb3455..4dd491221e 100644 --- a/arm_compute/runtime/CL/functions/CLElementwiseOperations.h +++ b/arm_compute/runtime/CL/functions/CLElementwiseOperations.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2020 Arm Limited. + * Copyright (c) 2018-2021 Arm Limited. * * SPDX-License-Identifier: MIT * @@ -35,74 +35,7 @@ class ITensorInfo; namespace experimental { -/** Basic function to run @ref CLSaturatedArithmeticOperationKernel for addition - * - * @note The tensor data type for the inputs must be U8/QASYMM8/QASYMM8_SIGNED/S16/QSYMM16/S32/F16/F32. - * @note The function performs an arithmetic addition between two tensors. - */ -class CLArithmeticAddition : public ICLOperator -{ -public: - /** Default Constructor */ - CLArithmeticAddition(); - /** Initialise the kernel's inputs, output and conversion policy. - * - * Valid configurations (Input1,Input2) -> Output : - * - * - (U8,U8) -> U8 - * - (U8,U8) -> S16 - * - (S16,U8) -> S16 - * - (U8,S16) -> S16 - * - (S16,S16) -> S16 - * - (S32,S32) -> S32 - * - (F16,F16) -> F16 - * - (F32,F32) -> F32 - * - (QASYMM8,QASYMM8) -> QASYMM8 - * - (QASYMM8_SIGNED,QASYMM8_SIGNED) -> QASYMM8_SIGNED - * - (QSYMM16,QSYMM16) -> QSYMM16 - * - * @param[in] compile_context The compile context to be used. - * @param[in, out] input1 First tensor input. Data types supported: U8/QASYMM8/QASYMM8_SIGNED/S16/QSYMM16/S32/F16/F32. - * The input tensor is [in, out] because its TensorInfo might be modified inside the kernel in case of broadcasting of dimension 0. - * @param[in, out] input2 Second tensor input. Data types supported: U8/QASYMM8/QASYMM8_SIGNED/S16/QSYMM16/S32/F16/F32. - * The input tensor is [in, out] because its TensorInfo might be modified inside the kernel in case of broadcasting of dimension 0. - * @param[out] output Output tensor. Data types supported: U8/QASYMM8/QASYMM8_SIGNED/S16/QSYMM16/S32/F16/F32. - * @param[in] policy Policy to use to handle overflow. - * @param[in] act_info (Optional) Activation layer information in case of a fused activation. - */ - void configure(const CLCompileContext &compile_context, ITensorInfo *input1, ITensorInfo *input2, ITensorInfo *output, ConvertPolicy policy, - const ActivationLayerInfo &act_info = ActivationLayerInfo()); - /** Static function to check if given info will lead to a valid configuration of @ref CLSaturatedArithmeticOperationKernel for addition - * - * Valid configurations (Input1,Input2) -> Output : - * - * - (U8,U8) -> U8 - * - (U8,U8) -> S16 - * - (S16,U8) -> S16 - * - (U8,S16) -> S16 - * - (S16,S16) -> S16 - * - (S32,S32) -> S32 - * - (F16,F16) -> F16 - * - (F32,F32) -> F32 - * - (QASYMM8,QASYMM8) -> QASYMM8 - * - (QASYMM8_SIGNED,QASYMM8_SIGNED) -> QASYMM8_SIGNED - * - (QSYMM16,QSYMM16) -> QSYMM16 - * - * @param[in] input1 First tensor input info. Data types supported: U8/QASYMM8/QASYMM8_SIGNED/S16/QSYMM16/S32/F16/F32. - * @param[in] input2 Second tensor input info. Data types supported: U8/QASYMM8/QASYMM8_SIGNED/S16/QSYMM16/S32/F16/F32. - * @param[in] output Output tensor info. Data types supported: U8/QASYMM8/QASYMM8_SIGNED/S16/QSYMM16/S32/F16/F32. - * @param[in] policy Policy to use to handle overflow. - * @param[in] act_info (Optional) Activation layer information in case of a fused activation. - * - * @return a status - */ - static Status validate(const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output, ConvertPolicy policy, const ActivationLayerInfo &act_info = ActivationLayerInfo()); - - // Inherited methods overridden: - void run(ITensorPack &tensors) override; -}; - -/** Basic function to run @ref CLSaturatedArithmeticOperationKernel for subtraction +/** Basic function to run @ref arm_compute::opencl::kernels::ClSaturatedArithmeticKernel for subtraction * * @note The tensor data type for the inputs must be U8/QASYMM8/QASYMM8_SIGNED/S16/S32/F16/F32. * @note The function performs an arithmetic subtraction between two tensors. @@ -139,7 +72,7 @@ public: */ void configure(const CLCompileContext &compile_context, ITensorInfo *input1, ITensorInfo *input2, ITensorInfo *output, ConvertPolicy policy, const ActivationLayerInfo &act_info = ActivationLayerInfo()); - /** Static function to check if given info will lead to a valid configuration of @ref CLSaturatedArithmeticOperationKernel for subtraction + /** Static function to check if given info will lead to a valid configuration of @ref arm_compute::opencl::kernels::ClSaturatedArithmeticKernel for subtraction * * Valid configurations (Input1,Input2) -> Output : * @@ -169,7 +102,7 @@ public: void run(ITensorPack &tensors) override; }; -/** Basic function to run @ref CLSaturatedArithmeticOperationKernel for division +/** Basic function to run @ref arm_compute::opencl::kernels::ClSaturatedArithmeticKernel for division * * @note The tensor data type for the inputs must be F16/F32. * @note The function performs an arithmetic division between two tensors. @@ -205,7 +138,7 @@ public: void run(ITensorPack &tensors) override; }; -/** Basic function to run @ref CLArithmeticOperationKernel for max +/** Basic function to run @ref arm_compute::opencl::kernels::ClArithmeticKernel for max * * @note The tensor data type for the inputs must be U8/QASYMM8/S16/QSYMM16/S32/U32/F16/F32. * @note The function performs a max operation between two tensors. @@ -226,7 +159,7 @@ public: * @param[in] act_info (Optional) Activation layer information in case of a fused activation. */ void configure(const CLCompileContext &compile_context, ITensorInfo *input1, ITensorInfo *input2, ITensorInfo *output, const ActivationLayerInfo &act_info = ActivationLayerInfo()); - /** Static function to check if given info will lead to a valid configuration of @ref CLArithmeticOperationKernel for max + /** Static function to check if given info will lead to a valid configuration of @ref arm_compute::opencl::kernels::ClArithmeticKernel for max * * @param[in] input1 First tensor input info. Data types supported: U8/QASYMM8/QASYMM8_SIGNED/S16/QSYMM16/S32/U32/F16/F32. * @param[in] input2 Second tensor input info. Data types supported: same as @p input1. @@ -241,7 +174,7 @@ public: void run(ITensorPack &tensors) override; }; -/** Basic function to run @ref CLArithmeticOperationKernel for min +/** Basic function to run @ref arm_compute::opencl::kernels::ClArithmeticKernel for min * * @note The tensor data type for the inputs must be U8/QASYMM8/S16/QSYMM16/S32/U32/F16/F32. * @note The function performs a max operation between two tensors. @@ -262,7 +195,7 @@ public: * @param[in] act_info (Optional) Activation layer information in case of a fused activation. */ void configure(const CLCompileContext &compile_context, ITensorInfo *input1, ITensorInfo *input2, ITensorInfo *output, const ActivationLayerInfo &act_info = ActivationLayerInfo()); - /** Static function to check if given info will lead to a valid configuration of @ref CLArithmeticOperationKernel for min + /** Static function to check if given info will lead to a valid configuration of @ref arm_compute::opencl::kernels::ClArithmeticKernel for min * * @param[in] input1 First tensor input info. Data types supported: U8/QASYMM8/QASYMM8_SIGNED/S16/QSYMM16/S32/U32/F16/F32. * @param[in] input2 Second tensor input info. Data types supported: same as @p input1. @@ -277,7 +210,7 @@ public: void run(ITensorPack &tensors) override; }; -/** Basic function to run @ref CLArithmeticOperationKernel for squared difference +/** Basic function to run @ref arm_compute::opencl::kernels::ClArithmeticKernel for squared difference * * @note The tensor data type for the inputs must be QASYMM8/U8/S16/QSYMM16/F16/F32. * @note The function performs a squared different operation between two tensors (i.e., out[i] = (in1[i] - in2[i])^2 @@ -298,7 +231,7 @@ public: * @param[in] act_info (Optional) Activation layer information in case of a fused activation. */ void configure(const CLCompileContext &compile_context, ITensorInfo *input1, ITensorInfo *input2, ITensorInfo *output, const ActivationLayerInfo &act_info = ActivationLayerInfo()); - /** Static function to check if given info will lead to a valid configuration of @ref CLArithmeticOperationKernel for squared difference + /** Static function to check if given info will lead to a valid configuration of @ref arm_compute::opencl::kernels::ClArithmeticKernel for squared difference * * @param[in] input1 First tensor input info. Data types supported: U8/QASYMM8/QASYMM8_SIGNED/S16/QSYMM16/F16/F32. * @param[in] input2 Second tensor input info. Data types supported: same as @p input1. @@ -313,7 +246,7 @@ public: void run(ITensorPack &tensors) override; }; -/** Basic function to run @ref CLArithmeticOperationKernel for power +/** Basic function to run @ref arm_compute::opencl::kernels::ClArithmeticKernel for power * * @note The tensor data type for the inputs must be F16/F32. * @note The function performs an elementwise power of in1 to in2 (i.e., out[i] = in1[i] ^ in2[i]) @@ -334,7 +267,7 @@ public: * @param[in] act_info (Optional) Activation layer information in case of a fused activation. */ void configure(const CLCompileContext &compile_context, ITensorInfo *input1, ITensorInfo *input2, ITensorInfo *output, const ActivationLayerInfo &act_info = ActivationLayerInfo()); - /** Static function to check if given info will lead to a valid configuration of @ref CLArithmeticOperationKernel for power + /** Static function to check if given info will lead to a valid configuration of @ref arm_compute::opencl::kernels::ClArithmeticKernel for power * * @param[in] input1 First tensor input info. Data types supported: F16/F32. * @param[in] input2 Second tensor input info. Data types supported: F16/F32. @@ -350,7 +283,7 @@ public: }; } // namespace experimental -/** Basic function to run @ref CLSaturatedArithmeticOperationKernel for addition +/** Basic function to run @ref opencl::kernels::ClSaturatedArithmeticKernel for addition * * @note The tensor data type for the inputs must be U8/QASYMM8/QASYMM8_SIGNED/S16/QSYMM16/S32/F16/F32. * @note The function performs an arithmetic addition between two tensors. @@ -422,7 +355,7 @@ public: */ void configure(const CLCompileContext &compile_context, const ICLTensor *input1, const ICLTensor *input2, ICLTensor *output, ConvertPolicy policy, const ActivationLayerInfo &act_info = ActivationLayerInfo()); - /** Static function to check if given info will lead to a valid configuration of @ref CLSaturatedArithmeticOperationKernel for addition + /** Static function to check if given info will lead to a valid configuration of @ref opencl::kernels::ClSaturatedArithmeticKernel for addition * * Valid configurations (Input1,Input2) -> Output : * @@ -456,7 +389,7 @@ private: std::unique_ptr _impl; }; -/** Basic function to run @ref CLSaturatedArithmeticOperationKernel for subtraction +/** Basic function to run @ref opencl::kernels::ClSaturatedArithmeticKernel for subtraction * * @note The tensor data type for the inputs must be U8/QASYMM8/QASYMM8_SIGNED/S16/S32/F16/F32. * @note The function performs an arithmetic subtraction between two tensors. @@ -528,7 +461,7 @@ public: */ void configure(const CLCompileContext &compile_context, const ICLTensor *input1, const ICLTensor *input2, ICLTensor *output, ConvertPolicy policy, const ActivationLayerInfo &act_info = ActivationLayerInfo()); - /** Static function to check if given info will lead to a valid configuration of @ref CLSaturatedArithmeticOperationKernel for subtraction + /** Static function to check if given info will lead to a valid configuration of @ref opencl::kernels::ClSaturatedArithmeticKernel for subtraction * * Valid configurations (Input1,Input2) -> Output : * @@ -562,7 +495,7 @@ private: std::unique_ptr _impl; }; -/** Basic function to run @ref CLSaturatedArithmeticOperationKernel for division +/** Basic function to run @ref opencl::kernels::ClSaturatedArithmeticKernel for division * * @note The tensor data type for the inputs must be F16/F32. * @note The function performs an arithmetic division between two tensors. @@ -622,7 +555,7 @@ private: std::unique_ptr _impl; }; -/** Basic function to run @ref CLArithmeticOperationKernel for max +/** Basic function to run @ref opencl::kernels::ClArithmeticKernel for max * * @note The tensor data type for the inputs must be U8/QASYMM8/S16/QSYMM16/S32/U32/F16/F32. * @note The function performs a max operation between two tensors. @@ -663,7 +596,7 @@ public: * @param[in] act_info (Optional) Activation layer information in case of a fused activation. */ void configure(const CLCompileContext &compile_context, ICLTensor *input1, ICLTensor *input2, ICLTensor *output, const ActivationLayerInfo &act_info = ActivationLayerInfo()); - /** Static function to check if given info will lead to a valid configuration of @ref CLArithmeticOperationKernel for max + /** Static function to check if given info will lead to a valid configuration of @ref opencl::kernels::ClArithmeticKernel for max * * @param[in] input1 First tensor input info. Data types supported: U8/QASYMM8/QASYMM8_SIGNED/S16/QSYMM16/S32/U32/F16/F32. * @param[in] input2 Second tensor input info. Data types supported: same as @p input1. @@ -682,7 +615,7 @@ private: std::unique_ptr _impl; }; -/** Basic function to run @ref CLArithmeticOperationKernel for min +/** Basic function to run @ref opencl::kernels::ClArithmeticKernel for min * * @note The tensor data type for the inputs must be U8/QASYMM8/S16/QSYMM16/S32/U32/F16/F32. * @note The function performs a max operation between two tensors. @@ -723,7 +656,7 @@ public: * @param[in] act_info (Optional) Activation layer information in case of a fused activation. */ void configure(const CLCompileContext &compile_context, ICLTensor *input1, ICLTensor *input2, ICLTensor *output, const ActivationLayerInfo &act_info = ActivationLayerInfo()); - /** Static function to check if given info will lead to a valid configuration of @ref CLArithmeticOperationKernel for min + /** Static function to check if given info will lead to a valid configuration of @ref opencl::kernels::ClArithmeticKernel for min * * @param[in] input1 First tensor input info. Data types supported: U8/QASYMM8/QASYMM8_SIGNED/S16/QSYMM16/S32/U32/F16/F32. * @param[in] input2 Second tensor input info. Data types supported: same as @p input1. @@ -742,7 +675,7 @@ private: std::unique_ptr _impl; }; -/** Basic function to run @ref CLArithmeticOperationKernel for squared difference +/** Basic function to run @ref opencl::kernels::ClArithmeticKernel for squared difference * * @note The tensor data type for the inputs must be QASYMM8/U8/S16/QSYMM16/F16/F32. * @note The function performs a squared different operation between two tensors (i.e., out[i] = (in1[i] - in2[i])^2 @@ -783,7 +716,7 @@ public: * @param[in] act_info (Optional) Activation layer information in case of a fused activation. */ void configure(const CLCompileContext &compile_context, ICLTensor *input1, ICLTensor *input2, ICLTensor *output, const ActivationLayerInfo &act_info = ActivationLayerInfo()); - /** Static function to check if given info will lead to a valid configuration of @ref CLArithmeticOperationKernel for squared difference + /** Static function to check if given info will lead to a valid configuration of @ref opencl::kernels::ClArithmeticKernel for squared difference * * @param[in] input1 First tensor input info. Data types supported: U8/QASYMM8/QASYMM8_SIGNED/S16/QSYMM16/F16/F32. * @param[in] input2 Second tensor input info. Data types supported: same as @p input1. @@ -802,7 +735,7 @@ private: std::unique_ptr _impl; }; -/** Basic function to run @ref CLArithmeticOperationKernel for power +/** Basic function to run @ref opencl::kernels::ClArithmeticKernel for power * * @note The tensor data type for the inputs must be F16/F32. * @note The function performs an elementwise power of in1 to in2 (i.e., out[i] = in1[i] ^ in2[i]) @@ -843,7 +776,7 @@ public: * @param[in] act_info (Optional) Activation layer information in case of a fused activation. */ void configure(const CLCompileContext &compile_context, ICLTensor *input1, ICLTensor *input2, ICLTensor *output, const ActivationLayerInfo &act_info = ActivationLayerInfo()); - /** Static function to check if given info will lead to a valid configuration of @ref CLArithmeticOperationKernel for power + /** Static function to check if given info will lead to a valid configuration of @ref opencl::kernels::ClArithmeticKernel for power * * @param[in] input1 First tensor input info. Data types supported: F16/F32. * @param[in] input2 Second tensor input info. Data types supported: F16/F32. diff --git a/arm_compute/runtime/CL/functions/CLLogicalAnd.h b/arm_compute/runtime/CL/functions/CLLogicalAnd.h index f5d834c876..f7038ee97a 100644 --- a/arm_compute/runtime/CL/functions/CLLogicalAnd.h +++ b/arm_compute/runtime/CL/functions/CLLogicalAnd.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Arm Limited. + * Copyright (c) 2020-2021 Arm Limited. * * SPDX-License-Identifier: MIT * @@ -51,7 +51,7 @@ public: * @param[out] output Output tensor. Data types supported: same as @p input1. */ void configure(const CLCompileContext &compile_context, ITensorInfo *input1, ITensorInfo *input2, ITensorInfo *output); - /** Static function to check if given info will lead to a valid configuration of @ref CLLogicalBinaryKernel + /** Static function to check if given info will lead to a valid configuration of @ref arm_compute::opencl::kernels::ClLogicalBinaryKernel * * @param[in] input1 First tensor input info. Data types supported: U8. * @param[in] input2 Second tensor input info. Data types supported: same as @p input1. @@ -65,7 +65,7 @@ public: }; } // namespace experimental -/** Basic function to run @ref CLLogicalBinaryKernel. +/** Basic function to run @ref arm_compute::opencl::kernels::ClLogicalBinaryKernel. * * @note The tensor data type for the inputs must be U8. * @note The function performs a logical AND operation using the two input tensors. diff --git a/arm_compute/runtime/CL/functions/CLLogicalOr.h b/arm_compute/runtime/CL/functions/CLLogicalOr.h index daf7d85aef..948baee9d9 100644 --- a/arm_compute/runtime/CL/functions/CLLogicalOr.h +++ b/arm_compute/runtime/CL/functions/CLLogicalOr.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Arm Limited. + * Copyright (c) 2020-2021 Arm Limited. * * SPDX-License-Identifier: MIT * @@ -51,7 +51,7 @@ public: * @param[out] output Output tensor. Data types supported: same as @p input1. */ void configure(const CLCompileContext &compile_context, ITensorInfo *input1, ITensorInfo *input2, ITensorInfo *output); - /** Static function to check if given info will lead to a valid configuration of @ref CLLogicalBinaryKernel + /** Static function to check if given info will lead to a valid configuration of @ref arm_compute::opencl::kernels::ClLogicalBinaryKernel * * @param[in] input1 First tensor input info. Data types supported: U8. * @param[in] input2 Second tensor input info. Data types supported: same as @p input1. @@ -65,7 +65,7 @@ public: }; } // namespace experimental -/** Basic function to run @ref CLLogicalBinaryKernel. +/** Basic function to run @ref arm_compute::opencl::kernels::ClLogicalBinaryKernel. * * @note The tensor data type for the inputs must be U8. * @note The function performs a logical OR operation using the two input tensors. diff --git a/arm_compute/runtime/CL/functions/CLPReluLayer.h b/arm_compute/runtime/CL/functions/CLPReluLayer.h index ab32bccc24..1751fda030 100644 --- a/arm_compute/runtime/CL/functions/CLPReluLayer.h +++ b/arm_compute/runtime/CL/functions/CLPReluLayer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 Arm Limited. + * Copyright (c) 2019-2021 Arm Limited. * * SPDX-License-Identifier: MIT * @@ -35,7 +35,7 @@ class ITensorInfo; namespace experimental { -/** Basic function to run @ref CLArithmeticOperationKernel for PRELU +/** Basic function to run @ref arm_compute::opencl::kernels::ClArithmeticKernel for PRELU * * @note The function implements an activation layer with the PRELU activation function. */ @@ -69,7 +69,7 @@ public: }; } // namespace experimental -/** Basic function to run @ref CLArithmeticOperationKernel for PRELU +/** Basic function to run @ref opencl::kernels::ClArithmeticKernel for PRELU * * @note The function implements an activation layer with the PRELU activation function. */ diff --git a/docs/00_introduction.dox b/docs/00_introduction.dox index 3c89cfddcb..d7ef3dcb7d 100644 --- a/docs/00_introduction.dox +++ b/docs/00_introduction.dox @@ -166,7 +166,7 @@ v20.11 Public major release - @ref NEGEMMLowpMatrixBReductionKernel - Removed padding from OpenCL kernels: - CLBatchConcatenateLayerKernel - - @ref CLElementwiseOperationKernel + - CLElementwiseOperationKernel - @ref CLBatchNormalizationLayerKernel - @ref CLPoolingLayerKernel - @ref CLWinogradInputTransformKernel diff --git a/src/core/CL/CLKernels.h b/src/core/CL/CLKernels.h index 76bc5271b4..916cf456c1 100644 --- a/src/core/CL/CLKernels.h +++ b/src/core/CL/CLKernels.h @@ -57,7 +57,6 @@ #include "src/core/CL/kernels/CLDilateKernel.h" #include "src/core/CL/kernels/CLDirectConvolutionLayerKernel.h" #include "src/core/CL/kernels/CLElementWiseUnaryLayerKernel.h" -#include "src/core/CL/kernels/CLElementwiseOperationKernel.h" #include "src/core/CL/kernels/CLErodeKernel.h" #include "src/core/CL/kernels/CLFFTDigitReverseKernel.h" #include "src/core/CL/kernels/CLFFTRadixStageKernel.h" diff --git a/src/core/CL/kernels/CLElementwiseOperationKernel.cpp b/src/core/CL/kernels/CLElementwiseOperationKernel.cpp deleted file mode 100644 index 7c1d940494..0000000000 --- a/src/core/CL/kernels/CLElementwiseOperationKernel.cpp +++ /dev/null @@ -1,516 +0,0 @@ -/* - * Copyright (c) 2018-2020 Arm Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include "src/core/CL/kernels/CLElementwiseOperationKernel.h" - -#include "arm_compute/core/CL/CLHelpers.h" -#include "arm_compute/core/CL/ICLTensor.h" -#include "src/core/CL/CLValidate.h" -#include "src/core/common/Validate.h" -#include "src/core/helpers/AutoConfiguration.h" -#include "src/core/helpers/WindowHelpers.h" -#include "support/Cast.h" -#include "support/StringSupport.h" -#include - -namespace arm_compute -{ -namespace -{ -constexpr unsigned int vector_size_byte_opencl = 16; - -std::map supported_arithmetic_ops = -{ - { ArithmeticOperation::ADD, "ADD" }, - { ArithmeticOperation::SUB, "SUB" }, - { ArithmeticOperation::DIV, "DIV" }, - { ArithmeticOperation::SQUARED_DIFF, "SQUARED_DIFF" }, - { ArithmeticOperation::MIN, "MIN" }, - { ArithmeticOperation::MAX, "MAX" }, - { ArithmeticOperation::POWER, "POWER" }, - { ArithmeticOperation::PRELU, "PRELU" }, -}; - -std::map supported_sat_arithmetic_ops = -{ - { ArithmeticOperation::ADD, "ADD" }, - { ArithmeticOperation::SUB, "SUB" }, -}; - -std::string generate_id_for_tuning_common(const std::string &kernel_name, const ITensorInfo &input1, const ITensorInfo &output) -{ - std::string config_id; - // Set config_id for enabling LWS tuning - config_id = kernel_name; - config_id += "_"; - config_id += lower_string(string_from_data_type(input1.data_type())); - config_id += "_"; - config_id += support::cpp11::to_string(output.dimension(0)); - config_id += "_"; - config_id += support::cpp11::to_string(output.dimension(1)); - return config_id; -} - -Status validate_arguments_with_float_only_supported_rules(const ITensorInfo &input1, const ITensorInfo &input2, const ITensorInfo &output) -{ - ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(&input1, &input2, &output); - ARM_COMPUTE_RETURN_ERROR_ON_F16_UNSUPPORTED(&input1); - ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(&input1, 1, DataType::F16, DataType::F32); - ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(&input1, &input2); - - const TensorShape out_shape = TensorShape::broadcast_shape(input1.tensor_shape(), input2.tensor_shape()); - - ARM_COMPUTE_RETURN_ERROR_ON_MSG(out_shape.total_size() == 0, "Inputs are not broadcast compatible"); - - // Validate in case of configured output - if(output.total_size() > 0) - { - ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(&output, 1, DataType::F16, DataType::F32); - ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(&input1, &output); - ARM_COMPUTE_RETURN_ERROR_ON_MSG(detail::have_different_dimensions(out_shape, output.tensor_shape(), 0), - "Wrong shape for output"); - } - - return Status{}; -} - -Status validate_arguments_with_arithmetic_rules(const ITensorInfo &input1, const ITensorInfo &input2, const ITensorInfo &output) -{ - ARM_COMPUTE_RETURN_ERROR_ON_F16_UNSUPPORTED(&input1); - ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(&input1, 1, DataType::U8, DataType::QASYMM8, DataType::QASYMM8_SIGNED, - DataType::S16, DataType::QSYMM16, DataType::F16, - DataType::S32, DataType::F32); - ARM_COMPUTE_RETURN_ERROR_ON_F16_UNSUPPORTED(&input2); - ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(&input2, 1, DataType::U8, DataType::QASYMM8, DataType::QASYMM8_SIGNED, - DataType::S16, DataType::QSYMM16, DataType::F16, - DataType::S32, DataType::F32); - - const bool is_quantized = is_data_type_quantized(input1.data_type()) || is_data_type_quantized(input2.data_type()); - if(is_quantized) - { - ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(&input1, &input2); - - if(is_data_type_quantized_symmetric(input1.data_type())) - { - const int32_t in1_offset = input1.quantization_info().uniform().offset; - const int32_t in2_offset = input2.quantization_info().uniform().offset; - ARM_COMPUTE_RETURN_ERROR_ON_MSG(in1_offset != 0, "For quantized symmetric, offset must be zero"); - ARM_COMPUTE_RETURN_ERROR_ON_MSG(in2_offset != 0, "For quantized symmetric, offset must be zero"); - } - } - - const TensorShape out_shape = TensorShape::broadcast_shape(input1.tensor_shape(), input2.tensor_shape()); - - ARM_COMPUTE_RETURN_ERROR_ON_MSG(out_shape.total_size() == 0, "Inputs are not broadcast compatible"); - - // Validate in case of configured output - if(output.total_size() > 0) - { - ARM_COMPUTE_RETURN_ERROR_ON_F16_UNSUPPORTED(&output); - ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(&output, 1, DataType::U8, DataType::QASYMM8, DataType::QASYMM8_SIGNED, - DataType::S16, DataType::QSYMM16, DataType::F16, - DataType::S32, DataType::F32); - ARM_COMPUTE_RETURN_ERROR_ON_MSG((output.data_type() == DataType::U8) && ((input1.data_type() != DataType::U8) || (input2.data_type() != DataType::U8)), - "Output can only be U8 if both inputs are U8"); - ARM_COMPUTE_RETURN_ERROR_ON_MSG(detail::have_different_dimensions(out_shape, output.tensor_shape(), 0), - "Wrong shape for output"); - - if(is_quantized) - { - ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(&input1, &output); - - if(is_data_type_quantized_symmetric(output.data_type())) - { - const int32_t offset = output.quantization_info().uniform().offset; - ARM_COMPUTE_RETURN_ERROR_ON_MSG(offset != 0, "For quantized symmetric, offset must be zero"); - } - } - } - return Status{}; -} - -CLBuildOptions generate_build_options_with_arithmetic_rules(const ITensorInfo &input1, const ITensorInfo &input2, const ITensorInfo &output, const std::string &operation_string) -{ - CLBuildOptions build_opts; - - const unsigned int num_elems_processed_per_iteration = adjust_vec_size(vector_size_byte_opencl / output.element_size(), output.dimension(0)); - - build_opts.add_option("-DDATA_TYPE_IN1=" + get_cl_type_from_data_type(input1.data_type())); - build_opts.add_option("-DDATA_TYPE_IN2=" + get_cl_type_from_data_type(input2.data_type())); - build_opts.add_option("-DDATA_TYPE_OUT=" + get_cl_type_from_data_type(output.data_type())); - build_opts.add_option("-DVEC_SIZE_IN1=" + support::cpp11::to_string(input1.dimension(0) == 1 ? 1 : num_elems_processed_per_iteration)); - build_opts.add_option("-DVEC_SIZE_IN2=" + support::cpp11::to_string(input2.dimension(0) == 1 ? 1 : num_elems_processed_per_iteration)); - build_opts.add_option("-DVEC_SIZE_OUT=" + support::cpp11::to_string(num_elems_processed_per_iteration)); - build_opts.add_option("-DVEC_SIZE_LEFTOVER=" + support::cpp11::to_string(output.dimension(0) % num_elems_processed_per_iteration)); - build_opts.add_option("-DOP=" + operation_string); - if(is_data_type_quantized(input1.data_type())) - { - const UniformQuantizationInfo iq1info = input1.quantization_info().uniform(); - const UniformQuantizationInfo iq2info = input2.quantization_info().uniform(); - const UniformQuantizationInfo oqinfo = output.quantization_info().uniform(); - - build_opts.add_option("-DOFFSET_IN1=" + support::cpp11::to_string(iq1info.offset)); - build_opts.add_option("-DOFFSET_IN2=" + support::cpp11::to_string(iq2info.offset)); - build_opts.add_option("-DOFFSET_OUT=" + support::cpp11::to_string(oqinfo.offset)); - build_opts.add_option("-DSCALE_IN1=" + float_to_string_with_full_precision(iq1info.scale)); - build_opts.add_option("-DSCALE_IN2=" + float_to_string_with_full_precision(iq2info.scale)); - build_opts.add_option("-DSCALE_OUT=" + float_to_string_with_full_precision(oqinfo.scale)); - } - return build_opts; -} - -std::pair configure_window_arithmetic_common(ITensorInfo &output) -{ - const unsigned int num_elems_processed_per_iteration = adjust_vec_size(vector_size_byte_opencl / output.element_size(), output.dimension(0)); - Window win = calculate_max_window(output, Steps(num_elems_processed_per_iteration)); - return std::make_pair(Status{}, win); -} - -std::pair validate_and_configure_window_for_arithmetic_operators(ITensorInfo &input1, ITensorInfo &input2, ITensorInfo &output) -{ - const std::pair broadcast_pair = ITensorInfo::broadcast_shape_and_valid_region(input1, input2); - const TensorShape &out_shape = broadcast_pair.first; - - set_shape_if_empty(output, out_shape); - - if(input1.data_type() == DataType::S16 || input2.data_type() == DataType::S16) - { - set_format_if_unknown(output, Format::S16); - } - else if(input1.data_type() == DataType::F16 || input2.data_type() == DataType::F16) - { - set_format_if_unknown(output, Format::F16); - } - else if(input1.data_type() == DataType::F32 || input2.data_type() == DataType::F32) - { - set_format_if_unknown(output, Format::F32); - } - else if(input1.data_type() == DataType::QASYMM8 || input2.data_type() == DataType::QASYMM8) - { - set_data_type_if_unknown(output, DataType::QASYMM8); - } - else if(input1.data_type() == DataType::QASYMM8_SIGNED || input2.data_type() == DataType::QASYMM8_SIGNED) - { - set_data_type_if_unknown(output, DataType::QASYMM8_SIGNED); - } - else if(input1.data_type() == DataType::QSYMM16 || input2.data_type() == DataType::QSYMM16) - { - set_data_type_if_unknown(output, DataType::QSYMM16); - } - - return configure_window_arithmetic_common(output); -} - -std::pair validate_and_configure_window_for_logical_binary_operators(ITensorInfo &input1, ITensorInfo &input2, ITensorInfo &output) -{ - const std::pair broadcast_pair = ITensorInfo::broadcast_shape_and_valid_region(input1, input2); - const TensorShape &out_shape = broadcast_pair.first; - - set_shape_if_empty(output, out_shape); - set_data_type_if_unknown(output, DataType::U8); - - // The arithmetic utility functions can be share - return configure_window_arithmetic_common(output); -} - -std::pair validate_and_configure_window_for_division(ITensorInfo &input1, ITensorInfo &input2, ITensorInfo &output) -{ - const std::pair broadcast_pair = ITensorInfo::broadcast_shape_and_valid_region(input1, input2); - const TensorShape &out_shape = broadcast_pair.first; - auto_init_if_empty(output, out_shape, 1, input1.data_type()); - return configure_window_arithmetic_common(output); -} -} // namespace - -CLElementwiseOperationKernel::CLElementwiseOperationKernel() - : _act_info(), _input1(nullptr), _input2(nullptr), _output(nullptr) -{ -} - -void CLElementwiseOperationKernel::configure_common(ITensorInfo *input1, ITensorInfo *input2, ITensorInfo *output) -{ - configure_common(CLKernelLibrary::get().get_compile_context(), input1, input2, output); -} - -void CLElementwiseOperationKernel::configure_common(const CLCompileContext &compile_context, ITensorInfo *input1, ITensorInfo *input2, ITensorInfo *output) -{ - // Configure kernel window - auto win_config = validate_and_configure_window(*input1, *input2, *output); - ARM_COMPUTE_ERROR_THROW_ON(win_config.first); - - _input1 = input1; - _input2 = input2; - _output = output; - - std::string kernel_name = "elementwise_operation_" + name(); - if(is_data_type_quantized(input1->data_type())) - { - kernel_name += "_quantized"; - } - - // Set kernel build options - CLBuildOptions build_opts = generate_build_options(*input1, *input2, *output); - if(_act_info.enabled()) - { - build_opts.add_option("-DACTIVATION_TYPE=" + lower_string(string_from_activation_func(_act_info.activation()))); - build_opts.add_option("-DA_VAL=" + float_to_string_with_full_precision(_act_info.a())); - build_opts.add_option("-DB_VAL=" + float_to_string_with_full_precision(_act_info.b())); - } - - // Create kernel - _kernel = create_kernel(compile_context, kernel_name, build_opts.options()); - - ICLKernel::configure_internal(win_config.second); - - _config_id = generate_id_for_tuning(kernel_name, *input1, *output); -} - -void CLElementwiseOperationKernel::run_op(ITensorPack &tensors, const Window &window, cl::CommandQueue &queue) -{ - ARM_COMPUTE_ERROR_ON_UNCONFIGURED_KERNEL(this); - ARM_COMPUTE_ERROR_ON_INVALID_SUBWINDOW(ICLKernel::window(), window); - - const auto src_0 = utils::cast::polymorphic_downcast(tensors.get_const_tensor(TensorType::ACL_SRC_0)); - const auto src_1 = utils::cast::polymorphic_downcast(tensors.get_const_tensor(TensorType::ACL_SRC_1)); - auto dst = utils::cast::polymorphic_downcast(tensors.get_tensor(TensorType::ACL_DST)); - - const TensorShape &in_shape1 = src_0->info()->tensor_shape(); - const TensorShape &in_shape2 = src_1->info()->tensor_shape(); - const TensorShape &out_shape = dst->info()->tensor_shape(); - - bool can_collapse = true; - const bool is_vector = in_shape1.num_dimensions() == 1 || in_shape2.num_dimensions() == 1; - if(std::min(in_shape1.total_size(), in_shape2.total_size()) > 1 && !is_vector) - { - can_collapse = (std::min(in_shape1.num_dimensions(), in_shape2.num_dimensions()) > Window::DimZ); - for(size_t d = Window::DimZ; can_collapse && (d < out_shape.num_dimensions()); d++) - { - can_collapse = (in_shape1[d] == in_shape2[d]); - } - } - - bool has_collapsed = false; - Window collapsed = can_collapse ? window.collapse_if_possible(ICLKernel::window(), Window::DimZ, &has_collapsed) : window; - - const TensorShape &in_shape1_collapsed = has_collapsed ? in_shape1.collapsed_from(Window::DimZ) : in_shape1; - const TensorShape &in_shape2_collapsed = has_collapsed ? in_shape2.collapsed_from(Window::DimZ) : in_shape2; - - Window slice = collapsed.first_slice_window_3D(); - Window slice_input1 = slice.broadcast_if_dimension_le_one(in_shape1_collapsed); - Window slice_input2 = slice.broadcast_if_dimension_le_one(in_shape2_collapsed); - do - { - unsigned int idx = 0; - add_3D_tensor_argument(idx, src_0, slice_input1); - add_3D_tensor_argument(idx, src_1, slice_input2); - add_3D_tensor_argument(idx, dst, slice); - - enqueue(queue, *this, slice, lws_hint()); - ARM_COMPUTE_UNUSED(collapsed.slide_window_slice_3D(slice_input1)); - ARM_COMPUTE_UNUSED(collapsed.slide_window_slice_3D(slice_input2)); - } - while(collapsed.slide_window_slice_3D(slice)); -} - -/** Logical binary */ - -void CLLogicalBinaryKernel::configure(const CLCompileContext &compile_context, kernels::LogicalOperation op, ITensorInfo *input1, ITensorInfo *input2, ITensorInfo *output) -{ - ARM_COMPUTE_ERROR_ON_NULLPTR(input1, input2, output); - ARM_COMPUTE_ERROR_THROW_ON(CLLogicalBinaryKernel::validate(op, input1, input2, output)); - _op = op; - configure_common(compile_context, input1, input2, output); -} - -Status CLLogicalBinaryKernel::validate(kernels::LogicalOperation op, const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output) -{ - ARM_COMPUTE_UNUSED(op); - ARM_COMPUTE_ASSERT(op != kernels::LogicalOperation::Unknown && op != kernels::LogicalOperation::Not); - ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(input1, input2, output); - - ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(input1, 1, DataType::U8); - ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(input1, input2); - - ARM_COMPUTE_RETURN_ON_ERROR(validate_arguments_with_arithmetic_rules(*input1, *input2, *output)); - ARM_COMPUTE_RETURN_ON_ERROR(validate_and_configure_window_for_logical_binary_operators(*input1->clone(), *input2->clone(), *output->clone()).first); - - return Status{}; -} - -std::string CLLogicalBinaryKernel::name() -{ - switch(_op) - { - case kernels::LogicalOperation::And: - return "AND"; - case kernels::LogicalOperation::Or: - return "OR"; - case kernels::LogicalOperation::Not: - /* fall through */ - default: - ARM_COMPUTE_ASSERT(true); - } - return ""; -} - -std::pair CLLogicalBinaryKernel::validate_and_configure_window(ITensorInfo &input1, ITensorInfo &input2, ITensorInfo &output) -{ - return validate_and_configure_window_for_logical_binary_operators(input1, input2, output); -} - -CLBuildOptions CLLogicalBinaryKernel::generate_build_options(const ITensorInfo &input1, const ITensorInfo &input2, const ITensorInfo &output) -{ - // The arithmetic utility functions can be share - return generate_build_options_with_arithmetic_rules(input1, input2, output, name()); -} - -std::string CLLogicalBinaryKernel::generate_id_for_tuning(const std::string &kernel_name, const ITensorInfo &input1, const ITensorInfo &output) -{ - return generate_id_for_tuning_common(kernel_name, input1, output); -} - -/** Arithmetic operations with saturation*/ - -void CLSaturatedArithmeticOperationKernel::configure(ArithmeticOperation op, ITensorInfo *input1, ITensorInfo *input2, ITensorInfo *output, const ConvertPolicy &policy, - const ActivationLayerInfo &act_info) -{ - configure(CLKernelLibrary::get().get_compile_context(), op, input1, input2, output, policy, act_info); -} - -void CLSaturatedArithmeticOperationKernel::configure(const CLCompileContext &compile_context, ArithmeticOperation op, ITensorInfo *input1, ITensorInfo *input2, ITensorInfo *output, - const ConvertPolicy &policy, - const ActivationLayerInfo &act_info) -{ - ARM_COMPUTE_ERROR_ON_NULLPTR(input1, input2, output); - ARM_COMPUTE_ERROR_THROW_ON(CLSaturatedArithmeticOperationKernel::validate(op, input1, input2, output, policy, act_info)); - auto padding_info = get_padding_info({ input1, input2, output }); - - _policy = policy; - _op = op; - _act_info = act_info; - configure_common(compile_context, input1, input2, output); - ARM_COMPUTE_ERROR_ON(has_padding_changed(padding_info)); -} - -Status CLSaturatedArithmeticOperationKernel::validate(ArithmeticOperation op, const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output, const ConvertPolicy &policy, - const ActivationLayerInfo &act_info) -{ - ARM_COMPUTE_UNUSED(op, policy); - ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(input1, input2, output); - ARM_COMPUTE_RETURN_ON_ERROR(validate_arguments_with_arithmetic_rules(*input1, *input2, *output)); - ARM_COMPUTE_RETURN_ON_ERROR(validate_and_configure_window_for_arithmetic_operators(*input1->clone(), *input2->clone(), *output->clone()).first); - ARM_COMPUTE_RETURN_ERROR_ON(act_info.enabled() && !is_data_type_float(output->data_type())); - - return Status{}; -} - -std::pair CLSaturatedArithmeticOperationKernel::validate_and_configure_window(ITensorInfo &input1, ITensorInfo &input2, ITensorInfo &output) -{ - return validate_and_configure_window_for_arithmetic_operators(input1, input2, output); -} - -CLBuildOptions CLSaturatedArithmeticOperationKernel::generate_build_options(const ITensorInfo &input1, const ITensorInfo &input2, const ITensorInfo &output) -{ - const bool has_float_out = is_data_type_float(output.data_type()); - auto build_options = generate_build_options_with_arithmetic_rules(input1, input2, output, name()); - build_options.add_option((_policy == ConvertPolicy::WRAP || has_float_out) ? "-DWRAP" : "-DSATURATE"); - return build_options; -} -std::string CLSaturatedArithmeticOperationKernel::generate_id_for_tuning(const std::string &kernel_name, const ITensorInfo &input1, const ITensorInfo &output) -{ - auto config_id = generate_id_for_tuning_common(kernel_name, input1, output); - config_id += (_policy == ConvertPolicy::WRAP) ? "_wrap_" : "_saturate_"; - config_id += lower_string(string_from_data_layout(input1.data_layout())); - return config_id; -} - -std::string CLSaturatedArithmeticOperationKernel::name() -{ - return supported_sat_arithmetic_ops[_op]; -} - -/** Arithmetic operations*/ - -void CLArithmeticOperationKernel::configure(ArithmeticOperation op, ITensorInfo *input1, ITensorInfo *input2, ITensorInfo *output, const ActivationLayerInfo &act_info) -{ - configure(CLKernelLibrary::get().get_compile_context(), op, input1, input2, output, act_info); -} - -void CLArithmeticOperationKernel::configure(const CLCompileContext &compile_context, ArithmeticOperation op, ITensorInfo *input1, ITensorInfo *input2, ITensorInfo *output, - const ActivationLayerInfo &act_info) -{ - ARM_COMPUTE_ERROR_ON_NULLPTR(input1, input2, output); - ARM_COMPUTE_ERROR_THROW_ON(CLArithmeticOperationKernel::validate(op, input1, input2, output, act_info)); - auto padding_info = get_padding_info({ input1, input2, output }); - - _op = op; - _act_info = act_info; - configure_common(compile_context, input1, input2, output); - ARM_COMPUTE_ERROR_ON(has_padding_changed(padding_info)); -} - -Status CLArithmeticOperationKernel::validate(ArithmeticOperation op, const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output, const ActivationLayerInfo &act_info) -{ - ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(input1, input2, output); - if(op == ArithmeticOperation::DIV || op == ArithmeticOperation::POWER) - { - // Division and Power operators don't support integer arithmetic - ARM_COMPUTE_RETURN_ON_ERROR(validate_arguments_with_float_only_supported_rules(*input1, *input2, *output)); - ARM_COMPUTE_RETURN_ON_ERROR(validate_and_configure_window_for_division(*input1->clone(), *input2->clone(), *output->clone()).first); - } - else - { - ARM_COMPUTE_RETURN_ON_ERROR(validate_arguments_with_arithmetic_rules(*input1, *input2, *output)); - ARM_COMPUTE_RETURN_ON_ERROR(validate_and_configure_window_for_arithmetic_operators(*input1->clone(), *input2->clone(), *output->clone()).first); - } - ARM_COMPUTE_RETURN_ERROR_ON(act_info.enabled() && !is_data_type_float(output->data_type())); - - return Status{}; -} -std::pair CLArithmeticOperationKernel::validate_and_configure_window(ITensorInfo &input1, ITensorInfo &input2, ITensorInfo &output) -{ - if(_op == ArithmeticOperation::DIV || _op == ArithmeticOperation::POWER) - { - // Division and Power operators don't support integer arithmetic - return validate_and_configure_window_for_division(input1, input2, output); - } - else - { - return validate_and_configure_window_for_arithmetic_operators(input1, input2, output); - } -} - -CLBuildOptions CLArithmeticOperationKernel::generate_build_options(const ITensorInfo &input1, const ITensorInfo &input2, const ITensorInfo &output) -{ - return generate_build_options_with_arithmetic_rules(input1, input2, output, name()); -} -std::string CLArithmeticOperationKernel::generate_id_for_tuning(const std::string &kernel_name, const ITensorInfo &input1, const ITensorInfo &output) -{ - return generate_id_for_tuning_common(kernel_name, input1, output); -} - -std::string CLArithmeticOperationKernel::name() -{ - return supported_arithmetic_ops[_op]; -} -} // namespace arm_compute diff --git a/src/core/CL/kernels/CLElementwiseOperationKernel.h b/src/core/CL/kernels/CLElementwiseOperationKernel.h deleted file mode 100644 index dd04a03ead..0000000000 --- a/src/core/CL/kernels/CLElementwiseOperationKernel.h +++ /dev/null @@ -1,256 +0,0 @@ -/* - * Copyright (c) 2018-2020 Arm Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef ARM_COMPUTE_CLELEMENTWISEOPERATIONKERNEL_H -#define ARM_COMPUTE_CLELEMENTWISEOPERATIONKERNEL_H - -#include "arm_compute/core/Types.h" -#include "src/core/CL/ICLKernel.h" -#include "src/core/KernelTypes.h" - -namespace arm_compute -{ -class ICLTensor; - -/** Interface for an element-wise operation kernel - * - * Element-wise operation is computed by: - * @f[ output(x,y) = OP(input1(x,y), input2(x,y))@f] - * - */ -class CLElementwiseOperationKernel : public ICLKernel -{ -public: - /** Default constructor */ - CLElementwiseOperationKernel(); - /** Prevent instances of this class from being copied (As this class contains pointers) */ - CLElementwiseOperationKernel(const CLElementwiseOperationKernel &) = delete; - /** Prevent instances of this class from being copied (As this class contains pointers) */ - CLElementwiseOperationKernel &operator=(const CLElementwiseOperationKernel &) = delete; - /** Allow instances of this class to be moved */ - CLElementwiseOperationKernel(CLElementwiseOperationKernel &&) = default; - /** Allow instances of this class to be moved */ - CLElementwiseOperationKernel &operator=(CLElementwiseOperationKernel &&) = default; - /** Default destructor */ - ~CLElementwiseOperationKernel() = default; - - // Inherited methods overridden: - void run_op(ITensorPack &tensors, const Window &window, cl::CommandQueue &queue) override; - -protected: - /** The name of the operation */ - virtual std::string name() = 0; - - /** Initialise the kernel's output. - * - * @param[in] input1 First tensor input info. Data types supported: U8/S8/QASYMM8/QASYMM8_SIGNED/U16/S16/F16/U32/S32/F32. - * @param[in] input2 Second tensor input info. Data types supported: Same as @p input1. - * @param[in] output Output tensor info. Data types supported: Same as @p input1. - * - * @return a pair of Status and Window - */ - virtual std::pair validate_and_configure_window(ITensorInfo &input1, ITensorInfo &input2, ITensorInfo &output) = 0; - - /** Generate the build options for the specific kernel - * - * @reutrn a CLBuildOptions struct - */ - virtual CLBuildOptions generate_build_options(const ITensorInfo &input1, const ITensorInfo &input2, const ITensorInfo &output) = 0; - - /** Generate the identifier for tuning - * - * @reutrn a string - */ - virtual std::string generate_id_for_tuning(const std::string &kernel_name, const ITensorInfo &input1, const ITensorInfo &output) = 0; - - /** Commmon configure function for element-wise operators with no additional options (e.g., Div, Min, Max, SquaredDiff) - * - */ - void configure_common(ITensorInfo *input1, ITensorInfo *input2, ITensorInfo *output); - /** Commmon configure function for element-wise operators with no additional options (e.g., Div, Min, Max, SquaredDiff) - * - */ - void configure_common(const CLCompileContext &compile_context, ITensorInfo *input1, ITensorInfo *input2, ITensorInfo *output); - - ActivationLayerInfo _act_info; - -private: - const ITensorInfo *_input1; /**< Source tensor info 1 */ - const ITensorInfo *_input2; /**< Source tensor info 2 */ - ITensorInfo *_output; /**< Destination tensor info */ -}; - -class CLLogicalBinaryKernel : public CLElementwiseOperationKernel -{ -public: - /** Default constructor */ - CLLogicalBinaryKernel() = default; - /** Prevent instances of this class from being copied (As this class contains pointers) */ - CLLogicalBinaryKernel(const CLLogicalBinaryKernel &) = delete; - /** Prevent instances of this class from being copied (As this class contains pointers) */ - CLLogicalBinaryKernel &operator=(const CLLogicalBinaryKernel &) = delete; - /** Allow instances of this class to be moved */ - CLLogicalBinaryKernel(CLLogicalBinaryKernel &&) = default; - /** Allow instances of this class to be moved */ - CLLogicalBinaryKernel &operator=(CLLogicalBinaryKernel &&) = default; - /** Default destructor */ - ~CLLogicalBinaryKernel() = default; - /** Function to configure kernel - * - * @param[in] compile_context The compile context to be used. - * @param[in] op Logical binary operation to be executed. - * @param[in] input1 First tensor input info. Data types supported: U8. - * @param[in] input2 Second tensor input info. Data types supported: same as @p input1. - * @param[in] output Output tensor info. Data types supported: same as @p input1. - */ - void configure(const CLCompileContext &compile_context, kernels::LogicalOperation op, ITensorInfo *input1, ITensorInfo *input2, ITensorInfo *output); - /** Static function to check if the given configuration is valid for this kernel - * - * @param[in] op Logical binary operation to be executed. - * @param[in] input1 First tensor input info. Data types supported: U8. - * @param[in] input2 Second tensor input info. Data types supported: same as @p input1. - * @param[in] output Output tensor info. Data types supported: same as @p input1. - */ - static Status validate(kernels::LogicalOperation op, const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output); - -private: - // Inherited methods overridden: - std::string name() override; - std::pair validate_and_configure_window(ITensorInfo &input1, ITensorInfo &input2, ITensorInfo &output) override; - CLBuildOptions generate_build_options(const ITensorInfo &input1, const ITensorInfo &input2, const ITensorInfo &output) override; - std::string generate_id_for_tuning(const std::string &kernel_name, const ITensorInfo &input1, const ITensorInfo &output) override; - - kernels::LogicalOperation _op{ kernels::LogicalOperation::Unknown }; -}; - -/** Addition operation */ -class CLSaturatedArithmeticOperationKernel : public CLElementwiseOperationKernel -{ -public: - CLSaturatedArithmeticOperationKernel() - : CLElementwiseOperationKernel(), _policy(), _op() - { - } - - /** Static function to check if given info will lead to a valid configuration of @ref CLSaturatedArithmeticOperationKernel - * - * @param[in] op Arithmetic operation to be executed. - * @param[in] input1 First tensor input info. Data types supported: U8/QASYMM8/QASYMM8_SIGNED/S16/QSYMM16/F16/S32/F32. - * @param[in] input2 Second tensor input info. Data types supported: Same as @p input1. - * @param[in] output Output tensor info. Data types supported: Same as @p input1. - * @param[in] policy Policy to use to handle overflow. - * @param[in] act_info (Optional) Activation layer information in case of a fused activation. - */ - void configure(ArithmeticOperation op, ITensorInfo *input1, ITensorInfo *input2, ITensorInfo *output, const ConvertPolicy &policy, const ActivationLayerInfo &act_info = ActivationLayerInfo()); - /** Static function to check if given info will lead to a valid configuration of @ref CLSaturatedArithmeticOperationKernel - * - * @param[in] compile_context The compile context to be used. - * @param[in] op Arithmetic operation to be executed. - * @param[in] input1 First tensor input info. Data types supported: U8/QASYMM8/QASYMM8_SIGNED/S16/QSYMM16/F16/S32/F32. - * @param[in] input2 Second tensor input info. Data types supported: Same as @p input1. - * @param[in] output Output tensor info. Data types supported: Same as @p input1. - * @param[in] policy Policy to use to handle overflow. - * @param[in] act_info (Optional) Activation layer information in case of a fused activation. - */ - void configure(const CLCompileContext &compile_context, ArithmeticOperation op, ITensorInfo *input1, ITensorInfo *input2, ITensorInfo *output, const ConvertPolicy &policy, - const ActivationLayerInfo &act_info = ActivationLayerInfo()); - - /** Static function to check if given info will lead to a valid configuration of @ref CLSaturatedArithmeticOperationKernel - * - * @param[in] op Arithmetic operation to be executed. - * @param[in] input1 First tensor input info info. Data types supported: U8/QASYMM8/QASYMM8_SIGNED/S16/QSYMM16/F16/S32/F32. - * @param[in] input2 Second tensor input info info. Data types supported: Same as @p input1. - * @param[in] output Output tensor info info. Data types supported: Same as @p input1. - * @param[in] policy Policy to use to handle overflow. - * @param[in] act_info (Optional) Activation layer information in case of a fused activation. - * - * @return a Status - */ - static Status validate(ArithmeticOperation op, const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output, const ConvertPolicy &policy, - const ActivationLayerInfo &act_info = ActivationLayerInfo()); - -protected: - // Inherited methods overridden: - std::string name() override; - std::pair validate_and_configure_window(ITensorInfo &input1, ITensorInfo &input2, ITensorInfo &output) override; - CLBuildOptions generate_build_options(const ITensorInfo &input1, const ITensorInfo &input2, const ITensorInfo &output) override; - std::string generate_id_for_tuning(const std::string &kernel_name, const ITensorInfo &input1, const ITensorInfo &output) override; - -private: - ConvertPolicy _policy; - ArithmeticOperation _op; -}; - -class CLArithmeticOperationKernel : public CLElementwiseOperationKernel -{ -public: - CLArithmeticOperationKernel() - : CLElementwiseOperationKernel(), _op() - { - } - - /** Static function to check if given info will lead to a valid configuration of @ref CLArithmeticOperationKernel - * - * @param[in] op Arithmetic operation to be executed. - * @param[in] input1 First tensor input info. Data types supported: U8/QASYMM8/QASYMM8_SIGNED/S16/QSYMM16/F16/S32/F32. - * @param[in] input2 Second tensor input info. Data types supported: Same as @p input1. - * @param[in] output Output tensor info. Data types supported: Same as @p input1. - * @param[in] act_info (Optional) Activation layer information in case of a fused activation. - */ - void configure(ArithmeticOperation op, ITensorInfo *input1, ITensorInfo *input2, ITensorInfo *output, const ActivationLayerInfo &act_info = ActivationLayerInfo()); - /** Static function to check if given info will lead to a valid configuration of @ref CLArithmeticOperationKernel - * - * @param[in] compile_context The compile context to be used. - * @param[in] op Arithmetic operation to be executed. - * @param[in] input1 First tensor input info. Data types supported: U8/QASYMM8/QASYMM8_SIGNED/S16/QSYMM16/F16/S32/F32. - * @param[in] input2 Second tensor input info. Data types supported: Same as @p input1. - * @param[in] output Output tensor info. Data types supported: Same as @p input1. - * @param[in] act_info (Optional) Activation layer information in case of a fused activation. - */ - void configure(const CLCompileContext &compile_context, ArithmeticOperation op, ITensorInfo *input1, ITensorInfo *input2, ITensorInfo *output, - const ActivationLayerInfo &act_info = ActivationLayerInfo()); - - /** Static function to check if given info will lead to a valid configuration of @ref CLArithmeticOperationKernel - * - * @param[in] op Arithmetic operation to be executed. - * @param[in] input1 First tensor input info. Data types supported: U8/QASYMM8/QASYMM8_SIGNED/S16/QSYMM16/F16/S32/F32. - * @param[in] input2 Second tensor input info. Data types supported: Same as @p input1. - * @param[in] output Output tensor info. Data types supported: Same as @p input1. - * @param[in] act_info (Optional) Activation layer information in case of a fused activation. - * - * @return a Status - */ - static Status validate(ArithmeticOperation op, const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output, const ActivationLayerInfo &act_info = ActivationLayerInfo()); - -protected: - // Inherited methods overridden: - std::string name() override; - std::pair validate_and_configure_window(ITensorInfo &input1, ITensorInfo &input2, ITensorInfo &output) override; - CLBuildOptions generate_build_options(const ITensorInfo &input1, const ITensorInfo &input2, const ITensorInfo &output) override; - std::string generate_id_for_tuning(const std::string &kernel_name, const ITensorInfo &input1, const ITensorInfo &output) override; - -private: - ArithmeticOperation _op; -}; -} // namespace arm_compute -#endif /* ARM_COMPUTE_CLELEMENTWISEOPERATIONKERNEL_H */ diff --git a/src/core/KernelTypes.h b/src/core/KernelTypes.h index 12e6bc90ae..a32f5db93c 100644 --- a/src/core/KernelTypes.h +++ b/src/core/KernelTypes.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Arm Limited. + * Copyright (c) 2020-2021 Arm Limited. * * SPDX-License-Identifier: MIT * @@ -26,8 +26,6 @@ namespace arm_compute { -namespace kernels -{ /** List of supported logical operations */ enum class LogicalOperation { @@ -36,6 +34,5 @@ enum class LogicalOperation Or, /**< Logical Or || */ Not, /**< Logical Not ! */ }; -} // namespace kernels } // namespace arm_compute #endif /* ARM_COMPUTE_KERNEL_TYPES_H */ diff --git a/src/core/gpu/cl/kernels/ClElementwiseKernel.cpp b/src/core/gpu/cl/kernels/ClElementwiseKernel.cpp new file mode 100644 index 0000000000..7d204b1348 --- /dev/null +++ b/src/core/gpu/cl/kernels/ClElementwiseKernel.cpp @@ -0,0 +1,505 @@ +/* + * Copyright (c) 2018-2021 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "src/core/gpu/cl/kernels/ClElementwiseKernel.h" + +#include "arm_compute/core/CL/CLHelpers.h" +#include "arm_compute/core/CL/ICLTensor.h" +#include "src/core/CL/CLValidate.h" +#include "src/core/common/Validate.h" +#include "src/core/helpers/AutoConfiguration.h" +#include "src/core/helpers/WindowHelpers.h" +#include "support/Cast.h" +#include "support/StringSupport.h" +#include + +namespace arm_compute +{ +namespace opencl +{ +namespace kernels +{ +namespace +{ +constexpr unsigned int vector_size_byte_opencl = 16; + +std::map supported_arithmetic_ops = +{ + { ArithmeticOperation::ADD, "ADD" }, + { ArithmeticOperation::SUB, "SUB" }, + { ArithmeticOperation::DIV, "DIV" }, + { ArithmeticOperation::SQUARED_DIFF, "SQUARED_DIFF" }, + { ArithmeticOperation::MIN, "MIN" }, + { ArithmeticOperation::MAX, "MAX" }, + { ArithmeticOperation::POWER, "POWER" }, + { ArithmeticOperation::PRELU, "PRELU" }, +}; + +std::map supported_sat_arithmetic_ops = +{ + { ArithmeticOperation::ADD, "ADD" }, + { ArithmeticOperation::SUB, "SUB" }, +}; + +std::string generate_id_for_tuning_common(const std::string &kernel_name, const ITensorInfo &src1, const ITensorInfo &dst) +{ + std::string config_id; + // Set config_id for enabling LWS tuning + config_id = kernel_name; + config_id += "_"; + config_id += lower_string(string_from_data_type(src1.data_type())); + config_id += "_"; + config_id += support::cpp11::to_string(dst.dimension(0)); + config_id += "_"; + config_id += support::cpp11::to_string(dst.dimension(1)); + return config_id; +} + +Status validate_arguments_with_float_only_supported_rules(const ITensorInfo &src1, const ITensorInfo &src2, const ITensorInfo &dst) +{ + ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(&src1, &src2, &dst); + ARM_COMPUTE_RETURN_ERROR_ON_F16_UNSUPPORTED(&src1); + ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(&src1, 1, DataType::F16, DataType::F32); + ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(&src1, &src2); + + const TensorShape out_shape = TensorShape::broadcast_shape(src1.tensor_shape(), src2.tensor_shape()); + + ARM_COMPUTE_RETURN_ERROR_ON_MSG(out_shape.total_size() == 0, "Inputs are not broadcast compatible"); + + // Validate in case of configured dst + if(dst.total_size() > 0) + { + ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(&dst, 1, DataType::F16, DataType::F32); + ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(&src1, &dst); + ARM_COMPUTE_RETURN_ERROR_ON_MSG(detail::have_different_dimensions(out_shape, dst.tensor_shape(), 0), + "Wrong shape for dst"); + } + + return Status{}; +} + +Status validate_arguments_with_arithmetic_rules(const ITensorInfo &src1, const ITensorInfo &src2, const ITensorInfo &dst) +{ + ARM_COMPUTE_RETURN_ERROR_ON_F16_UNSUPPORTED(&src1); + ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(&src1, 1, DataType::U8, DataType::QASYMM8, DataType::QASYMM8_SIGNED, + DataType::S16, DataType::QSYMM16, DataType::F16, + DataType::S32, DataType::F32); + ARM_COMPUTE_RETURN_ERROR_ON_F16_UNSUPPORTED(&src2); + ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(&src2, 1, DataType::U8, DataType::QASYMM8, DataType::QASYMM8_SIGNED, + DataType::S16, DataType::QSYMM16, DataType::F16, + DataType::S32, DataType::F32); + + const bool is_quantized = is_data_type_quantized(src1.data_type()) || is_data_type_quantized(src2.data_type()); + if(is_quantized) + { + ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(&src1, &src2); + + if(is_data_type_quantized_symmetric(src1.data_type())) + { + const int32_t in1_offset = src1.quantization_info().uniform().offset; + const int32_t in2_offset = src2.quantization_info().uniform().offset; + ARM_COMPUTE_RETURN_ERROR_ON_MSG(in1_offset != 0, "For quantized symmetric, offset must be zero"); + ARM_COMPUTE_RETURN_ERROR_ON_MSG(in2_offset != 0, "For quantized symmetric, offset must be zero"); + } + } + + const TensorShape out_shape = TensorShape::broadcast_shape(src1.tensor_shape(), src2.tensor_shape()); + + ARM_COMPUTE_RETURN_ERROR_ON_MSG(out_shape.total_size() == 0, "Inputs are not broadcast compatible"); + + // Validate in case of configured dst + if(dst.total_size() > 0) + { + ARM_COMPUTE_RETURN_ERROR_ON_F16_UNSUPPORTED(&dst); + ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(&dst, 1, DataType::U8, DataType::QASYMM8, DataType::QASYMM8_SIGNED, + DataType::S16, DataType::QSYMM16, DataType::F16, + DataType::S32, DataType::F32); + ARM_COMPUTE_RETURN_ERROR_ON_MSG((dst.data_type() == DataType::U8) && ((src1.data_type() != DataType::U8) || (src2.data_type() != DataType::U8)), + "dst can only be U8 if both inputs are U8"); + ARM_COMPUTE_RETURN_ERROR_ON_MSG(detail::have_different_dimensions(out_shape, dst.tensor_shape(), 0), + "Wrong shape for dst"); + + if(is_quantized) + { + ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(&src1, &dst); + + if(is_data_type_quantized_symmetric(dst.data_type())) + { + const int32_t offset = dst.quantization_info().uniform().offset; + ARM_COMPUTE_RETURN_ERROR_ON_MSG(offset != 0, "For quantized symmetric, offset must be zero"); + } + } + } + return Status{}; +} + +CLBuildOptions generate_build_options_with_arithmetic_rules(const ITensorInfo &src1, const ITensorInfo &src2, const ITensorInfo &dst, const std::string &operation_string) +{ + CLBuildOptions build_opts; + + const unsigned int num_elems_processed_per_iteration = adjust_vec_size(vector_size_byte_opencl / dst.element_size(), dst.dimension(0)); + + build_opts.add_option("-DDATA_TYPE_IN1=" + get_cl_type_from_data_type(src1.data_type())); + build_opts.add_option("-DDATA_TYPE_IN2=" + get_cl_type_from_data_type(src2.data_type())); + build_opts.add_option("-DDATA_TYPE_OUT=" + get_cl_type_from_data_type(dst.data_type())); + build_opts.add_option("-DVEC_SIZE_IN1=" + support::cpp11::to_string(src1.dimension(0) == 1 ? 1 : num_elems_processed_per_iteration)); + build_opts.add_option("-DVEC_SIZE_IN2=" + support::cpp11::to_string(src2.dimension(0) == 1 ? 1 : num_elems_processed_per_iteration)); + build_opts.add_option("-DVEC_SIZE_OUT=" + support::cpp11::to_string(num_elems_processed_per_iteration)); + build_opts.add_option("-DVEC_SIZE_LEFTOVER=" + support::cpp11::to_string(dst.dimension(0) % num_elems_processed_per_iteration)); + build_opts.add_option("-DOP=" + operation_string); + if(is_data_type_quantized(src1.data_type())) + { + const UniformQuantizationInfo iq1info = src1.quantization_info().uniform(); + const UniformQuantizationInfo iq2info = src2.quantization_info().uniform(); + const UniformQuantizationInfo oqinfo = dst.quantization_info().uniform(); + + build_opts.add_option("-DOFFSET_IN1=" + support::cpp11::to_string(iq1info.offset)); + build_opts.add_option("-DOFFSET_IN2=" + support::cpp11::to_string(iq2info.offset)); + build_opts.add_option("-DOFFSET_OUT=" + support::cpp11::to_string(oqinfo.offset)); + build_opts.add_option("-DSCALE_IN1=" + float_to_string_with_full_precision(iq1info.scale)); + build_opts.add_option("-DSCALE_IN2=" + float_to_string_with_full_precision(iq2info.scale)); + build_opts.add_option("-DSCALE_OUT=" + float_to_string_with_full_precision(oqinfo.scale)); + } + return build_opts; +} + +std::pair configure_window_arithmetic_common(ITensorInfo &dst) +{ + const unsigned int num_elems_processed_per_iteration = adjust_vec_size(vector_size_byte_opencl / dst.element_size(), dst.dimension(0)); + Window win = calculate_max_window(dst, Steps(num_elems_processed_per_iteration)); + return std::make_pair(Status{}, win); +} + +std::pair validate_and_configure_window_for_arithmetic_operators(ITensorInfo &src1, ITensorInfo &src2, ITensorInfo &dst) +{ + const std::pair broadcast_pair = ITensorInfo::broadcast_shape_and_valid_region(src1, src2); + const TensorShape &out_shape = broadcast_pair.first; + + set_shape_if_empty(dst, out_shape); + + if(src1.data_type() == DataType::S16 || src2.data_type() == DataType::S16) + { + set_format_if_unknown(dst, Format::S16); + } + else if(src1.data_type() == DataType::F16 || src2.data_type() == DataType::F16) + { + set_format_if_unknown(dst, Format::F16); + } + else if(src1.data_type() == DataType::F32 || src2.data_type() == DataType::F32) + { + set_format_if_unknown(dst, Format::F32); + } + else if(src1.data_type() == DataType::QASYMM8 || src2.data_type() == DataType::QASYMM8) + { + set_data_type_if_unknown(dst, DataType::QASYMM8); + } + else if(src1.data_type() == DataType::QASYMM8_SIGNED || src2.data_type() == DataType::QASYMM8_SIGNED) + { + set_data_type_if_unknown(dst, DataType::QASYMM8_SIGNED); + } + else if(src1.data_type() == DataType::QSYMM16 || src2.data_type() == DataType::QSYMM16) + { + set_data_type_if_unknown(dst, DataType::QSYMM16); + } + + return configure_window_arithmetic_common(dst); +} + +std::pair validate_and_configure_window_for_logical_binary_operators(ITensorInfo &src1, ITensorInfo &src2, ITensorInfo &dst) +{ + const std::pair broadcast_pair = ITensorInfo::broadcast_shape_and_valid_region(src1, src2); + const TensorShape &out_shape = broadcast_pair.first; + + set_shape_if_empty(dst, out_shape); + set_data_type_if_unknown(dst, DataType::U8); + + // The arithmetic utility functions can be share + return configure_window_arithmetic_common(dst); +} + +std::pair validate_and_configure_window_for_division(ITensorInfo &src1, ITensorInfo &src2, ITensorInfo &dst) +{ + const std::pair broadcast_pair = ITensorInfo::broadcast_shape_and_valid_region(src1, src2); + const TensorShape &out_shape = broadcast_pair.first; + auto_init_if_empty(dst, out_shape, 1, src1.data_type()); + return configure_window_arithmetic_common(dst); +} +} // namespace + +void ClElementwiseKernel::configure_common(ITensorInfo *src1, ITensorInfo *src2, ITensorInfo *dst) +{ + configure_common(CLKernelLibrary::get().get_compile_context(), src1, src2, dst); +} + +void ClElementwiseKernel::configure_common(const ClCompileContext &compile_context, ITensorInfo *src1, ITensorInfo *src2, ITensorInfo *dst) +{ + // Configure kernel window + auto win_config = validate_and_configure_window(*src1, *src2, *dst); + ARM_COMPUTE_ERROR_THROW_ON(win_config.first); + + _src1 = src1; + _src2 = src2; + _dst = dst; + + std::string kernel_name = "elementwise_operation_" + name(); + if(is_data_type_quantized(src1->data_type())) + { + kernel_name += "_quantized"; + } + + // Set kernel build options + CLBuildOptions build_opts = generate_build_options(*src1, *src2, *dst); + if(_act_info.enabled()) + { + build_opts.add_option("-DACTIVATION_TYPE=" + lower_string(string_from_activation_func(_act_info.activation()))); + build_opts.add_option("-DA_VAL=" + float_to_string_with_full_precision(_act_info.a())); + build_opts.add_option("-DB_VAL=" + float_to_string_with_full_precision(_act_info.b())); + } + + // Create kernel + _kernel = create_kernel(compile_context, kernel_name, build_opts.options()); + + ICLKernel::configure_internal(win_config.second); + + _config_id = generate_id_for_tuning(kernel_name, *src1, *dst); +} + +void ClElementwiseKernel::run_op(ITensorPack &tensors, const Window &window, ::cl::CommandQueue &queue) +{ + ARM_COMPUTE_ERROR_ON_UNCONFIGURED_KERNEL(this); + ARM_COMPUTE_ERROR_ON_INVALID_SUBWINDOW(ICLKernel::window(), window); + + const auto src_0 = utils::cast::polymorphic_downcast(tensors.get_const_tensor(TensorType::ACL_SRC_0)); + const auto src_1 = utils::cast::polymorphic_downcast(tensors.get_const_tensor(TensorType::ACL_SRC_1)); + auto dst = utils::cast::polymorphic_downcast(tensors.get_tensor(TensorType::ACL_DST)); + + const TensorShape &in_shape1 = src_0->info()->tensor_shape(); + const TensorShape &in_shape2 = src_1->info()->tensor_shape(); + const TensorShape &out_shape = dst->info()->tensor_shape(); + + bool can_collapse = true; + const bool is_vector = in_shape1.num_dimensions() == 1 || in_shape2.num_dimensions() == 1; + if(std::min(in_shape1.total_size(), in_shape2.total_size()) > 1 && !is_vector) + { + can_collapse = (std::min(in_shape1.num_dimensions(), in_shape2.num_dimensions()) > Window::DimZ); + for(size_t d = Window::DimZ; can_collapse && (d < out_shape.num_dimensions()); d++) + { + can_collapse = (in_shape1[d] == in_shape2[d]); + } + } + + bool has_collapsed = false; + Window collapsed = can_collapse ? window.collapse_if_possible(ICLKernel::window(), Window::DimZ, &has_collapsed) : window; + + const TensorShape &in_shape1_collapsed = has_collapsed ? in_shape1.collapsed_from(Window::DimZ) : in_shape1; + const TensorShape &in_shape2_collapsed = has_collapsed ? in_shape2.collapsed_from(Window::DimZ) : in_shape2; + + Window slice = collapsed.first_slice_window_3D(); + Window slice_src1 = slice.broadcast_if_dimension_le_one(in_shape1_collapsed); + Window slice_src2 = slice.broadcast_if_dimension_le_one(in_shape2_collapsed); + do + { + unsigned int idx = 0; + add_3D_tensor_argument(idx, src_0, slice_src1); + add_3D_tensor_argument(idx, src_1, slice_src2); + add_3D_tensor_argument(idx, dst, slice); + + enqueue(queue, *this, slice, lws_hint()); + ARM_COMPUTE_UNUSED(collapsed.slide_window_slice_3D(slice_src1)); + ARM_COMPUTE_UNUSED(collapsed.slide_window_slice_3D(slice_src2)); + } + while(collapsed.slide_window_slice_3D(slice)); +} + +/** Logical binary */ + +void ClLogicalBinaryKernel::configure(const ClCompileContext &compile_context, LogicalOperation op, ITensorInfo *src1, ITensorInfo *src2, ITensorInfo *dst) +{ + ARM_COMPUTE_ERROR_ON_NULLPTR(src1, src2, dst); + ARM_COMPUTE_ERROR_THROW_ON(ClLogicalBinaryKernel::validate(op, src1, src2, dst)); + _op = op; + configure_common(compile_context, src1, src2, dst); +} + +Status ClLogicalBinaryKernel::validate(LogicalOperation op, const ITensorInfo *src1, const ITensorInfo *src2, const ITensorInfo *dst) +{ + ARM_COMPUTE_UNUSED(op); + ARM_COMPUTE_ASSERT(op != LogicalOperation::Unknown && op != LogicalOperation::Not); + ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(src1, src2, dst); + + ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(src1, 1, DataType::U8); + ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(src1, src2); + + ARM_COMPUTE_RETURN_ON_ERROR(validate_arguments_with_arithmetic_rules(*src1, *src2, *dst)); + ARM_COMPUTE_RETURN_ON_ERROR(validate_and_configure_window_for_logical_binary_operators(*src1->clone(), *src2->clone(), *dst->clone()).first); + + return Status{}; +} + +std::string ClLogicalBinaryKernel::name() +{ + switch(_op) + { + case LogicalOperation::And: + return "AND"; + case LogicalOperation::Or: + return "OR"; + case LogicalOperation::Not: + /* fall through */ + default: + ARM_COMPUTE_ASSERT(true); + } + return ""; +} + +std::pair ClLogicalBinaryKernel::validate_and_configure_window(ITensorInfo &src1, ITensorInfo &src2, ITensorInfo &dst) +{ + return validate_and_configure_window_for_logical_binary_operators(src1, src2, dst); +} + +CLBuildOptions ClLogicalBinaryKernel::generate_build_options(const ITensorInfo &src1, const ITensorInfo &src2, const ITensorInfo &dst) +{ + // The arithmetic utility functions can be share + return generate_build_options_with_arithmetic_rules(src1, src2, dst, name()); +} + +std::string ClLogicalBinaryKernel::generate_id_for_tuning(const std::string &kernel_name, const ITensorInfo &src1, const ITensorInfo &dst) +{ + return generate_id_for_tuning_common(kernel_name, src1, dst); +} + +/** Arithmetic operations with saturation*/ +void ClSaturatedArithmeticKernel::configure(const ClCompileContext &compile_context, ArithmeticOperation op, ITensorInfo *input1, ITensorInfo *input2, ITensorInfo *output, + const ConvertPolicy &policy, + const ActivationLayerInfo &act_info) +{ + ARM_COMPUTE_ERROR_ON_NULLPTR(input1, input2, output); + ARM_COMPUTE_ERROR_THROW_ON(ClSaturatedArithmeticKernel::validate(op, input1, input2, output, policy, act_info)); + auto padding_info = get_padding_info({ input1, input2, output }); + + _policy = policy; + _op = op; + _act_info = act_info; + configure_common(compile_context, input1, input2, output); + ARM_COMPUTE_ERROR_ON(has_padding_changed(padding_info)); +} + +Status ClSaturatedArithmeticKernel::validate(ArithmeticOperation op, const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output, const ConvertPolicy &policy, + const ActivationLayerInfo &act_info) +{ + ARM_COMPUTE_UNUSED(op, policy); + ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(input1, input2, output); + ARM_COMPUTE_RETURN_ON_ERROR(validate_arguments_with_arithmetic_rules(*input1, *input2, *output)); + ARM_COMPUTE_RETURN_ON_ERROR(validate_and_configure_window_for_arithmetic_operators(*input1->clone(), *input2->clone(), *output->clone()).first); + ARM_COMPUTE_RETURN_ERROR_ON(act_info.enabled() && !is_data_type_float(output->data_type())); + + return Status{}; +} + +std::pair ClSaturatedArithmeticKernel::validate_and_configure_window(ITensorInfo &input1, ITensorInfo &input2, ITensorInfo &output) +{ + return validate_and_configure_window_for_arithmetic_operators(input1, input2, output); +} + +CLBuildOptions ClSaturatedArithmeticKernel::generate_build_options(const ITensorInfo &input1, const ITensorInfo &input2, const ITensorInfo &output) +{ + const bool has_float_out = is_data_type_float(output.data_type()); + auto build_options = generate_build_options_with_arithmetic_rules(input1, input2, output, name()); + build_options.add_option((_policy == ConvertPolicy::WRAP || has_float_out) ? "-DWRAP" : "-DSATURATE"); + return build_options; +} + +std::string ClSaturatedArithmeticKernel::generate_id_for_tuning(const std::string &kernel_name, const ITensorInfo &input1, const ITensorInfo &output) +{ + auto config_id = generate_id_for_tuning_common(kernel_name, input1, output); + config_id += (_policy == ConvertPolicy::WRAP) ? "_wrap_" : "_saturate_"; + config_id += lower_string(string_from_data_layout(input1.data_layout())); + return config_id; +} + +std::string ClSaturatedArithmeticKernel::name() +{ + return supported_sat_arithmetic_ops[_op]; +} + +/** Arithmetic operations*/ +void ClArithmeticKernel::configure(const ClCompileContext &compile_context, ArithmeticOperation op, ITensorInfo *src1, ITensorInfo *src2, ITensorInfo *dst, + const ActivationLayerInfo &act_info) +{ + ARM_COMPUTE_ERROR_ON_NULLPTR(src1, src2, dst); + ARM_COMPUTE_ERROR_THROW_ON(ClArithmeticKernel::validate(op, src1, src2, dst, act_info)); + auto padding_info = get_padding_info({ src1, src2, dst }); + + _op = op; + _act_info = act_info; + configure_common(compile_context, src1, src2, dst); + ARM_COMPUTE_ERROR_ON(has_padding_changed(padding_info)); +} + +Status ClArithmeticKernel::validate(ArithmeticOperation op, const ITensorInfo *src1, const ITensorInfo *src2, const ITensorInfo *dst, const ActivationLayerInfo &act_info) +{ + ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(src1, src2, dst); + if(op == ArithmeticOperation::DIV || op == ArithmeticOperation::POWER) + { + // Division and Power operators don't support integer arithmetic + ARM_COMPUTE_RETURN_ON_ERROR(validate_arguments_with_float_only_supported_rules(*src1, *src2, *dst)); + ARM_COMPUTE_RETURN_ON_ERROR(validate_and_configure_window_for_division(*src1->clone(), *src2->clone(), *dst->clone()).first); + } + else + { + ARM_COMPUTE_RETURN_ON_ERROR(validate_arguments_with_arithmetic_rules(*src1, *src2, *dst)); + ARM_COMPUTE_RETURN_ON_ERROR(validate_and_configure_window_for_arithmetic_operators(*src1->clone(), *src2->clone(), *dst->clone()).first); + } + ARM_COMPUTE_RETURN_ERROR_ON(act_info.enabled() && !is_data_type_float(dst->data_type())); + + return Status{}; +} +std::pair ClArithmeticKernel::validate_and_configure_window(ITensorInfo &src1, ITensorInfo &src2, ITensorInfo &dst) +{ + if(_op == ArithmeticOperation::DIV || _op == ArithmeticOperation::POWER) + { + // Division and Power operators don't support integer arithmetic + return validate_and_configure_window_for_division(src1, src2, dst); + } + else + { + return validate_and_configure_window_for_arithmetic_operators(src1, src2, dst); + } +} + +CLBuildOptions ClArithmeticKernel::generate_build_options(const ITensorInfo &src1, const ITensorInfo &src2, const ITensorInfo &dst) +{ + return generate_build_options_with_arithmetic_rules(src1, src2, dst, name()); +} +std::string ClArithmeticKernel::generate_id_for_tuning(const std::string &kernel_name, const ITensorInfo &src1, const ITensorInfo &dst) +{ + return generate_id_for_tuning_common(kernel_name, src1, dst); +} + +std::string ClArithmeticKernel::name() +{ + return supported_arithmetic_ops[_op]; +} +} // namespace kernels +} // namespace opencl +} // namespace arm_compute diff --git a/src/core/gpu/cl/kernels/ClElementwiseKernel.h b/src/core/gpu/cl/kernels/ClElementwiseKernel.h new file mode 100644 index 0000000000..4ed8ae73ab --- /dev/null +++ b/src/core/gpu/cl/kernels/ClElementwiseKernel.h @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2018-2021 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef ARM_COMPUTE_CL_ELEMENTWISE_KERNEL_H +#define ARM_COMPUTE_CL_ELEMENTWISE_KERNEL_H + +#include "src/core/KernelTypes.h" +#include "src/core/common/Macros.h" +#include "src/core/gpu/cl/ClCompileContext.h" +#include "src/core/gpu/cl/IClKernel.h" + +namespace arm_compute +{ +namespace opencl +{ +namespace kernels +{ +/** Interface for an element-wise operation kernel + * + * Element-wise operation is computed by: + * @f[ dst(x,y) = OP(src1(x,y), src2(x,y))@f] + * + */ +class ClElementwiseKernel : public IClKernel +{ +public: + /** Default constructor */ + ClElementwiseKernel() = default; + ARM_COMPUTE_DISALLOW_COPY_ALLOW_MOVE(ClElementwiseKernel); + + // Inherited methods overridden: + void run_op(ITensorPack &tensors, const Window &window, ::cl::CommandQueue &queue) override; + +protected: + /** The name of the operation */ + virtual std::string name() = 0; + + /** Configure kernel for a given list of arguments + * + * @param[in] src1 First source tensor info. Data types supported: U8/S8/QASYMM8/QASYMM8_SIGNED/U16/S16/F16/U32/S32/F32. + * @param[in] src2 Second source tensor info. Data types supported: same as @p src1. + * @param[in] dst Destination tensor info. Data types supported: same as @p src1. + * + * @return a pair of Status and Window + */ + virtual std::pair validate_and_configure_window(ITensorInfo &src1, ITensorInfo &src2, ITensorInfo &dst) = 0; + + /** Generate the build options for the specific kernel + * + * @reutrn a CLBuildOptions struct + */ + virtual CLBuildOptions generate_build_options(const ITensorInfo &src1, const ITensorInfo &src2, const ITensorInfo &dst) = 0; + + /** Generate the identifier for tuning + * + * @reutrn a string + */ + virtual std::string generate_id_for_tuning(const std::string &kernel_name, const ITensorInfo &src1, const ITensorInfo &dst) = 0; + + /** Commmon configure function for element-wise operators with no additional options (e.g., Div, Min, Max, SquaredDiff) + * + */ + void configure_common(ITensorInfo *src1, ITensorInfo *src2, ITensorInfo *dst); + /** Commmon configure function for element-wise operators with no additional options (e.g., Div, Min, Max, SquaredDiff) + * + */ + void configure_common(const ClCompileContext &compile_context, ITensorInfo *src1, ITensorInfo *src2, ITensorInfo *dst); + + ActivationLayerInfo _act_info{}; + +private: + const ITensorInfo *_src1{ nullptr }; /**< Source tensor info 1 */ + const ITensorInfo *_src2{ nullptr }; /**< Source tensor info 2 */ + ITensorInfo *_dst{ nullptr }; /**< Destination tensor info */ +}; + +class ClLogicalBinaryKernel : public ClElementwiseKernel +{ +public: + /** Default constructor */ + ClLogicalBinaryKernel() = default; + ARM_COMPUTE_DISALLOW_COPY_ALLOW_MOVE(ClLogicalBinaryKernel); + /** Function to configure kernel + * + * @param[in] compile_context The compile context to be used. + * @param[in] op Logical binary operation to be executed. + * @param[in] src1 First source tensor info. Data types supported: U8. + * @param[in] src2 Second source tensor info. Data types supported: same as @p src1. + * @param[in] dst Destination tensor info. Data types supported: same as @p src1. + */ + void configure(const ClCompileContext &compile_context, LogicalOperation op, ITensorInfo *src1, ITensorInfo *src2, ITensorInfo *dst); + /** Static function to check if the given configuration is valid for this kernel + * + * @param[in] op Logical binary operation to be executed. + * @param[in] src1 First source tensor info. Data types supported: U8. + * @param[in] src2 Second source tensor info. Data types supported: same as @p src1. + * @param[in] dst Destination tensor info. Data types supported: same as @p src1. + */ + static Status validate(LogicalOperation op, const ITensorInfo *src1, const ITensorInfo *src2, const ITensorInfo *dst); + +private: + // Inherited methods overridden: + std::string name() override; + std::pair validate_and_configure_window(ITensorInfo &src1, ITensorInfo &src2, ITensorInfo &dst) override; + CLBuildOptions generate_build_options(const ITensorInfo &src1, const ITensorInfo &src2, const ITensorInfo &dst) override; + std::string generate_id_for_tuning(const std::string &kernel_name, const ITensorInfo &src1, const ITensorInfo &dst) override; + + LogicalOperation _op{ LogicalOperation::Unknown }; +}; + +/** Addition operation */ +class ClSaturatedArithmeticKernel : public ClElementwiseKernel +{ +public: + ClSaturatedArithmeticKernel() = default; + ARM_COMPUTE_DISALLOW_COPY_ALLOW_MOVE(ClSaturatedArithmeticKernel); + /** Static function to check if given info will lead to a valid configuration of @ref ClSaturatedArithmeticKernel + * + * @param[in] compile_context The compile context to be used. + * @param[in] op Arithmetic operation to be executed. + * @param[in] input1 First tensor input info. Data types supported: U8/QASYMM8/QASYMM8_SIGNED/S16/QSYMM16/F16/S32/F32. + * @param[in] input2 Second tensor input info. Data types supported: Same as @p input1. + * @param[in] output Output tensor info. Data types supported: Same as @p input1. + * @param[in] policy Policy to use to handle overflow. + * @param[in] act_info (Optional) Activation layer information in case of a fused activation. + */ + void configure(const ClCompileContext &compile_context, ArithmeticOperation op, ITensorInfo *input1, ITensorInfo *input2, ITensorInfo *output, const ConvertPolicy &policy, + const ActivationLayerInfo &act_info = ActivationLayerInfo()); + + /** Static function to check if given info will lead to a valid configuration of @ref ClSaturatedArithmeticKernel + * + * @param[in] op Arithmetic operation to be executed. + * @param[in] input1 First tensor input info info. Data types supported: U8/QASYMM8/QASYMM8_SIGNED/S16/QSYMM16/F16/S32/F32. + * @param[in] input2 Second tensor input info info. Data types supported: Same as @p input1. + * @param[in] output Output tensor info info. Data types supported: Same as @p input1. + * @param[in] policy Policy to use to handle overflow. + * @param[in] act_info (Optional) Activation layer information in case of a fused activation. + * + * @return a Status + */ + static Status validate(ArithmeticOperation op, const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output, const ConvertPolicy &policy, + const ActivationLayerInfo &act_info = ActivationLayerInfo()); + +protected: + // Inherited methods overridden: + std::string name() override; + std::pair validate_and_configure_window(ITensorInfo &input1, ITensorInfo &input2, ITensorInfo &output) override; + CLBuildOptions generate_build_options(const ITensorInfo &input1, const ITensorInfo &input2, const ITensorInfo &output) override; + std::string generate_id_for_tuning(const std::string &kernel_name, const ITensorInfo &input1, const ITensorInfo &output) override; + +private: + ConvertPolicy _policy{}; + ArithmeticOperation _op{}; +}; + +class ClArithmeticKernel : public ClElementwiseKernel +{ +public: + ClArithmeticKernel() = default; + ARM_COMPUTE_DISALLOW_COPY_ALLOW_MOVE(ClArithmeticKernel); + + /** Static function to check if given info will lead to a valid configuration of @ref ClArithmeticKernel + * + * @param[in] compile_context The compile context to be used. + * @param[in] op Arithmetic operation to be executed. + * @param[in] src1 First source tensor info. Data types supported: U8/QASYMM8/QASYMM8_SIGNED/S16/QSYMM16/F16/S32/F32. + * @param[in] src2 Second source tensor info. Data types supported: same as @p src1. + * @param[in] dst Destination tensor info. Data types supported: same as @p src1. + * @param[in] act_info (Optional) Activation layer information in case of a fused activation. + */ + void configure(const ClCompileContext &compile_context, ArithmeticOperation op, ITensorInfo *src1, ITensorInfo *src2, ITensorInfo *dst, + const ActivationLayerInfo &act_info = ActivationLayerInfo()); + + /** Static function to check if given info will lead to a valid configuration of @ref ClArithmeticKernel + * + * @param[in] op Arithmetic operation to be executed. + * @param[in] src1 First source tensor info. Data types supported: U8/QASYMM8/QASYMM8_SIGNED/S16/QSYMM16/F16/S32/F32. + * @param[in] src2 Second source tensor info. Data types supported: same as @p src1. + * @param[in] dst Destination tensor info. Data types supported: same as @p src1. + * @param[in] act_info (Optional) Activation layer information in case of a fused activation. + * + * @return a Status + */ + static Status validate(ArithmeticOperation op, const ITensorInfo *src1, const ITensorInfo *src2, const ITensorInfo *dst, const ActivationLayerInfo &act_info = ActivationLayerInfo()); + +protected: + // Inherited methods overridden: + std::string name() override; + std::pair validate_and_configure_window(ITensorInfo &src1, ITensorInfo &src2, ITensorInfo &dst) override; + CLBuildOptions generate_build_options(const ITensorInfo &src1, const ITensorInfo &src2, const ITensorInfo &dst) override; + std::string generate_id_for_tuning(const std::string &kernel_name, const ITensorInfo &src1, const ITensorInfo &dst) override; + +private: + ArithmeticOperation _op{}; +}; +} // namespace kernels +} // namespace opencl +} // namespace arm_compute +#endif /* ARM_COMPUTE_CL_ELEMENTWISE_KERNEL_H */ diff --git a/src/runtime/CL/functions/CLElementwiseOperations.cpp b/src/runtime/CL/functions/CLElementwiseOperations.cpp index a72e957fe6..638990e472 100644 --- a/src/runtime/CL/functions/CLElementwiseOperations.cpp +++ b/src/runtime/CL/functions/CLElementwiseOperations.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2020 Arm Limited. + * Copyright (c) 2018-2021 Arm Limited. * * SPDX-License-Identifier: MIT * @@ -25,7 +25,9 @@ #include "arm_compute/core/CL/ICLTensor.h" #include "arm_compute/runtime/CL/CLScheduler.h" -#include "src/core/CL/kernels/CLElementwiseOperationKernel.h" +#include "src/core/gpu/cl/kernels/ClElementwiseKernel.h" + +#include "src/runtime/gpu/cl/operators/ClAdd.h" #include @@ -33,34 +35,13 @@ namespace arm_compute { namespace experimental { -CLArithmeticAddition::CLArithmeticAddition() -{ -} - -void CLArithmeticAddition::configure(const CLCompileContext &compile_context, ITensorInfo *input1, ITensorInfo *input2, ITensorInfo *output, ConvertPolicy policy, const ActivationLayerInfo &act_info) -{ - auto k = std::make_unique(); - k->configure(compile_context, ArithmeticOperation::ADD, input1, input2, output, policy, act_info); - _kernel = std::move(k); -} - -Status CLArithmeticAddition::validate(const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output, ConvertPolicy policy, const ActivationLayerInfo &act_info) -{ - return CLSaturatedArithmeticOperationKernel::validate(ArithmeticOperation::ADD, input1, input2, output, policy, act_info); -} - -void CLArithmeticAddition::run(ITensorPack &tensors) -{ - ICLOperator::run(tensors); -} - CLArithmeticSubtraction::CLArithmeticSubtraction() { } void CLArithmeticSubtraction::configure(const CLCompileContext &compile_context, ITensorInfo *input1, ITensorInfo *input2, ITensorInfo *output, ConvertPolicy policy, const ActivationLayerInfo &act_info) { - auto k = std::make_unique(); + auto k = std::make_unique(); k->configure(compile_context, ArithmeticOperation::SUB, input1, input2, output, policy, act_info); _kernel = std::move(k); } @@ -68,7 +49,7 @@ void CLArithmeticSubtraction::configure(const CLCompileContext &compile_context, Status CLArithmeticSubtraction::validate(const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output, ConvertPolicy policy, const ActivationLayerInfo &act_info) { ARM_COMPUTE_UNUSED(policy); - return CLSaturatedArithmeticOperationKernel::validate(ArithmeticOperation::SUB, input1, input2, output, policy, act_info); + return arm_compute::opencl::kernels::ClSaturatedArithmeticKernel::validate(ArithmeticOperation::SUB, input1, input2, output, policy, act_info); } void CLArithmeticSubtraction::run(ITensorPack &tensors) @@ -82,14 +63,14 @@ CLArithmeticDivision::CLArithmeticDivision() void CLArithmeticDivision::configure(const CLCompileContext &compile_context, ITensorInfo *input1, ITensorInfo *input2, ITensorInfo *output, const ActivationLayerInfo &act_info) { - auto k = std::make_unique(); + auto k = std::make_unique(); k->configure(compile_context, ArithmeticOperation::DIV, input1, input2, output, act_info); _kernel = std::move(k); } Status CLArithmeticDivision::validate(const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output, const ActivationLayerInfo &act_info) { - return CLArithmeticOperationKernel::validate(ArithmeticOperation::DIV, input1, input2, output, act_info); + return arm_compute::opencl::kernels::ClArithmeticKernel::validate(ArithmeticOperation::DIV, input1, input2, output, act_info); } void CLArithmeticDivision::run(ITensorPack &tensors) @@ -103,14 +84,14 @@ CLElementwiseMax::CLElementwiseMax() void CLElementwiseMax::configure(const CLCompileContext &compile_context, ITensorInfo *input1, ITensorInfo *input2, ITensorInfo *output, const ActivationLayerInfo &act_info) { - auto k = std::make_unique(); + auto k = std::make_unique(); k->configure(compile_context, ArithmeticOperation::MAX, input1, input2, output, act_info); _kernel = std::move(k); } Status CLElementwiseMax::validate(const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output, const ActivationLayerInfo &act_info) { - return CLArithmeticOperationKernel::validate(ArithmeticOperation::MAX, input1, input2, output, act_info); + return arm_compute::opencl::kernels::ClArithmeticKernel::validate(ArithmeticOperation::MAX, input1, input2, output, act_info); } void CLElementwiseMax::run(ITensorPack &tensors) @@ -124,14 +105,14 @@ CLElementwiseMin::CLElementwiseMin() void CLElementwiseMin::configure(const CLCompileContext &compile_context, ITensorInfo *input1, ITensorInfo *input2, ITensorInfo *output, const ActivationLayerInfo &act_info) { - auto k = std::make_unique(); + auto k = std::make_unique(); k->configure(compile_context, ArithmeticOperation::MIN, input1, input2, output, act_info); _kernel = std::move(k); } Status CLElementwiseMin::validate(const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output, const ActivationLayerInfo &act_info) { - return CLArithmeticOperationKernel::validate(ArithmeticOperation::MIN, input1, input2, output, act_info); + return arm_compute::opencl::kernels::ClArithmeticKernel::validate(ArithmeticOperation::MIN, input1, input2, output, act_info); } void CLElementwiseMin::run(ITensorPack &tensors) @@ -145,14 +126,14 @@ CLElementwiseSquaredDiff::CLElementwiseSquaredDiff() void CLElementwiseSquaredDiff::configure(const CLCompileContext &compile_context, ITensorInfo *input1, ITensorInfo *input2, ITensorInfo *output, const ActivationLayerInfo &act_info) { - auto k = std::make_unique(); + auto k = std::make_unique(); k->configure(compile_context, ArithmeticOperation::SQUARED_DIFF, input1, input2, output, act_info); _kernel = std::move(k); } Status CLElementwiseSquaredDiff::validate(const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output, const ActivationLayerInfo &act_info) { - return CLArithmeticOperationKernel::validate(ArithmeticOperation::SQUARED_DIFF, input1, input2, output, act_info); + return arm_compute::opencl::kernels::ClArithmeticKernel::validate(ArithmeticOperation::SQUARED_DIFF, input1, input2, output, act_info); } void CLElementwiseSquaredDiff::run(ITensorPack &tensors) @@ -166,14 +147,14 @@ CLElementwisePower::CLElementwisePower() void CLElementwisePower::configure(const CLCompileContext &compile_context, ITensorInfo *input1, ITensorInfo *input2, ITensorInfo *output, const ActivationLayerInfo &act_info) { - auto k = std::make_unique(); + auto k = std::make_unique(); k->configure(compile_context, ArithmeticOperation::POWER, input1, input2, output, act_info); _kernel = std::move(k); } Status CLElementwisePower::validate(const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output, const ActivationLayerInfo &act_info) { - return CLArithmeticOperationKernel::validate(ArithmeticOperation::POWER, input1, input2, output, act_info); + return arm_compute::opencl::kernels::ClArithmeticKernel::validate(ArithmeticOperation::POWER, input1, input2, output, act_info); } void CLElementwisePower::run(ITensorPack &tensors) @@ -181,13 +162,12 @@ void CLElementwisePower::run(ITensorPack &tensors) ICLOperator::run(tensors); } } // namespace experimental - struct CLArithmeticAddition::Impl { - const ICLTensor *src_0{ nullptr }; - const ICLTensor *src_1{ nullptr }; - ICLTensor *dst{ nullptr }; - std::unique_ptr op{ nullptr }; + const ICLTensor *src_0{ nullptr }; + const ICLTensor *src_1{ nullptr }; + ICLTensor *dst{ nullptr }; + std::unique_ptr op{ nullptr }; }; CLArithmeticAddition::CLArithmeticAddition() @@ -209,13 +189,13 @@ void CLArithmeticAddition::configure(const CLCompileContext &compile_context, co _impl->src_0 = input1; _impl->src_1 = input2; _impl->dst = output; - _impl->op = std::make_unique(); + _impl->op = std::make_unique(); _impl->op->configure(compile_context, input1->info(), input2->info(), output->info(), policy, act_info); } Status CLArithmeticAddition::validate(const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output, ConvertPolicy policy, const ActivationLayerInfo &act_info) { - return experimental::CLArithmeticAddition::validate(input1, input2, output, policy, act_info); + return opencl::ClAdd::validate(input1, input2, output, policy, act_info); } void CLArithmeticAddition::run() diff --git a/src/runtime/CL/functions/CLLogicalAnd.cpp b/src/runtime/CL/functions/CLLogicalAnd.cpp index f1c53651c7..98c98abed5 100644 --- a/src/runtime/CL/functions/CLLogicalAnd.cpp +++ b/src/runtime/CL/functions/CLLogicalAnd.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Arm Limited. + * Copyright (c) 2020-2021 Arm Limited. * * SPDX-License-Identifier: MIT * @@ -23,7 +23,7 @@ */ #include "arm_compute/runtime/CL/functions/CLLogicalAnd.h" #include "arm_compute/core/CL/ICLTensor.h" -#include "src/core/CL/kernels/CLElementwiseOperationKernel.h" +#include "src/core/gpu/cl/kernels/ClElementwiseKernel.h" #include @@ -33,14 +33,14 @@ namespace experimental { void CLLogicalAnd::configure(const CLCompileContext &compile_context, ITensorInfo *input1, ITensorInfo *input2, ITensorInfo *output) { - auto k = std::make_unique(); - k->configure(compile_context, kernels::LogicalOperation::And, input1, input2, output); + auto k = std::make_unique(); + k->configure(compile_context, LogicalOperation::And, input1, input2, output); _kernel = std::move(k); } Status CLLogicalAnd::validate(const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output) { - return CLLogicalBinaryKernel::validate(kernels::LogicalOperation::And, input1, input2, output); + return arm_compute::opencl::kernels::ClLogicalBinaryKernel::validate(LogicalOperation::And, input1, input2, output); } void CLLogicalAnd::run(ITensorPack &tensors) diff --git a/src/runtime/CL/functions/CLLogicalOr.cpp b/src/runtime/CL/functions/CLLogicalOr.cpp index 8c6087ed7d..897963ab50 100644 --- a/src/runtime/CL/functions/CLLogicalOr.cpp +++ b/src/runtime/CL/functions/CLLogicalOr.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Arm Limited. + * Copyright (c) 2020-2021 Arm Limited. * * SPDX-License-Identifier: MIT * @@ -23,7 +23,7 @@ */ #include "arm_compute/runtime/CL/functions/CLLogicalOr.h" #include "arm_compute/core/CL/ICLTensor.h" -#include "src/core/CL/kernels/CLElementwiseOperationKernel.h" +#include "src/core/gpu/cl/kernels/ClElementwiseKernel.h" #include @@ -33,14 +33,14 @@ namespace experimental { void CLLogicalOr::configure(const CLCompileContext &compile_context, ITensorInfo *input1, ITensorInfo *input2, ITensorInfo *output) { - auto k = std::make_unique(); - k->configure(compile_context, kernels::LogicalOperation::Or, input1, input2, output); + auto k = std::make_unique(); + k->configure(compile_context, LogicalOperation::Or, input1, input2, output); _kernel = std::move(k); } Status CLLogicalOr::validate(const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output) { - return CLLogicalBinaryKernel::validate(kernels::LogicalOperation::Or, input1, input2, output); + return arm_compute::opencl::kernels::ClLogicalBinaryKernel::validate(LogicalOperation::Or, input1, input2, output); } void CLLogicalOr::run(ITensorPack &tensors) diff --git a/src/runtime/CL/functions/CLPReluLayer.cpp b/src/runtime/CL/functions/CLPReluLayer.cpp index 876b5de0f7..74286d46ca 100644 --- a/src/runtime/CL/functions/CLPReluLayer.cpp +++ b/src/runtime/CL/functions/CLPReluLayer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 Arm Limited. + * Copyright (c) 2019-2021 Arm Limited. * * SPDX-License-Identifier: MIT * @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -#include "src/core/CL/kernels/CLElementwiseOperationKernel.h" +#include "src/core/gpu/cl/kernels/ClElementwiseKernel.h" #include "arm_compute/core/CL/ICLTensor.h" #include "arm_compute/runtime/CL/CLScheduler.h" @@ -37,14 +37,14 @@ CLPReluLayer::CLPReluLayer() void CLPReluLayer::configure(const CLCompileContext &compile_context, ITensorInfo *input, ITensorInfo *alpha, ITensorInfo *output) { - auto k = std::make_unique(); + auto k = std::make_unique(); k->configure(compile_context, ArithmeticOperation::PRELU, input, alpha, output); _kernel = std::move(k); } Status CLPReluLayer::validate(const ITensorInfo *input, const ITensorInfo *alpha, const ITensorInfo *output) { - return CLArithmeticOperationKernel::validate(ArithmeticOperation::PRELU, input, alpha, output); + return arm_compute::opencl::kernels::ClArithmeticKernel::validate(ArithmeticOperation::PRELU, input, alpha, output); } void CLPReluLayer::run(ITensorPack &tensors) diff --git a/src/runtime/NEON/functions/NELogical.cpp b/src/runtime/NEON/functions/NELogical.cpp index 190998b042..674ba40fcd 100644 --- a/src/runtime/NEON/functions/NELogical.cpp +++ b/src/runtime/NEON/functions/NELogical.cpp @@ -50,7 +50,7 @@ void NELogicalAnd::configure(const ITensor *input1, const ITensor *input2, ITens ARM_COMPUTE_ERROR_ON_NULLPTR(input1, input2, output); _impl->kernel = std::make_unique(); - _impl->kernel->configure(input1->info(), input2->info(), output->info(), kernels::LogicalOperation::And); + _impl->kernel->configure(input1->info(), input2->info(), output->info(), LogicalOperation::And); _impl->pack = ITensorPack(); _impl->pack.add_tensor(TensorType::ACL_SRC_0, input1); @@ -60,7 +60,7 @@ void NELogicalAnd::configure(const ITensor *input1, const ITensor *input2, ITens Status NELogicalAnd::validate(const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output) { - return kernels::NELogicalKernel::validate(input1, input2, output, kernels::LogicalOperation::And); + return kernels::NELogicalKernel::validate(input1, input2, output, LogicalOperation::And); } void NELogicalAnd::run() @@ -83,7 +83,7 @@ void NELogicalOr::configure(const ITensor *input1, const ITensor *input2, ITenso ARM_COMPUTE_ERROR_ON_NULLPTR(input1, input2, output); _impl->kernel = std::make_unique(); - _impl->kernel->configure(input1->info(), input2->info(), output->info(), kernels::LogicalOperation::Or); + _impl->kernel->configure(input1->info(), input2->info(), output->info(), LogicalOperation::Or); _impl->pack = ITensorPack(); _impl->pack.add_tensor(TensorType::ACL_SRC_0, input1); @@ -93,7 +93,7 @@ void NELogicalOr::configure(const ITensor *input1, const ITensor *input2, ITenso Status NELogicalOr::validate(const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output) { - return kernels::NELogicalKernel::validate(input1, input2, output, kernels::LogicalOperation::Or); + return kernels::NELogicalKernel::validate(input1, input2, output, LogicalOperation::Or); } void NELogicalOr::run() @@ -116,7 +116,7 @@ void NELogicalNot::configure(const ITensor *input, ITensor *output) ARM_COMPUTE_ERROR_ON_NULLPTR(input, output); _impl->kernel = std::make_unique(); - _impl->kernel->configure(input->info(), nullptr, output->info(), kernels::LogicalOperation::Not); + _impl->kernel->configure(input->info(), nullptr, output->info(), LogicalOperation::Not); _impl->pack = ITensorPack(); _impl->pack.add_tensor(TensorType::ACL_SRC_0, input); @@ -125,7 +125,7 @@ void NELogicalNot::configure(const ITensor *input, ITensor *output) Status NELogicalNot::validate(const ITensorInfo *input, const ITensorInfo *output) { - return kernels::NELogicalKernel::validate(input, nullptr, output, kernels::LogicalOperation::Not); + return kernels::NELogicalKernel::validate(input, nullptr, output, LogicalOperation::Not); } void NELogicalNot::run() diff --git a/src/runtime/gpu/cl/operators/ClAdd.cpp b/src/runtime/gpu/cl/operators/ClAdd.cpp new file mode 100644 index 0000000000..01f550f819 --- /dev/null +++ b/src/runtime/gpu/cl/operators/ClAdd.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "src/runtime/gpu/cl/operators/ClAdd.h" + +#include "src/core/gpu/cl/ClCompileContext.h" +#include "src/core/gpu/cl/kernels/ClElementwiseKernel.h" + +namespace arm_compute +{ +namespace opencl +{ +void ClAdd::configure(const ClCompileContext &compile_context, ITensorInfo *src1, ITensorInfo *src2, ITensorInfo *dst, + ConvertPolicy policy, const ActivationLayerInfo &act_info) +{ + auto k = std::make_unique(); + k->configure(compile_context, ArithmeticOperation::ADD, src1, src2, dst, policy, act_info); + _kernel = std::move(k); +} + +Status ClAdd::validate(const ITensorInfo *src1, const ITensorInfo *src2, const ITensorInfo *dst, + ConvertPolicy policy, const ActivationLayerInfo &act_info) +{ + return kernels::ClSaturatedArithmeticKernel::validate(ArithmeticOperation::ADD, src1, src2, dst, policy, act_info); +} +} // namespace opencl +} // namespace arm_compute diff --git a/src/runtime/gpu/cl/operators/ClAdd.h b/src/runtime/gpu/cl/operators/ClAdd.h new file mode 100644 index 0000000000..2854c16180 --- /dev/null +++ b/src/runtime/gpu/cl/operators/ClAdd.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2021 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef ARM_COMPUTE_CL_ADD_H +#define ARM_COMPUTE_CL_ADD_H + +#include "src/core/gpu/cl/ClCompileContext.h" +#include "src/runtime/gpu/cl/IClOperator.h" + +namespace arm_compute +{ +namespace opencl +{ +/** Basic function to run arithmetic addition + * + * @note The tensor data type for the inputs must be U8/QASYMM8/QASYMM8_SIGNED/S16/QSYMM16/S32/F16/F32. + * @note The function performs an arithmetic addition between two tensors. + */ +class ClAdd : public IClOperator +{ +public: + /** Default Constructor */ + ClAdd() = default; + /** Configure function for a given list of arguments. + * + * Valid configurations (src1,src2) -> dst : + * + * - (U8,U8) -> U8 + * - (U8,U8) -> S16 + * - (S16,U8) -> S16 + * - (U8,S16) -> S16 + * - (S16,S16) -> S16 + * - (S32,S32) -> S32 + * - (F16,F16) -> F16 + * - (F32,F32) -> F32 + * - (QASYMM8,QASYMM8) -> QASYMM8 + * - (QASYMM8_SIGNED,QASYMM8_SIGNED) -> QASYMM8_SIGNED + * - (QSYMM16,QSYMM16) -> QSYMM16 + * + * @param[in] compile_context The compile context to be used. + * @param[in, out] src1 First source tensor. Data types supported: U8/QASYMM8/QASYMM8_SIGNED/S16/QSYMM16/S32/F16/F32. + * The source tensor is [in, out] because its TensorInfo might be modified inside the kernel in case of broadcasting of dimension 0. + * @param[in, out] src2 Second source tensor. Data types supported: U8/QASYMM8/QASYMM8_SIGNED/S16/QSYMM16/S32/F16/F32. + * The source tensor is [in, out] because its TensorInfo might be modified inside the kernel in case of broadcasting of dimension 0. + * @param[out] dst Destination tensor. Data types supported: U8/QASYMM8/QASYMM8_SIGNED/S16/QSYMM16/S32/F16/F32. + * @param[in] policy Policy to use to handle overflow. + * @param[in] act_info (Optional) Activation layer information in case of a fused activation. + */ + void configure(const ClCompileContext &compile_context, ITensorInfo *src1, ITensorInfo *src2, ITensorInfo *dst, ConvertPolicy policy, + const ActivationLayerInfo &act_info = ActivationLayerInfo()); + /** Static function to check if given info will lead to a valid configuration of @ref ClAdd + * + * Valid configurations (src1,src2) -> dst : + * + * - (U8,U8) -> U8 + * - (U8,U8) -> S16 + * - (S16,U8) -> S16 + * - (U8,S16) -> S16 + * - (S16,S16) -> S16 + * - (S32,S32) -> S32 + * - (F16,F16) -> F16 + * - (F32,F32) -> F32 + * - (QASYMM8,QASYMM8) -> QASYMM8 + * - (QASYMM8_SIGNED,QASYMM8_SIGNED) -> QASYMM8_SIGNED + * - (QSYMM16,QSYMM16) -> QSYMM16 + * + * @param[in] src1 First source tensor info. Data types supported: U8/QASYMM8/QASYMM8_SIGNED/S16/QSYMM16/S32/F16/F32. + * @param[in] src2 Second source tensor info. Data types supported: U8/QASYMM8/QASYMM8_SIGNED/S16/QSYMM16/S32/F16/F32. + * @param[in] dst Destination tensor info. Data types supported: U8/QASYMM8/QASYMM8_SIGNED/S16/QSYMM16/S32/F16/F32. + * @param[in] policy Policy to use to handle overflow. + * @param[in] act_info (Optional) Activation layer information in case of a fused activation. + * + * @return a status + */ + static Status validate(const ITensorInfo *src1, const ITensorInfo *src2, const ITensorInfo *dst, ConvertPolicy policy, + const ActivationLayerInfo &act_info = ActivationLayerInfo()); +}; +} // namespace opencl +} // namespace arm_compute +#endif /* ARM_COMPUTE_CL_ADD_H */ diff --git a/tests/validation/reference/Logical.cpp b/tests/validation/reference/Logical.cpp index 099abf6f96..aab57d9118 100644 --- a/tests/validation/reference/Logical.cpp +++ b/tests/validation/reference/Logical.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Arm Limited. + * Copyright (c) 2020-2021 Arm Limited. * * SPDX-License-Identifier: MIT * @@ -34,18 +34,18 @@ namespace validation namespace reference { template -T logical_binary_op(arm_compute::kernels::LogicalOperation op, T src1, T src2) +T logical_binary_op(arm_compute::LogicalOperation op, T src1, T src2) { switch(op) { - case arm_compute::kernels::LogicalOperation::And: + case arm_compute::LogicalOperation::And: return src1 && src2; - case arm_compute::kernels::LogicalOperation::Or: + case arm_compute::LogicalOperation::Or: return src1 || src2; // The following operators are either invalid or not binary operator - case arm_compute::kernels::LogicalOperation::Not: + case arm_compute::LogicalOperation::Not: // fall through - case arm_compute::kernels::LogicalOperation::Unknown: + case arm_compute::LogicalOperation::Unknown: // fall through default: ARM_COMPUTE_ASSERT(true); @@ -57,7 +57,7 @@ template struct BroadcastUnroll { template - static void unroll(arm_compute::kernels::LogicalOperation op, const SimpleTensor &src1, const SimpleTensor &src2, SimpleTensor &dst, + static void unroll(arm_compute::LogicalOperation op, const SimpleTensor &src1, const SimpleTensor &src2, SimpleTensor &dst, Coordinates &id_src1, Coordinates &id_src2, Coordinates &id_dst) { const bool src1_is_broadcast = (src1.shape()[dim - 1] != dst.shape()[dim - 1]); @@ -84,7 +84,7 @@ template <> struct BroadcastUnroll<0> { template - static void unroll(arm_compute::kernels::LogicalOperation op, const SimpleTensor &src1, const SimpleTensor &src2, SimpleTensor &dst, + static void unroll(arm_compute::LogicalOperation op, const SimpleTensor &src1, const SimpleTensor &src2, SimpleTensor &dst, Coordinates &id_src1, Coordinates &id_src2, Coordinates &id_dst) { dst[coord2index(dst.shape(), id_dst)] = logical_binary_op(op, src1[coord2index(src1.shape(), id_src1)], src2[coord2index(src2.shape(), id_src2)]); @@ -99,7 +99,7 @@ SimpleTensor logical_or(const SimpleTensor &src1, const SimpleTensor &s Coordinates id_dst{}; SimpleTensor dst{ TensorShape::broadcast_shape(src1.shape(), src2.shape()), src1.data_type() }; - BroadcastUnroll::unroll(arm_compute::kernels::LogicalOperation::Or, src1, src2, dst, id_src1, id_src2, id_dst); + BroadcastUnroll::unroll(arm_compute::LogicalOperation::Or, src1, src2, dst, id_src1, id_src2, id_dst); return dst; } @@ -112,7 +112,7 @@ SimpleTensor logical_and(const SimpleTensor &src1, const SimpleTensor & Coordinates id_dst{}; SimpleTensor dst{ TensorShape::broadcast_shape(src1.shape(), src2.shape()), src1.data_type() }; - BroadcastUnroll::unroll(arm_compute::kernels::LogicalOperation::And, src1, src2, dst, id_src1, id_src2, id_dst); + BroadcastUnroll::unroll(arm_compute::LogicalOperation::And, src1, src2, dst, id_src1, id_src2, id_dst); return dst; } -- cgit v1.2.1