/*++ Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT License. Module Name: activate.cpp Abstract: This module implements the fused activation and bias addition routines. --*/ #include "mlasi.h" // // Templates for bias addition functions. // template struct MLAS_BIAS_ADDITION; template<> struct MLAS_BIAS_ADDITION { MLAS_FLOAT32X4 BiasBroadcast; void LoadNext(const float*& Bias) { BiasBroadcast = MlasBroadcastFloat32x4(Bias++); } MLAS_FLOAT32X4 Add(MLAS_FLOAT32X4 Value) { return MlasAddFloat32x4(Value, BiasBroadcast); } float Add(float Value) { return Value + MlasExtractLaneFloat32x4<0>(BiasBroadcast); } }; template<> struct MLAS_BIAS_ADDITION { void LoadNext(const float*& Bias) { MLAS_UNREFERENCED_PARAMETER(Bias); } MLAS_FLOAT32X4 Add(MLAS_FLOAT32X4 Value) { return Value; } float Add(float Value) { return Value; } }; // // Templates for activation functions. // template struct MLAS_ACTIVATION_FUNCTION; template<> struct MLAS_ACTIVATION_FUNCTION { MLAS_ACTIVATION_FUNCTION(const MLAS_ACTIVATION* Activation) { MLAS_UNREFERENCED_PARAMETER(Activation); } MLAS_FLOAT32X4 Activate(MLAS_FLOAT32X4 Value) { return Value; } float Activate(float Value) { return Value; } }; template<> struct MLAS_ACTIVATION_FUNCTION { const MLAS_FLOAT32X4 ZeroFloat32x4 = MlasZeroFloat32x4(); MLAS_ACTIVATION_FUNCTION(const MLAS_ACTIVATION* Activation) { MLAS_UNREFERENCED_PARAMETER(Activation); } MLAS_FLOAT32X4 Activate(MLAS_FLOAT32X4 Value) { return MlasMaximumFloat32x4(ZeroFloat32x4, Value); } float Activate(float Value) { #if defined(MLAS_SSE2_INTRINSICS) return _mm_cvtss_f32(Activate(_mm_set_ss(Value))); #else return std::max(Value, 0.0f); #endif } }; template<> struct MLAS_ACTIVATION_FUNCTION { const MLAS_FLOAT32X4 ZeroFloat32x4 = MlasZeroFloat32x4(); MLAS_FLOAT32X4 AlphaBroadcast; MLAS_ACTIVATION_FUNCTION(const MLAS_ACTIVATION* Activation) { AlphaBroadcast = MlasBroadcastFloat32x4(&Activation->Parameters.LeakyRelu.alpha); } MLAS_FLOAT32X4 Activate(MLAS_FLOAT32X4 Value) { MLAS_FLOAT32X4 ValueTimesAlpha = MlasMultiplyFloat32x4(Value, AlphaBroadcast); #if defined(MLAS_NEON_INTRINSICS) #if defined(_WIN32) return vbslq_f32(vcleq_z_f32_ex(Value), ValueTimesAlpha, Value); #else // N.B. Standard NEON headers lack an intrinsic for the "vcle #0" form. return vbslq_f32(vcleq_f32(Value, ZeroFloat32x4), ValueTimesAlpha, Value); #endif #elif defined(MLAS_AVX_INTRINSICS) return _mm_blendv_ps(ValueTimesAlpha, Value, _mm_cmple_ps(ZeroFloat32x4, Value)); #elif defined(MLAS_SSE2_INTRINSICS) return MlasBlendFloat32x4(ValueTimesAlpha, Value, _mm_cmple_ps(ZeroFloat32x4, Value)); #elif defined(MLAS_VSX_INTRINSICS) return vec_sel(ValueTimesAlpha, Value, vec_cmple(ZeroFloat32x4, Value)); #else return MlasBlendFloat32x4(ValueTimesAlpha, Value, ZeroFloat32x4 < Value); #endif } float Activate(float Value) { float ValueTimesAlpha = Value * MlasExtractLaneFloat32x4<0>(AlphaBroadcast); #if defined(MLAS_SSE2_INTRINSICS) return (Value >= MlasExtractLaneFloat32x4<0>(ZeroFloat32x4)) ? Value : ValueTimesAlpha; #else return (Value >= 0.0f) ? Value : ValueTimesAlpha; #endif } }; template<> struct MLAS_ACTIVATION_FUNCTION { MLAS_FLOAT32X4 MinimumBroadcast; MLAS_FLOAT32X4 MaximumBroadcast; MLAS_ACTIVATION_FUNCTION(const MLAS_ACTIVATION* Activation) { MinimumBroadcast = MlasBroadcastFloat32x4(&Activation->Parameters.Clip.minimum); MaximumBroadcast = MlasBroadcastFloat32x4(&Activation->Parameters.Clip.maximum); } MLAS_FLOAT32X4 Activate(MLAS_FLOAT32X4 Value) { Value = MlasMaximumFloat32x4(MinimumBroadcast, Value); Value = MlasMinimumFloat32x4(MaximumBroadcast, Value); return Value; } float Activate(float Value) { #if defined(MLAS_SSE2_INTRINSICS) return _mm_cvtss_f32(Activate(_mm_set_ss(Value))); #else Value = std::max(Value, MlasExtractLaneFloat32x4<0>(MinimumBroadcast)); Value = std::min(Value, MlasExtractLaneFloat32x4<0>(MaximumBroadcast)); return Value; #endif } }; template<> struct MLAS_ACTIVATION_FUNCTION { MLAS_FLOAT32X4 AlphaBroadcast; MLAS_FLOAT32X4 BetaBroadcast; MLAS_FLOAT32X4 MinimumBroadcast; MLAS_FLOAT32X4 MaximumBroadcast; MLAS_ACTIVATION_FUNCTION(const MLAS_ACTIVATION* Activation) { AlphaBroadcast = MlasBroadcastFloat32x4(&Activation->Parameters.HardSigmoid.alpha); BetaBroadcast = MlasBroadcastFloat32x4(&Activation->Parameters.HardSigmoid.beta); MinimumBroadcast = MlasZeroFloat32x4(); MaximumBroadcast = MlasBroadcastFloat32x4(1.0f); } MLAS_FLOAT32X4 Activate(MLAS_FLOAT32X4 Value) { Value = MlasMultiplyAddFloat32x4(Value, AlphaBroadcast, BetaBroadcast); Value = MlasMinimumFloat32x4(MaximumBroadcast, Value); Value = MlasMaximumFloat32x4(MinimumBroadcast, Value); return Value; } float Activate(float Value) { #if defined(MLAS_SSE2_INTRINSICS) return _mm_cvtss_f32(Activate(_mm_set_ss(Value))); #else Value = MlasExtractLaneFloat32x4<0>(AlphaBroadcast) * Value + MlasExtractLaneFloat32x4<0>(BetaBroadcast); Value = std::min(Value, MlasExtractLaneFloat32x4<0>(MaximumBroadcast)); Value = std::max(Value, MlasExtractLaneFloat32x4<0>(MinimumBroadcast)); return Value; #endif } }; template void MlasActivationKernel( const MLAS_ACTIVATION* Activation, float* Buffer, const float* Bias, size_t M, size_t N, size_t ldc ) /*++ Routine Description: This routine steps over the output matrix and invokes the templated bias addition and activation functions. Arguments: Activation - Supplies the parameters for the activation. Buffer - Supplies the output matrix. Bias - Supplies the optional bias vector. M - Supplies the number of elements of the bias vector and the number of rows in the output matrix. N - Supplies the number of columns of the output matrix. ldc - Supplies the number of elements per row of the output matrix. Return Value: None. --*/ { MLAS_ACTIVATION_FUNCTION ActivationFunction(Activation); MLAS_BIAS_ADDITION BiasAddition; // // Step through each row of the output matrix. // while (M-- > 0) { float* buffer = Buffer; size_t n = N; BiasAddition.LoadNext(Bias); if (n >= 4) { do { MLAS_FLOAT32X4 Vector = BiasAddition.Add(MlasLoadFloat32x4(buffer)); MlasStoreFloat32x4(buffer, ActivationFunction.Activate(Vector)); buffer += 4; n -= 4; } while (n >= 4); } while (n > 0) { float Scalar = BiasAddition.Add(*buffer); *buffer++ = ActivationFunction.Activate(Scalar); n -= 1; } Buffer += ldc; } } template<> inline void MlasActivationKernel( const MLAS_ACTIVATION* Activation, float* Buffer, const float* Bias, size_t M, size_t N, size_t ldc ) /*++ Routine Description: This routine is invoked for the special case of an identity operation with no bias addition, which translates to a no-op. Arguments: Activation - Supplies the parameters for the activation. Buffer - Supplies the output matrix. Bias - Supplies the optional bias vector. M - Supplies the number of elements of the bias vector and the number of rows in the output matrix. N - Supplies the number of columns of the output matrix. ldc - Supplies the number of elements per row of the output matrix. Return Value: None. --*/ { // // No operation. // MLAS_UNREFERENCED_PARAMETER(Activation); MLAS_UNREFERENCED_PARAMETER(Buffer); MLAS_UNREFERENCED_PARAMETER(Bias); MLAS_UNREFERENCED_PARAMETER(M); MLAS_UNREFERENCED_PARAMETER(N); MLAS_UNREFERENCED_PARAMETER(ldc); } template inline void MlasActivationKernel( const MLAS_ACTIVATION* Activation, float* Buffer, const float* Bias, size_t M, size_t N, size_t ldc ) /*++ Routine Description: This routine invokes the appropriate activation kernel based on the optional bias vector. Arguments: Activation - Supplies the parameters for the activation. Buffer - Supplies the output matrix. Bias - Supplies the optional bias vector. M - Supplies the number of elements of the bias vector and the number of rows in the output matrix. N - Supplies the number of columns of the output matrix. ldc - Supplies the number of elements per row of the output matrix. Return Value: None. --*/ { if (Bias != nullptr) { MlasActivationKernel(Activation, Buffer, Bias, M, N, ldc); } else { MlasActivationKernel(Activation, Buffer, Bias, M, N, ldc); } } void MLASCALL MlasActivation( const MLAS_ACTIVATION* Activation, float* Buffer, const float* Bias, size_t M, size_t N, size_t ldc ) /*++ Routine Description: This routine applies an activation function to the output matrix after optionally adding a bias vector. Arguments: Activation - Supplies the parameters for the activation. Buffer - Supplies the output matrix. Bias - Supplies the optional bias vector. M - Supplies the number of elements of the bias vector and the number of rows in the output matrix. N - Supplies the number of columns of the output matrix. ldc - Supplies the number of elements per row of the output matrix. Return Value: None. --*/ { switch (Activation->ActivationKind) { case MlasIdentityActivation: { MlasActivationKernel(Activation, Buffer, Bias, M, N, ldc); break; } case MlasReluActivation: { MlasActivationKernel(Activation, Buffer, Bias, M, N, ldc); break; } case MlasLeakyReluActivation: { MlasActivationKernel(Activation, Buffer, Bias, M, N, ldc); break; } case MlasTanhActivation: { if (Bias != nullptr) { MlasActivationKernel(Activation, Buffer, Bias, M, N, ldc); } if (N == ldc) { MlasComputeTanh(Buffer, Buffer, M * N); } else { while (M-- > 0) { MlasComputeTanh(Buffer, Buffer, N); Buffer += ldc; } } break; } case MlasLogisticActivation: { if (Bias != nullptr) { MlasActivationKernel(Activation, Buffer, Bias, M, N, ldc); } if (N == ldc) { MlasComputeLogistic(Buffer, Buffer, M * N); } else { while (M-- > 0) { MlasComputeLogistic(Buffer, Buffer, N); Buffer += ldc; } } break; } case MlasClipActivation: { MlasActivationKernel(Activation, Buffer, Bias, M, N, ldc); break; } case MlasHardSigmoidActivation: { MlasActivationKernel(Activation, Buffer, Bias, M, N, ldc); break; } case MlasActivationKindCount: { MLAS_THROW_EX(std::runtime_error, "bad mlas activation kind"); break; } } }