[PR] avformat: optimizations (PR #24283)
michaelni via ffmpeg-devel <[email protected]>
| Newsgroups | gmane.comp.video.ffmpeg.devel |
|---|---|
| Message-ID | <[email protected]> |
PR #24283 opened by michaelni URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24283 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24283.patch Demuxing a 1.4G MOV with -c copy from an NVMe drive: 3.420 -> 3.182 seconds cold cache (-7%, n=12), 3.257 -> 3.184 seconds warm (-2.2%, n=15), both p < 1e-4. Demuxing a fragmented MP4 with -c copy, where moof/trun parsing is dominated by these readers, drops from 2.703 +/- 0.036 to 2.426 +/- 0.040 seconds of user time (-10%, n=30, p < 1e-4). >From aca4c53194cc03ee7d74699eab591417b12d188e Mon Sep 17 00:00:00 2001 From: Michael Niedermayer <[email protected]> Date: Wed, 26 Aug 2026 04:59:35 +0200 Subject: [PATCH 1/3] avformat/aviobuf: generate the multi-byte readers from one macro avio_rl16() through avio_rb64() are eight copies of the same two-step read differing only in the partial reads and shifts, so generate them from a macro taking those as parameters. The generated machine code is identical to the open-coded functions. --- libavformat/aviobuf.c | 75 ++++++++----------------------------------- 1 file changed, 14 insertions(+), 61 deletions(-) diff --git a/libavformat/aviobuf.c b/libavformat/aviobuf.c index 60a5d33304..7012b182f6 100644 --- a/libavformat/aviobuf.c +++ b/libavformat/aviobuf.c @@ -714,61 +714,22 @@ int avio_read_partial(AVIOContext *s, unsigned char *buf, int size) return len; } -unsigned int avio_rl16(AVIOContext *s) -{ - unsigned int val; - val = avio_r8(s); - val |= avio_r8(s) << 8; - return val; -} - -unsigned int avio_rl24(AVIOContext *s) -{ - unsigned int val; - val = avio_rl16(s); - val |= avio_r8(s) << 16; - return val; -} - -unsigned int avio_rl32(AVIOContext *s) -{ - unsigned int val; - val = avio_rl16(s); - val |= avio_rl16(s) << 16; - return val; -} - -uint64_t avio_rl64(AVIOContext *s) -{ - uint64_t val; - val = (uint64_t)avio_rl32(s); - val |= (uint64_t)avio_rl32(s) << 32; - return val; -} - -unsigned int avio_rb16(AVIOContext *s) -{ - unsigned int val; - val = avio_r8(s) << 8; - val |= avio_r8(s); - return val; -} - -unsigned int avio_rb24(AVIOContext *s) -{ - unsigned int val; - val = avio_rb16(s) << 8; - val |= avio_r8(s); - return val; -} -unsigned int avio_rb32(AVIOContext *s) -{ - unsigned int val; - val = avio_rb16(s) << 16; - val |= avio_rb16(s); - return val; +#define AVIO_READER(name, type, read1, shift1, read2, shift2) \ +type name(AVIOContext *s) \ +{ \ + type val = (type)read1(s) << shift1; \ + val |= (type)read2(s) << shift2; \ + return val; \ } +AVIO_READER(avio_rl16, unsigned int, avio_r8, 0, avio_r8, 8) +AVIO_READER(avio_rl24, unsigned int, avio_rl16, 0, avio_r8, 16) +AVIO_READER(avio_rl32, unsigned int, avio_rl16, 0, avio_rl16, 16) +AVIO_READER(avio_rl64, uint64_t, avio_rl32, 0, avio_rl32, 32) +AVIO_READER(avio_rb16, unsigned int, avio_r8, 8, avio_r8, 0) +AVIO_READER(avio_rb24, unsigned int, avio_rb16, 8, avio_r8, 0) +AVIO_READER(avio_rb32, unsigned int, avio_rb16, 16, avio_rb16, 0) +AVIO_READER(avio_rb64, uint64_t, avio_rb32, 32, avio_rb32, 0) int ff_get_line(AVIOContext *s, char *buf, int maxlen) { int i = 0; @@ -908,14 +869,6 @@ GET_STR16(be, avio_rb16) #undef GET_STR16 -uint64_t avio_rb64(AVIOContext *s) -{ - uint64_t val; - val = (uint64_t)avio_rb32(s) << 32; - val |= (uint64_t)avio_rb32(s); - return val; -} - uint64_t ffio_read_varlen(AVIOContext *bc){ uint64_t val = 0; int tmp; -- 2.52.0 >From ba0ed1601ae667c14e985a831a4bfab130092df2 Mon Sep 17 00:00:00 2001 From: Thierry Foucu <[email protected]> Date: Wed, 26 Aug 2026 04:59:48 +0200 Subject: [PATCH 2/3] avformat/aviobuf: read multi-byte values directly from the buffer The readers fetch their input through nested avio_r8() calls, paying a buffer bounds check per byte. Read the value with a single load when it is fully contained in the buffer and keep the byte-wise code as the refill fallback. Demuxing a fragmented MP4 with -c copy, where moof/trun parsing is dominated by these readers, drops from 2.703 +/- 0.036 to 2.426 +/- 0.040 seconds of user time (-10%, n=30, p < 1e-4). --- libavformat/aviobuf.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/libavformat/aviobuf.c b/libavformat/aviobuf.c index 7012b182f6..edbbc5ffd6 100644 --- a/libavformat/aviobuf.c +++ b/libavformat/aviobuf.c @@ -714,22 +714,28 @@ int avio_read_partial(AVIOContext *s, unsigned char *buf, int size) return len; } -#define AVIO_READER(name, type, read1, shift1, read2, shift2) \ +#define AVIO_READER(name, type, bytes, load, read1, shift1, read2, shift2) \ type name(AVIOContext *s) \ { \ - type val = (type)read1(s) << shift1; \ - val |= (type)read2(s) << shift2; \ + type val; \ + if (s->buf_end - s->buf_ptr >= bytes) { \ + val = load(s->buf_ptr); \ + s->buf_ptr += bytes; \ + } else { \ + val = (type)read1(s) << shift1; \ + val |= (type)read2(s) << shift2; \ + } \ return val; \ } -AVIO_READER(avio_rl16, unsigned int, avio_r8, 0, avio_r8, 8) -AVIO_READER(avio_rl24, unsigned int, avio_rl16, 0, avio_r8, 16) -AVIO_READER(avio_rl32, unsigned int, avio_rl16, 0, avio_rl16, 16) -AVIO_READER(avio_rl64, uint64_t, avio_rl32, 0, avio_rl32, 32) -AVIO_READER(avio_rb16, unsigned int, avio_r8, 8, avio_r8, 0) -AVIO_READER(avio_rb24, unsigned int, avio_rb16, 8, avio_r8, 0) -AVIO_READER(avio_rb32, unsigned int, avio_rb16, 16, avio_rb16, 0) -AVIO_READER(avio_rb64, uint64_t, avio_rb32, 32, avio_rb32, 0) +AVIO_READER(avio_rl16, unsigned int, 2, AV_RL16, avio_r8, 0, avio_r8, 8) +AVIO_READER(avio_rl24, unsigned int, 3, AV_RL24, avio_rl16, 0, avio_r8, 16) +AVIO_READER(avio_rl32, unsigned int, 4, AV_RL32, avio_rl16, 0, avio_rl16, 16) +AVIO_READER(avio_rl64, uint64_t, 8, AV_RL64, avio_rl32, 0, avio_rl32, 32) +AVIO_READER(avio_rb16, unsigned int, 2, AV_RB16, avio_r8, 8, avio_r8, 0) +AVIO_READER(avio_rb24, unsigned int, 3, AV_RB24, avio_rb16, 8, avio_r8, 0) +AVIO_READER(avio_rb32, unsigned int, 4, AV_RB32, avio_rb16, 16, avio_rb16, 0) +AVIO_READER(avio_rb64, uint64_t, 8, AV_RB64, avio_rb32, 32, avio_rb32, 0) int ff_get_line(AVIOContext *s, char *buf, int maxlen) { int i = 0; -- 2.52.0 >From c79624b8efbf4454e418386b53cd54850f33137f Mon Sep 17 00:00:00 2001 From: Michael Niedermayer <[email protected]> Date: Wed, 26 Aug 2026 04:59:48 +0200 Subject: [PATCH 3/3] avformat/file: use the bigger write buffer size for reads too Local file writes already use a 256k buffer instead of the 32k default; apply the same size to reads, cutting the number of read calls to an eighth. Reading through a bigger buffer was part of a larger performance patch by Thierry Foucu. Demuxing a 1.4G MOV with -c copy from an NVMe drive: 3.420 -> 3.182 seconds cold cache (-7%, n=12), 3.257 -> 3.184 seconds warm (-2.2%, n=15), both p < 1e-4. --- libavformat/file.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libavformat/file.c b/libavformat/file.c index 71d30a25c4..41d832da66 100644 --- a/libavformat/file.c +++ b/libavformat/file.c @@ -321,9 +321,9 @@ static int file_open(URLContext *h, const char *filename, int flags) if (c->pkt_size) { h->max_packet_size = c->pkt_size; } else { - /* Buffer writes more than the default 32k to improve throughput especially - * with networked file systems */ - if (!h->is_streamed && flags & AVIO_FLAG_WRITE) + /* Buffer reads and writes with more than the default 32k to improve + * throughput especially with networked file systems */ + if (!h->is_streamed) h->max_packet_size = 262144; } /* Disable per-packet flushing by default to improve throughput especially -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]