mirror of
https://git.ffmpeg.org/ffmpeg.git
synced 2026-10-28 10:04:37 +08:00
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:
1 parent
84e568e72d
commit
58a2dc3c2e
10 files changed
+345
-28
No files matched your search
@@ -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 \
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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 },
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 \
|
||||
|
||||
Reference in new issue
Block a user