Skip to content

Commit a02f769

Browse files
akote123Sanket KaleCopilot
authored
[MLAS] Enable FP16 for Gelu (#26815)
Enabled fp16 Gelu for opset20.Gelu uses tanh and ERF functions depending on the approximation method used. Implemented tanh in sve and erf in sve and neon . Gr3E results: with tanh and erf approximation: <html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40"> <head> <meta name=ProgId content=Excel.Sheet> <meta name=Generator content="Microsoft Excel 15"> <link id=Main-File rel=Main-File href="file:///C:/Users/arunak/AppData/Local/Temp/msohtmlclip1/01/clip.htm"> <link rel=File-List href="file:///C:/Users/arunak/AppData/Local/Temp/msohtmlclip1/01/clip_filelist.xml"> <!--table {mso-displayed-decimal-separator:"\."; mso-displayed-thousand-separator:"\,";} @page {margin:.75in .7in .75in .7in; mso-header-margin:.3in; mso-footer-margin:.3in;} tr {mso-height-source:auto;} col {mso-width-source:auto;} br {mso-data-placement:same-cell;} td {padding-top:1px; padding-right:1px; padding-left:1px; mso-ignore:padding; color:black; font-size:11.0pt; font-weight:400; font-style:normal; text-decoration:none; font-family:"Aptos Narrow", sans-serif; mso-font-charset:0; mso-number-format:General; text-align:general; vertical-align:bottom; border:none; mso-background-source:auto; mso-pattern:auto; mso-protection:locked visible; white-space:nowrap; mso-rotate:0;} .xl65 {text-align:center; border:.5pt solid windowtext; background:#ADADAD; mso-pattern:black none;} .xl66 {text-align:center; border-top:.5pt solid windowtext; border-right:.5pt solid windowtext; border-bottom:1.0pt solid windowtext; border-left:.5pt solid windowtext; background:#ADADAD; mso-pattern:black none;} .xl67 {font-weight:700; text-align:center; border-top:none; border-right:none; border-bottom:.5pt solid windowtext; border-left:1.0pt solid windowtext; background:#F7C7AC; mso-pattern:black none;} .xl68 {font-weight:700; text-align:center; border-top:.5pt solid windowtext; border-right:none; border-bottom:.5pt solid windowtext; border-left:1.0pt solid windowtext; background:#F7C7AC; mso-pattern:black none;} .xl69 {font-weight:700; text-align:center; border-top:.5pt solid windowtext; border-right:none; border-bottom:1.0pt solid windowtext; border-left:1.0pt solid windowtext; background:#F7C7AC; mso-pattern:black none;} .xl70 {text-align:center; border-top:.5pt solid windowtext; border-right:.5pt solid windowtext; border-bottom:.5pt solid windowtext; border-left:1.0pt solid windowtext; background:#ADADAD; mso-pattern:black none;} .xl71 {text-align:center; border-top:.5pt solid windowtext; border-right:.5pt solid windowtext; border-bottom:1.0pt solid windowtext; border-left:1.0pt solid windowtext; background:#ADADAD; mso-pattern:black none;} .xl72 {font-weight:700; text-align:center; border-top:none; border-right:.5pt solid windowtext; border-bottom:none; border-left:1.0pt solid windowtext; background:#F7C7AC; mso-pattern:black none;} .xl73 {font-weight:700; text-align:center; border-top:none; border-right:.5pt solid windowtext; border-bottom:none; border-left:.5pt solid windowtext; background:#F7C7AC; mso-pattern:black none;} .xl74 {font-weight:700; text-align:center; border-top:none; border-right:none; border-bottom:1.0pt solid windowtext; border-left:1.0pt solid windowtext; background:#F7C7AC; mso-pattern:black none;} .xl75 {font-weight:700; border:1.0pt solid windowtext;} .xl76 {font-weight:700; text-align:center; border-top:1.0pt solid windowtext; border-right:none; border-bottom:1.0pt solid windowtext; border-left:1.0pt solid windowtext;} .xl77 {font-weight:700; text-align:center; border-top:1.0pt solid windowtext; border-right:none; border-bottom:1.0pt solid windowtext; border-left:none;} --> </head> <body link="#467886" vlink="#96607D"> GELU(ms) | Tanh_SVE | ERF_SVE | Tanh_NEON | ERF_NEON -- | -- | -- | -- | -- Shape | F32 | F16 | F32 | F16 | F32 | F16 | F32 | F16 100 | 0.007 | 0.007 | 0.007 | 0.007 | 0.007 | 0.007 | 0.007 | 0.007 1000 | 0.008 | 0.007 | 0.012 | 0.008 | 0.008 | 0.008 | 0.012 | 0.008 1000000 | 0.076 | 0.039 | 0.203 | 0.07 | 0.066 | 0.043 | 0.151 | 0.064 </body> </html> Gr4 results: with tanh and erf approximation: <html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40"> <head> <meta name=ProgId content=Excel.Sheet> <meta name=Generator content="Microsoft Excel 15"> <link id=Main-File rel=Main-File href="file:///C:/Users/arunak/AppData/Local/Temp/msohtmlclip1/01/clip.htm"> <link rel=File-List href="file:///C:/Users/arunak/AppData/Local/Temp/msohtmlclip1/01/clip_filelist.xml"> <!--table {mso-displayed-decimal-separator:"\."; mso-displayed-thousand-separator:"\,";} @page {margin:.75in .7in .75in .7in; mso-header-margin:.3in; mso-footer-margin:.3in;} tr {mso-height-source:auto;} col {mso-width-source:auto;} br {mso-data-placement:same-cell;} td {padding-top:1px; padding-right:1px; padding-left:1px; mso-ignore:padding; color:black; font-size:11.0pt; font-weight:400; font-style:normal; text-decoration:none; font-family:"Aptos Narrow", sans-serif; mso-font-charset:0; mso-number-format:General; text-align:general; vertical-align:bottom; border:none; mso-background-source:auto; mso-pattern:auto; mso-protection:locked visible; white-space:nowrap; mso-rotate:0;} .xl65 {text-align:center; border:.5pt solid windowtext; background:#ADADAD; mso-pattern:black none;} .xl66 {text-align:center; border-top:.5pt solid windowtext; border-right:.5pt solid windowtext; border-bottom:1.0pt solid windowtext; border-left:.5pt solid windowtext; background:#ADADAD; mso-pattern:black none;} .xl67 {font-weight:700; text-align:center; border-top:none; border-right:none; border-bottom:.5pt solid windowtext; border-left:1.0pt solid windowtext; background:#F7C7AC; mso-pattern:black none;} .xl68 {font-weight:700; text-align:center; border-top:.5pt solid windowtext; border-right:none; border-bottom:.5pt solid windowtext; border-left:1.0pt solid windowtext; background:#F7C7AC; mso-pattern:black none;} .xl69 {font-weight:700; text-align:center; border-top:.5pt solid windowtext; border-right:none; border-bottom:1.0pt solid windowtext; border-left:1.0pt solid windowtext; background:#F7C7AC; mso-pattern:black none;} .xl70 {text-align:center; border-top:.5pt solid windowtext; border-right:.5pt solid windowtext; border-bottom:.5pt solid windowtext; border-left:1.0pt solid windowtext; background:#ADADAD; mso-pattern:black none;} .xl71 {text-align:center; border-top:.5pt solid windowtext; border-right:.5pt solid windowtext; border-bottom:1.0pt solid windowtext; border-left:1.0pt solid windowtext; background:#ADADAD; mso-pattern:black none;} .xl72 {font-weight:700; text-align:center; border-top:none; border-right:.5pt solid windowtext; border-bottom:none; border-left:1.0pt solid windowtext; background:#F7C7AC; mso-pattern:black none;} .xl73 {font-weight:700; text-align:center; border-top:none; border-right:.5pt solid windowtext; border-bottom:none; border-left:.5pt solid windowtext; background:#F7C7AC; mso-pattern:black none;} .xl74 {font-weight:700; text-align:center; border-top:none; border-right:none; border-bottom:1.0pt solid windowtext; border-left:1.0pt solid windowtext; background:#F7C7AC; mso-pattern:black none;} .xl75 {font-weight:700; border:1.0pt solid windowtext;} .xl76 {font-weight:700; text-align:center; border-top:1.0pt solid windowtext; border-right:none; border-bottom:1.0pt solid windowtext; border-left:1.0pt solid windowtext;} .xl77 {font-weight:700; text-align:center; border-top:1.0pt solid windowtext; border-right:none; border-bottom:1.0pt solid windowtext; border-left:none;} --> </head> <body link="#467886" vlink="#96607D"> GELU(ms) | Tanh_SVE | ERF_SVE | Tanh_NEON | ERF_NEON -- | -- | -- | -- | -- Shape | F32 | F16 | F32 | F16 | F32 | F16 | F32 | F16 100 | 0.005 | 0.005 | 0.005 | 0.005 | 0.005 | 0.005 | 0.005 | 0.005 1000 | 0.006 | 0.006 | 0.008 | 0.006 | 0.006 | 0.006 | 0.008 | 0.006 1000000 | 0.092 | 0.046 | 0.224 | 0.088 | 0.091 | 0.051 | 0.213 | 0.086 </body> </html> This PR is a joint contribution by: Aruna K(@akote123) Abhishek Jain(@abhijain1204fujitsu) Sanket Kale(@sanketkaleoss ) --------- Co-authored-by: Sanket Kale <sanketk.kale@fujitsu.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent c7c99ce commit a02f769

19 files changed

Lines changed: 1169 additions & 27 deletions

cmake/onnxruntime_mlas.cmake

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,10 @@ else()
510510
if (onnxruntime_USE_SVE)
511511
list(APPEND mlas_platform_srcs ${MLAS_SRC_DIR}/sve/mlasi_sve.h)
512512
list(APPEND mlas_platform_srcs ${MLAS_SRC_DIR}/sve/elementwise_sve.cpp)
513+
list(APPEND mlas_platform_srcs ${MLAS_SRC_DIR}/sve/elementwise_sve_fp16.cpp)
514+
list(APPEND mlas_platform_srcs ${MLAS_SRC_DIR}/sve/mlas_sve_fp16.h)
513515
set_source_files_properties(${MLAS_SRC_DIR}/sve/elementwise_sve.cpp PROPERTIES COMPILE_FLAGS " -march=armv8.2-a+sve+fp16 ")
516+
set_source_files_properties(${MLAS_SRC_DIR}/sve/elementwise_sve_fp16.cpp PROPERTIES COMPILE_FLAGS " -march=armv8.2-a+sve+fp16 ")
514517
list(APPEND mlas_private_compile_definitions MLAS_USE_SVE)
515518
endif()
516519

@@ -548,6 +551,10 @@ else()
548551
${MLAS_SRC_DIR}/halfgemm_kernel_neon_fp16.cpp
549552
${MLAS_SRC_DIR}/softmax_kernel_neon_fp16.cpp
550553
${MLAS_SRC_DIR}/eltwise_kernel_neon_fp16.cpp
554+
${MLAS_SRC_DIR}/erf_neon_fp16.h
555+
${MLAS_SRC_DIR}/erf_neon_fp16.cpp
556+
${MLAS_SRC_DIR}/gelu_neon_fp16.h
557+
${MLAS_SRC_DIR}/gelu_neon_fp16.cpp
551558
)
552559
if (onnxruntime_USE_ARM_NEON_NCHWC)
553560
list(APPEND mlas_platform_srcs
@@ -577,6 +584,8 @@ else()
577584
set_source_files_properties(${MLAS_SRC_DIR}/aarch64/SconvKernelNeonBf16.S PROPERTIES COMPILE_FLAGS " -march=armv8.2-a+bf16 ")
578585
set_source_files_properties(${MLAS_SRC_DIR}/aarch64/SconvPointwiseKernelNeonBf16.S PROPERTIES COMPILE_FLAGS " -march=armv8.2-a+bf16 ")
579586
endif()
587+
set_source_files_properties(${MLAS_SRC_DIR}/erf_neon_fp16.cpp PROPERTIES COMPILE_FLAGS " -march=armv8.2-a+fp16 ")
588+
set_source_files_properties(${MLAS_SRC_DIR}/gelu_neon_fp16.cpp PROPERTIES COMPILE_FLAGS " -march=armv8.2-a+fp16 ")
580589
endif()
581590

582591
if(ONNXRUNTIME_MLAS_MULTI_ARCH)
@@ -988,4 +997,4 @@ if (NOT onnxruntime_ORT_MINIMAL_BUILD)
988997
endif()
989998
endif()
990999

991-
endif()
1000+
endif()

onnxruntime/core/mlas/inc/mlas.h

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2237,3 +2237,72 @@ MlasFlashAttention(
22372237
MlasFlashAttentionThreadedArgs* args,
22382238
MLAS_THREADPOOL* ThreadPool
22392239
);
2240+
2241+
/**
2242+
* @brief Enumeration of supported GELU algorithm variants.
2243+
*
2244+
* MlasGeluErf - Exact GELU implementation using the error function (erf).
2245+
* MlasGeluTanh - Approximate GELU implementation using tanh-based formulation.
2246+
*/
2247+
typedef enum MLAS_GELU_ALGORITHM {
2248+
MlasGeluErf = 0,
2249+
MlasGeluTanh = 1
2250+
} MLAS_GELU_ALGORITHM;
2251+
2252+
/**
2253+
* @brief Computes element-wise FP16 error function (erf).
2254+
*
2255+
* This routine computes:
2256+
* Output[i] = erf(Input[i])
2257+
* for N elements. Depending on platform capabilities, this may use
2258+
* vectorized FP16 intrinsics or fall back to a scalar FP32 conversion path.
2259+
*
2260+
* @param Input Pointer to input buffer of N FP16 elements.
2261+
* @param Output Pointer to output buffer of N FP16 elements.
2262+
* @param Input_tmp_fp32 Pointer to caller-allocated scratch buffer of N floats
2263+
* for FP32 input conversion (used only on fallback path).
2264+
* @param Output_tmp_fp32 Pointer to caller-allocated scratch buffer of N floats
2265+
* for FP32 output conversion (used only on fallback path).
2266+
* @param N Number of elements to process.
2267+
*/
2268+
void
2269+
MLASCALL
2270+
MlasComputeFP16Erf(
2271+
const MLAS_FP16* Input,
2272+
MLAS_FP16* Output,
2273+
float* Input_tmp_fp32,
2274+
float* Output_tmp_fp32,
2275+
size_t N
2276+
);
2277+
2278+
/**
2279+
* @brief Computes element-wise FP16 GELU activation.
2280+
*
2281+
* This routine computes:
2282+
*
2283+
* If algo == MlasGeluTanh (approximate):
2284+
* GELU(x) = 0.5 * x * (1 + tanh(sqrt(2/pi) * (x + 0.044715 * x^3)))
2285+
*
2286+
* If algo == MlasGeluErf (exact):
2287+
* GELU(x) = 0.5 * x * (1 + erf(x / sqrt(2)))
2288+
*
2289+
* Depending on platform capabilities, this may use vectorized FP16 kernels
2290+
* (SVE/NEON) or fall back to a scalar FP32 conversion path.
2291+
*
2292+
* @param input Pointer to input buffer of FP16 elements.
2293+
* @param output Pointer to output buffer of FP16 elements.
2294+
* @param temp Temporary scratch buffer of at least 'count' FP16 elements.
2295+
* Required by certain vectorized implementations. May be unused
2296+
* in scalar fallback paths.
2297+
* @param count Number of elements to process.
2298+
* @param algo GELU algorithm variant (exact erf or tanh approximation).
2299+
*/
2300+
void
2301+
MLASCALL
2302+
MlasComputeFP16Gelu(
2303+
const MLAS_FP16* input,
2304+
MLAS_FP16* output,
2305+
MLAS_FP16* temp,
2306+
size_t count,
2307+
MLAS_GELU_ALGORITHM algo
2308+
);

onnxruntime/core/mlas/lib/erf.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,3 +266,23 @@ Return Value:
266266
MlasErfKernel(Input, Output, N);
267267
#endif
268268
}
269+
270+
void
271+
MLASCALL
272+
MlasComputeFP16Erf(
273+
const MLAS_FP16* Input,
274+
MLAS_FP16* Output,
275+
float* Input_tmp_fp32,
276+
float* Output_tmp_fp32,
277+
size_t N
278+
)
279+
{
280+
if(GetMlasPlatform().ErfFP16KernelRoutine){
281+
GetMlasPlatform().ErfFP16KernelRoutine(Input, Output, N);
282+
return;
283+
}
284+
285+
MlasConvertHalfToFloatBuffer(Input, Input_tmp_fp32, N);
286+
MlasComputeErf(Input_tmp_fp32, Output_tmp_fp32, N);
287+
MlasConvertFloatToHalfBuffer(Output_tmp_fp32, Output, N);
288+
}
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
/*++
2+
3+
Copyright 2025 FUJITSU LIMITED
4+
Copyright (c) Microsoft Corporation. All rights reserved.
5+
6+
Licensed under the MIT License.
7+
8+
Module Name:
9+
10+
erf_neon_fp16.cpp
11+
12+
Abstract:
13+
14+
This module contains the procedure prototypes for the ERF NEON FP16 intrinsics.
15+
16+
--*/
17+
18+
#include "erf_neon_fp16.h"
19+
20+
#if defined(MLAS_F16VEC_INTRINSICS_SUPPORTED)
21+
22+
using _mlas_fp16_ = uint16_t;
23+
// Helpers to safely convert between float and FP16-bit representation
24+
static float
25+
fp16_to_float(uint16_t h)
26+
{
27+
__fp16 tmp;
28+
std::memcpy(&tmp, &h, sizeof(h));
29+
return (float)tmp;
30+
}
31+
32+
static uint16_t
33+
float_to_fp16(float f)
34+
{
35+
__fp16 tmp = (__fp16)f;
36+
uint16_t h;
37+
std::memcpy(&h, &tmp, sizeof(h));
38+
return h;
39+
}
40+
41+
static inline MLAS_FLOAT16X8
42+
exp_neg_rational_approx_f16(MLAS_FLOAT16X8 x)
43+
{
44+
const float16_t a0 = 6.0f;
45+
MLAS_FLOAT16X8 max_x = MlasBroadcastF16Float16x8(a0);
46+
x = MlasMinimumFloat16(x, max_x);
47+
48+
const float16_t c0 = 1.330f;
49+
const float16_t c1 = -0.390f;
50+
const float16_t c2 = 0.0288f;
51+
52+
const float16_t d0 = 1.338f;
53+
const float16_t d1 = 0.848f;
54+
const float16_t d2 = 0.467f;
55+
56+
MLAS_FLOAT16X8 c0v = MlasBroadcastF16Float16x8(c0);
57+
MLAS_FLOAT16X8 c1v = MlasBroadcastF16Float16x8(c1);
58+
MLAS_FLOAT16X8 c2v = MlasBroadcastF16Float16x8(c2);
59+
60+
MLAS_FLOAT16X8 d0v = MlasBroadcastF16Float16x8(d0);
61+
MLAS_FLOAT16X8 d1v = MlasBroadcastF16Float16x8(d1);
62+
MLAS_FLOAT16X8 d2v = MlasBroadcastF16Float16x8(d2);
63+
MLAS_FLOAT16X8 x2 = MlasMultiplyFloat16(x, x);
64+
MLAS_FLOAT16X8 num = MlasMultiplyAddFloat16(c1v, x, c0v);
65+
num = MlasMultiplyAddFloat16(c2v, x2, num);
66+
MLAS_FLOAT16X8 den = MlasMultiplyAddFloat16(d1v, x, d0v);
67+
den = MlasMultiplyAddFloat16(d2v, x2, den);
68+
MLAS_FLOAT16X8 recip = MlasApproximateReciprocalFloat16(den);
69+
recip = MlasMultiplyFloat16(recip, MlasReciprocalStepFloat16(den, recip));
70+
recip = MlasMultiplyFloat16(recip, MlasReciprocalStepFloat16(den, recip));
71+
MLAS_FLOAT16X8 result = MlasMultiplyFloat16(num, recip);
72+
return result;
73+
}
74+
75+
void
76+
MlasNeonErfFP16Kernel(const MLAS_FP16* Input, MLAS_FP16* Output, size_t N)
77+
{
78+
const auto* input = reinterpret_cast<const _mlas_fp16_*>(Input);
79+
auto* output = reinterpret_cast<_mlas_fp16_*>(Output);
80+
const float16_t p = 0.328f;
81+
const float16_t a1 = 0.2505f;
82+
const float16_t a2 = -0.2881f;
83+
const float16_t a3 = 1.4102f;
84+
const float16_t a4 = -1.423f;
85+
const float16_t a5 = 1.0547f;
86+
87+
MLAS_FLOAT16X8 vp = MlasBroadcastF16Float16x8(p);
88+
MLAS_FLOAT16X8 va1 = MlasBroadcastF16Float16x8(a1);
89+
MLAS_FLOAT16X8 va2 = MlasBroadcastF16Float16x8(a2);
90+
MLAS_FLOAT16X8 va3 = MlasBroadcastF16Float16x8(a3);
91+
MLAS_FLOAT16X8 va4 = MlasBroadcastF16Float16x8(a4);
92+
MLAS_FLOAT16X8 va5 = MlasBroadcastF16Float16x8(a5);
93+
94+
constexpr float16_t one_fp16 = 1.0f;
95+
constexpr float16_t neg_one_fp16 = -1.0f;
96+
constexpr float16_t zero_fp16 = 0.0f;
97+
constexpr float16_t four_fp16 = 4.0f;
98+
99+
MLAS_FLOAT16X8 vone = MlasBroadcastF16Float16x8(one_fp16);
100+
MLAS_FLOAT16X8 vneg_one = MlasBroadcastF16Float16x8(neg_one_fp16);
101+
MLAS_FLOAT16X8 vzero = MlasBroadcastF16Float16x8(zero_fp16);
102+
MLAS_FLOAT16X8 vth = MlasBroadcastF16Float16x8(four_fp16);
103+
104+
size_t i = 0;
105+
for (; i + 8 <= N; i += 8) {
106+
MLAS_FLOAT16X8 x = MlasLoadFloat16x8(&input[i]);
107+
MLAS_UINT16X8 neg_mask = MlasCompareLessThanFloat16(x, vzero);
108+
MLAS_FLOAT16X8 sign = MlasSelectFloat16(neg_mask, vneg_one, vone);
109+
MLAS_FLOAT16X8 absx = MlasAbsFloat16(x);
110+
MLAS_UINT16X8 use_mask = MlasCompareLessThanFloat16(absx, vth);
111+
MLAS_FLOAT16X8 absx_clamped = MlasMinimumFloat16(absx, vth);
112+
MLAS_FLOAT16X8 denom = MlasMultiplyAddFloat16(vp, absx_clamped, vone);
113+
MLAS_FLOAT16X8 t = MlasApproximateReciprocalFloat16(denom);
114+
t = MlasMultiplyFloat16(t, MlasReciprocalStepFloat16(denom, t));
115+
t = MlasMultiplyFloat16(t, MlasReciprocalStepFloat16(denom, t));
116+
MLAS_FLOAT16X8 t2 = MlasMultiplyFloat16(t, t);
117+
MLAS_FLOAT16X8 t3 = MlasMultiplyFloat16(t2, t);
118+
MLAS_FLOAT16X8 t4 = MlasMultiplyFloat16(t3, t);
119+
MLAS_FLOAT16X8 t5 = MlasMultiplyFloat16(t4, t);
120+
MLAS_FLOAT16X8 poly = MlasMultiplyFloat16(va1, t);
121+
poly = MlasMultiplyAddFloat16(va2, t2, poly);
122+
poly = MlasMultiplyAddFloat16(va3, t3, poly);
123+
poly = MlasMultiplyAddFloat16(va4, t4, poly);
124+
poly = MlasMultiplyAddFloat16(va5, t5, poly);
125+
MLAS_FLOAT16X8 x2 = MlasMultiplyFloat16(absx_clamped, absx_clamped);
126+
MLAS_FLOAT16X8 exp_neg_x2 = exp_neg_rational_approx_f16(x2);
127+
MLAS_FLOAT16X8 poly_mul_exp = MlasMultiplyFloat16(poly, exp_neg_x2);
128+
MLAS_FLOAT16X8 one_minus_term = MlasSubtractFloat16(vone, poly_mul_exp);
129+
MLAS_FLOAT16X8 erf_approx = MlasMultiplyFloat16(sign, one_minus_term);
130+
erf_approx = MlasMinimumFloat16(erf_approx, vone);
131+
erf_approx = MlasMaximumFloat16(erf_approx, vneg_one);
132+
MLAS_FLOAT16X8 result = MlasSelectFloat16(use_mask, erf_approx, sign);
133+
MlasStoreFloat16x8(&output[i], result);
134+
}
135+
136+
for (; i < N; i++) {
137+
float x = fp16_to_float(input[i]);
138+
float sign = (x < 0) ? -1.0f : 1.0f;
139+
float absx = fabsf(x);
140+
141+
if (absx > 4.0f) {
142+
output[i] = float_to_fp16(sign);
143+
continue;
144+
}
145+
146+
float t = 1.0f / (1.0f + p * absx);
147+
float poly = a1 * t + a2 * t * t + a3 * t * t * t + a4 * t * t * t * t + a5 * t * t * t * t * t;
148+
float exp_neg_x2 = expf(-absx * absx);
149+
float erf_approx = sign * (1.0f - poly * exp_neg_x2);
150+
if (erf_approx > 1.0f) erf_approx = 1.0f;
151+
if (erf_approx < -1.0f) erf_approx = -1.0f;
152+
153+
output[i] = float_to_fp16(erf_approx);
154+
}
155+
}
156+
#endif
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*++
2+
3+
Copyright 2025 FUJITSU LIMITED
4+
Copyright (c) Microsoft Corporation. All rights reserved.
5+
6+
Licensed under the MIT License.
7+
8+
Module Name:
9+
10+
erf_neon_fp16.h
11+
12+
Abstract:
13+
14+
This module contains the procedure prototypes for the ERF NEON FP16 intrinsics.
15+
16+
--*/
17+
18+
#pragma once
19+
20+
#include <arm_neon.h>
21+
22+
#include "mlasi.h"
23+
#include "fp16_common.h"
24+
#include "softmax_kernel_neon.h"
25+
#include <cstring>
26+
27+
void MlasNeonErfFP16Kernel(const MLAS_FP16* Input, MLAS_FP16* Output, size_t N);

onnxruntime/core/mlas/lib/fp16_common.h

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ Module Name:
1919
#include "mlas_float16.h"
2020
#include "mlasi.h"
2121

22-
#ifdef MLAS_F16VEC_INTRINSICS_SUPPORTED
22+
#if defined(MLAS_F16VEC_INTRINSICS_SUPPORTED)
2323

2424
// TODO!! Add intel fp16 implementations
2525

@@ -579,4 +579,56 @@ MlasShiftLeftInt16(MLAS_INT16X4 Vector)
579579
return vshl_n_s16(Vector, ShiftCount);
580580
}
581581

582-
#endif // fp16 vector intrinsic supported
582+
// NEON FP16 vector intrinsics
583+
#if defined(__ARM_FEATURE_FP16_VECTOR_ARITHMETIC)
584+
MLAS_FORCEINLINE
585+
MLAS_FLOAT16X8
586+
MlasBroadcastF16Float16x8(float16_t Value) { return vdupq_n_f16(Value); }
587+
588+
MLAS_FORCEINLINE
589+
MLAS_FLOAT16X8
590+
MlasLoadf16Float16x8(const float16_t* Buffer) { return vld1q_f16(Buffer); }
591+
592+
MLAS_FORCEINLINE
593+
void
594+
MlasStoref16Float16x8(float16_t* Buffer, MLAS_FLOAT16X8 Vector)
595+
{
596+
vst1q_f16(Buffer, Vector);
597+
}
598+
599+
MLAS_FORCEINLINE
600+
MLAS_FLOAT16X8
601+
MlasReciprocalStepFloat16(MLAS_FLOAT16X8 Vector1, MLAS_FLOAT16X8 Vector2)
602+
{
603+
return vrecpsq_f16(Vector1, Vector2);
604+
}
605+
606+
MLAS_FORCEINLINE
607+
MLAS_FLOAT16X8
608+
MlasApproximateReciprocalFloat16(MLAS_FLOAT16X8 Vector)
609+
{
610+
return vrecpeq_f16(Vector);
611+
}
612+
613+
MLAS_FORCEINLINE
614+
MLAS_UINT16X8
615+
MlasCompareLessThanFloat16(MLAS_FLOAT16X8 Vector1, MLAS_FLOAT16X8 Vector2)
616+
{
617+
return vcltq_f16(Vector1, Vector2);
618+
}
619+
620+
MLAS_FORCEINLINE
621+
MLAS_FLOAT16X8
622+
MlasAbsFloat16(MLAS_FLOAT16X8 Vector)
623+
{
624+
return vabsq_f16(Vector);
625+
}
626+
627+
MLAS_FORCEINLINE
628+
MLAS_FLOAT16X8
629+
MlasSelectFloat16(MLAS_UINT16X8 Vector, MLAS_FLOAT16X8 Vector1, MLAS_FLOAT16X8 Vector2)
630+
{
631+
return vbslq_f16(Vector, Vector1, Vector2);
632+
}
633+
#endif // NEON FP16 vector intrinsics supported
634+
#endif // mlas fp16 intrinsic supported

0 commit comments

Comments
 (0)