avutil/base64: add aarch64 NEON implementation for base64 encode

Add NEON implementation using LD3 to load 48 bytes with de-interleaving
using which 6 bits are extracted using USHR and SLI to get 4 registers, which
is used as a index into lookup table using TBL with 4 registers to translate
into the 64-byte output, which is written using ST4 to interleave.
Add a checkasm test to validate the input lengths from 0 to 239 bytes to cover
all the code paths introduced in the NEON implementation.
Refactored the C implementation into a header file so that it can be used
from checkasm test without having to duplicate the code there.

Observed about 4x speedup by checkasm on A720 core on 1kB input:
C:
 - base64.base64 [OK]
NEON:
 - base64.base64 [OK]
checkasm: all 1 tests passed
Benchmark results:
  name                      nsecs (vs ref)
  av_base64_encode_c:      1286.9
  av_base64_encode_neon:    315.5 ( 4.08x)
This commit is contained in:
Shreesh Adiga authored and michaelni committed 2026-09-18 14:20:59 +00:00
1 parent 84e568e72d
commit 58a2dc3c2e
10 files changed
+345 -28

No files matched your search

+1
View File
@@ -6,6 +6,7 @@ OBJS += aarch64/aes_init.o \
ARMV8-OBJS += aarch64/crc.o
NEON-OBJS += aarch64/aes.o \
aarch64/base64.o \
aarch64/float_dsp_neon.o \
aarch64/tx_float_neon.o \
+162
View File
@@ -0,0 +1,162 @@
/*
* Copyright (c) 2026 Shreesh Adiga <16567adigashreesh@gmail.com>
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "asm.S"
function ff_base64_encode_neon, export=1
// x0 - char *out
// x1 - const char *in
// w2 - int in_size
// x3 - const char *tbl
uxtw x2, w2
cmp x2, #48
add x4, x2, x1 // end = src + in_size
mov x5, x0 // dst = out
b.lt 2f // skip NEON loop
ld1 { v0.16b - v3.16b }, [x3]
movi v4.16b, #63
sub x6, x4, #48
1: // NEON 48bytes loop
ld3 { v5.16b - v7.16b }, [x1], #48
ushr v18.16b, v5.16b, #2
ushr v16.16b, v6.16b, #4
tbl v18.16b, { v0.16b, v1.16b, v2.16b, v3.16b }, v18.16b
ushr v17.16b, v7.16b, #6
sli v16.16b, v5.16b, #4
and v5.16b, v7.16b, v4.16b
sli v17.16b, v6.16b, #2
tbl v21.16b, { v0.16b, v1.16b, v2.16b, v3.16b }, v5.16b
and v16.16b, v16.16b, v4.16b
cmp x1, x6
tbl v19.16b, { v0.16b, v1.16b, v2.16b, v3.16b }, v16.16b
and v17.16b, v17.16b, v4.16b
tbl v20.16b, { v0.16b, v1.16b, v2.16b, v3.16b }, v17.16b
st4 { v18.16b - v21.16b }, [x5], #64
b.ls 1b // NEON 48bytes loop
sub x6, x4, #24
cmp x1, x6
b.gt 2f
// process 24 bytes using half width vector
ld3 { v5.8b - v7.8b }, [x1], #24
ushr v18.8b, v5.8b, #2
ushr v16.8b, v6.8b, #4
tbl v18.8b, { v0.16b, v1.16b, v2.16b, v3.16b }, v18.8b
ushr v17.8b, v7.8b, #6
sli v16.8b, v5.8b, #4
and v5.8b, v7.8b, v4.8b
sli v17.8b, v6.8b, #2
tbl v21.8b, { v0.16b, v1.16b, v2.16b, v3.16b }, v5.8b
and v16.8b, v16.8b, v4.8b
tbl v19.8b, { v0.16b, v1.16b, v2.16b, v3.16b }, v16.8b
and v17.8b, v17.8b, v4.8b
tbl v20.8b, { v0.16b, v1.16b, v2.16b, v3.16b }, v17.8b
st4 { v18.8b - v21.8b }, [x5], #32
2: // skip NEON loop
sub x6, x4, #4
cmp x1, x6
b.gt 4f // <4 bytes tail
3: // 4bytes available loop
ldr w7, [x1], #3
rev w7, w7
lsr x8, x7, #26
ubfx x9, x7, #20, #6
ldrb w12, [x3, x8]
ubfx x10, x7, #14, #6
ldrb w13, [x3, x9]
ubfx x11, x7, #8, #6
ldrb w14, [x3, x10]
orr w12, w12, w13, lsl #8
cmp x1, x6
ldrb w15, [x3, x11]
orr w12, w12, w14, lsl #16
orr w12, w12, w15, lsl #24
str w12, [x5], #4
b.ls 3b // 4bytes available loop
4: // <4 bytes tail
sub x6, x4, x1
cmp x6, #3
b.eq 7f
cmp x6, #2
b.eq 6f
cmp x6, #1
b.eq 5f
strb wzr, [x5]
ret
5:
// 1 byte input remaining, 2 bytes padding needed
ldrb w7, [x1]
lsr x8, x7, #2
ubfiz x9, x7, #4, #2
ldrb w8, [x3, x8]
movz w10, #0x3d3d, lsl #16
ldrb w9, [x3, x9]
orr w10, w10, w8
orr w10, w10, w9, lsl #8
str w10, [x5]
strb wzr, [x5, #4]
ret
6:
// 2 bytes input remaining, 1 byte padding needed
ldrb w7, [x1]
ldrb w8, [x1, #1]
lsr w9, w7, #2
lsr w10, w8, #4
ubfiz w11, w8, #2, #4
ldrb w12, [x3, x9]
bfi w10, w7, #4, #2
ldrb w14, [x3, x11]
movz w15, #0x3d00, lsl #16
ldrb w13, [x3, x10]
orr w15, w15, w12
orr w15, w15, w14, lsl #16
orr w15, w15, w13, lsl #8
str w15, [x5]
strb wzr, [x5, #4]
ret
7:
// 3 bytes input remaining, no padding needed
ldrb w7, [x1]
ldrb w8, [x1, #1]
lsr w10, w7, #2
ldrb w9, [x1, #2]
lsr w11, w8, #4
ldrb w10, [x3, x10]
lsr w12, w9, #6
bfi w11, w7, #4, #2
bfi w12, w8, #2, #4
ldrb w11, [x3, x11]
and w13, w9, #0x3f
ldrb w12, [x3, x12]
orr w10, w10, w11, lsl #8
ldrb w13, [x3, x13]
orr w10, w10, w12, lsl #16
orr w10, w10, w13, lsl #24
str w10, [x5]
strb wzr, [x5, #4]
ret
endfunc
+26
View File
@@ -0,0 +1,26 @@
/*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef AVUTIL_AARCH64_BASE64_H
#define AVUTIL_AARCH64_BASE64_H
#include <stdint.h>
char *ff_base64_encode_neon(char *out, const uint8_t *in, int in_size, const char *tbl);
#endif /* AVUTIL_AARCH64_BASE64_H */
+11 -28
View File
@@ -28,8 +28,13 @@
#include <stddef.h>
#include "base64.h"
#include "base64_internal.h"
#include "error.h"
#include "intreadwrite.h"
#if ARCH_AARCH64
#include "libavutil/aarch64/cpu.h"
#include "libavutil/aarch64/base64.h"
#endif
/* ---------------- private code */
static const uint8_t map2[256] =
@@ -148,36 +153,14 @@ char *av_base64_encode(char *out, int out_size, const uint8_t *in, int in_size)
{
static const char b64[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
char *ret, *dst;
unsigned i_bits = 0;
int i_shift = 0;
int bytes_remaining = in_size;
if (in_size >= UINT_MAX / 4 ||
out_size < AV_BASE64_SIZE(in_size))
return NULL;
ret = dst = out;
while (bytes_remaining > 3) {
i_bits = AV_RB32(in);
in += 3; bytes_remaining -= 3;
*dst++ = b64[ i_bits>>26 ];
*dst++ = b64[(i_bits>>20) & 0x3F];
*dst++ = b64[(i_bits>>14) & 0x3F];
*dst++ = b64[(i_bits>>8 ) & 0x3F];
#if ARCH_AARCH64
if (in_size >= 48 && have_neon(av_get_cpu_flags())) {
return ff_base64_encode_neon(out, in, in_size, b64);
}
i_bits = 0;
while (bytes_remaining) {
i_bits = (i_bits << 8) + *in++;
bytes_remaining--;
i_shift += 8;
}
while (i_shift > 0) {
*dst++ = b64[(i_bits << 6 >> i_shift) & 0x3f];
i_shift -= 6;
}
while ((dst - ret) & 3)
*dst++ = '=';
*dst = '\0';
return ret;
#endif
return ff_base64_encode_c(out, in, in_size, b64);
}
+66
View File
@@ -0,0 +1,66 @@
/*
* Copyright (c) 2006 Ryan Martell. (rdm4@martellventures.com)
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef AVUTIL_BASE64_INTERNAL_H
#define AVUTIL_BASE64_INTERNAL_H
#include <stdint.h>
#include "intreadwrite.h"
/*****************************************************************************
* b64_encode: Stolen from VLC's http.c.
* Simplified by Michael.
* Fixed edge cases and made it work from data (vs. strings) by Ryan.
*****************************************************************************/
static inline char *ff_base64_encode_c(char *out, const uint8_t *in, int in_size, const char *b64)
{
char *ret, *dst;
unsigned i_bits = 0;
int i_shift = 0;
int bytes_remaining = in_size;
ret = dst = out;
while (bytes_remaining > 3) {
i_bits = AV_RB32(in);
in += 3; bytes_remaining -= 3;
*dst++ = b64[ i_bits>>26 ];
*dst++ = b64[(i_bits>>20) & 0x3F];
*dst++ = b64[(i_bits>>14) & 0x3F];
*dst++ = b64[(i_bits>>8 ) & 0x3F];
}
i_bits = 0;
while (bytes_remaining) {
i_bits = (i_bits << 8) + *in++;
bytes_remaining--;
i_shift += 8;
}
while (i_shift > 0) {
*dst++ = b64[(i_bits << 6 >> i_shift) & 0x3f];
i_shift -= 6;
}
while ((dst - ret) & 3)
*dst++ = '=';
*dst = '\0';
return ret;
}
#endif /* AVUTIL_BASE64_INTERNAL_H */
+1
View File
@@ -101,6 +101,7 @@ CHECKASMOBJS-$(CONFIG_SWSCALE) += $(SWSCALEOBJS)
# libavutil tests
AVUTILOBJS += aes.o
AVUTILOBJS += av_tx.o
AVUTILOBJS += base64.o
AVUTILOBJS += crc.o
AVUTILOBJS += fixed_dsp.o
AVUTILOBJS += float_dsp.o
+75
View File
@@ -0,0 +1,75 @@
/*
* Copyright (c) 2026 Shreesh Adiga <16567adigashreesh@gmail.com>
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with FFmpeg; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "checkasm.h"
#include "libavutil/base64.h"
#include "libavutil/base64_internal.h"
#include "libavutil/mem_internal.h"
#if ARCH_AARCH64
#include "libavutil/aarch64/cpu.h"
#include "libavutil/aarch64/base64.h"
#endif
#include <string.h>
#define BUF_SIZE (1024)
static char *(*base64_encode_get_fn(void))(char *, const uint8_t *, int, const char *) {
#if ARCH_AARCH64
int cpu_flags = av_get_cpu_flags();
if (have_neon(cpu_flags))
return ff_base64_encode_neon;
else
#endif
return ff_base64_encode_c;
}
static void check_base64_encode(void)
{
LOCAL_ALIGNED_32(uint8_t, buf, [BUF_SIZE]);
LOCAL_ALIGNED_32(uint8_t, out1, [AV_BASE64_SIZE(BUF_SIZE)]);
LOCAL_ALIGNED_32(uint8_t, out2, [AV_BASE64_SIZE(BUF_SIZE)]);
static const char b64[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
for (int i = 0; i < BUF_SIZE; i++)
buf[i] = rnd();
declare_func(char *, char *out, const uint8_t *in, int in_size, const char *b64);
func_type *fn = base64_encode_get_fn();
if (check_func(fn, "av_base64_encode")) {
for (int i = 0; i < 48 * 5; i++) {
size_t offset = rnd() % (BUF_SIZE - i);
call_new(out1, i ? buf + offset : NULL, i, b64);
call_ref(out2, i ? buf + offset : NULL, i, b64);
if (memcmp(out1, out2, AV_BASE64_SIZE(i)))
fail();
}
bench_new(out1, buf, BUF_SIZE, b64);
}
}
void checkasm_check_base64(void)
{
check_base64_encode();
report("base64");
}
+1
View File
@@ -303,6 +303,7 @@ static const CheckasmTest tests[] = {
#endif
#if CONFIG_AVUTIL
{ "aes", checkasm_check_aes },
{ "base64", checkasm_check_base64 },
{ "crc", checkasm_check_crc, .uninit = checkasm_uninit_crc },
{ "fixed_dsp", checkasm_check_fixed_dsp },
{ "float_dsp", checkasm_check_float_dsp },
+1
View File
@@ -39,6 +39,7 @@ void checkasm_check_alacdsp(void);
void checkasm_check_apv_dsp(void);
void checkasm_check_audiodsp(void);
void checkasm_check_av_tx(void);
void checkasm_check_base64(void);
void checkasm_check_blackdetect(void);
void checkasm_check_blend(void);
void checkasm_check_blockdsp(void);
+1
View File
@@ -8,6 +8,7 @@ FATE_CHECKASM = fate-checkasm-aacencdsp \
fate-checkasm-apv_dsp \
fate-checkasm-audiodsp \
fate-checkasm-av_tx \
fate-checkasm-base64 \
fate-checkasm-blockdsp \
fate-checkasm-bswapdsp \
fate-checkasm-cavsdsp \