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]

Reply via email to