Speed up the oggdec filter and avoid wasting tons of memory.
authorAndre Noll <maan@systemlinux.org>
Wed, 9 Feb 2011 17:18:17 +0000 (18:18 +0100)
committerAndre Noll <maan@systemlinux.org>
Sun, 13 Feb 2011 22:15:43 +0000 (23:15 +0100)
Calls to ov_read appear to return at most 4K, no matter how much data
was requested. We allocated 64K per output chunk, so 60K of that space
is wasted. On the other hand we need large output buffers in order to
not sacrifice performance when para_filter decodes to stdout.

Fix this flaw by increasing the oggdec output buffer size from 64K to
640K, calling ov_read() until the output buffer is full or there is
nothing left to read, and then reallocating the buffer to the amount
of bytes actually read.

Assuming CD audio, the 640K output buffer size roughly matches
the decoded size of the 32K input buffer used by the stdin task,
so each input buffer corresponds roughly to one output buffer. The
patched version performs almost identically to the oggdec reference
implementation while previous versions were up to a factor of 2 slower.

oggdec_filter.c

index 94c284a..44d299f 100644 (file)
@@ -113,7 +113,7 @@ static void ogg_close(struct filter_node *fn)
        fn->private_data = NULL;
 }
 
        fn->private_data = NULL;
 }
 
-#define OGGDEC_OUTPUT_CHUNK_SIZE (64 * 1024)
+#define OGGDEC_OUTPUT_CHUNK_SIZE (640 * 1024)
 
 static int oggdec_execute(struct btr_node *btrn, const char *cmd, char **result)
 {
 
 static int oggdec_execute(struct btr_node *btrn, const char *cmd, char **result)
 {
@@ -200,6 +200,8 @@ static void ogg_post_select(__a_unused struct sched *s, struct task *t)
        struct private_oggdec_data *pod = fn->private_data;
        struct btr_node *btrn = fn->btrn;
        int ret, ns;
        struct private_oggdec_data *pod = fn->private_data;
        struct btr_node *btrn = fn->btrn;
        int ret, ns;
+       char *out;
+       ssize_t read_ret, have;
 
        pod->converted = 0;
        t->error = 0;
 
        pod->converted = 0;
        t->error = 0;
@@ -214,27 +216,36 @@ static void ogg_post_select(__a_unused struct sched *s, struct task *t)
                if (ret <= 0)
                        goto out;
        }
                if (ret <= 0)
                        goto out;
        }
-       for (;;) {
-               char *out = para_malloc(OGGDEC_OUTPUT_CHUNK_SIZE);
-               ssize_t read_ret = ov_read(pod->vf, out, OGGDEC_OUTPUT_CHUNK_SIZE,
+       out = para_malloc(OGGDEC_OUTPUT_CHUNK_SIZE);
+       for (have = 0;;) {
+               read_ret = ov_read(pod->vf, out + have,
+                       OGGDEC_OUTPUT_CHUNK_SIZE - have,
                        ENDIAN, 2 /* 16 bit */, 1 /* signed */, NULL);
                btr_consume(btrn, pod->converted);
                pod->converted = 0;
                if (read_ret <= 0)
                        ENDIAN, 2 /* 16 bit */, 1 /* signed */, NULL);
                btr_consume(btrn, pod->converted);
                pod->converted = 0;
                if (read_ret <= 0)
-                       free(out);
-               ret = ns;
-               if (read_ret == OV_HOLE) /* avoid buffer underruns */
-                       fn->min_iqs = 9000;
-               if (read_ret == 0 || read_ret == OV_HOLE)
-                       goto out;
-               ret = -E_OGGDEC_BADLINK;
-               if (read_ret < 0)
-                       goto out;
-               btr_add_output(out, read_ret, btrn);
+                       break;
+               have += read_ret;
+               if (have >= OGGDEC_OUTPUT_CHUNK_SIZE)
+                       break;
+       }
+       if (have == 0)
+               free(out);
+       else if (have < OGGDEC_OUTPUT_CHUNK_SIZE)
+               out = para_realloc(out, have);
+       if (have > 0) {
+               btr_add_output(out, have, btrn);
                fn->min_iqs = 0;
                fn->min_iqs = 0;
-               if (btr_get_output_queue_size(btrn) > 128 * 1024)
-                       return; /* enough data for the moment */
        }
        }
+       ret = ns;
+       if (read_ret == OV_HOLE) /* avoid buffer underruns */
+               fn->min_iqs = 9000;
+       if (read_ret == 0 || read_ret == OV_HOLE)
+               goto out;
+       ret = -E_OGGDEC_BADLINK;
+       if (read_ret < 0)
+               goto out;
+       ret = 0;
 out:
        if (ret < 0) {
                t->error = ret;
 out:
        if (ret < 0) {
                t->error = ret;