[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]
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.