diff mbox

[19/19] buffer: allow a buffer to shrink gracefully

Message ID 1446203414-4013-20-git-send-email-kraxel@redhat.com
State New
Headers show

Commit Message

Gerd Hoffmann Oct. 30, 2015, 11:10 a.m. UTC
From: Peter Lieven <pl@kamp.de>

the idea behind this patch is to allow the buffer to shrink, but
make this a seldom operation. The buffers average size is measured
exponentionally smoothed with am alpha of 1/128.

Signed-off-by: Peter Lieven <pl@kamp.de>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 include/qemu/buffer.h |  1 +
 util/buffer.c         | 34 ++++++++++++++++++++++++++++------
 2 files changed, 29 insertions(+), 6 deletions(-)

Comments

Daniel P. Berrangé Oct. 30, 2015, 12:33 p.m. UTC | #1
On Fri, Oct 30, 2015 at 12:10:14PM +0100, Gerd Hoffmann wrote:
> From: Peter Lieven <pl@kamp.de>
> 
> the idea behind this patch is to allow the buffer to shrink, but
> make this a seldom operation. The buffers average size is measured
> exponentionally smoothed with am alpha of 1/128.

s/am/an/

> 
> Signed-off-by: Peter Lieven <pl@kamp.de>
> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
> ---
>  include/qemu/buffer.h |  1 +
>  util/buffer.c         | 34 ++++++++++++++++++++++++++++------
>  2 files changed, 29 insertions(+), 6 deletions(-)


> diff --git a/util/buffer.c b/util/buffer.c
> index fe5a44e..5461f86 100644
> --- a/util/buffer.c
> +++ b/util/buffer.c
> @@ -23,6 +23,7 @@
>  
>  #define BUFFER_MIN_INIT_SIZE     4096
>  #define BUFFER_MIN_SHRINK_SIZE  65536
> +#define BUFFER_AVG_SIZE_SHIFT       7
>  
>  static size_t buffer_req_size(Buffer *buffer, size_t len)
>  {
> @@ -37,6 +38,11 @@ static void buffer_adj_size(Buffer *buffer, size_t len)
>      buffer->buffer = g_realloc(buffer->buffer, buffer->capacity);
>      trace_buffer_resize(buffer->name ?: "unnamed",
>                          old, buffer->capacity);
> +
> +    /* make it even harder for the buffer to shrink, reset average size
> +     * to currenty capacity if it is larger than the average. */

s/currenty/current/

Reviewed-by: Daniel P. Berrange <berrange@redhat.com>


Regards,
Daniel
Peter Lieven Nov. 3, 2015, 7:10 a.m. UTC | #2
Am 30.10.2015 um 12:10 schrieb Gerd Hoffmann:
> From: Peter Lieven <pl@kamp.de>
>
> the idea behind this patch is to allow the buffer to shrink, but
> make this a seldom operation. The buffers average size is measured
> exponentionally smoothed with am alpha of 1/128.
>
> Signed-off-by: Peter Lieven <pl@kamp.de>
> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
> ---
>   include/qemu/buffer.h |  1 +
>   util/buffer.c         | 34 ++++++++++++++++++++++++++++------
>   2 files changed, 29 insertions(+), 6 deletions(-)
>
> diff --git a/include/qemu/buffer.h b/include/qemu/buffer.h
> index 0a69b3a..dead9b7 100644
> --- a/include/qemu/buffer.h
> +++ b/include/qemu/buffer.h
> @@ -37,6 +37,7 @@ struct Buffer {
>       char *name;
>       size_t capacity;
>       size_t offset;
> +    uint64_t avg_size;
>       uint8_t *buffer;
>   };
>   
> diff --git a/util/buffer.c b/util/buffer.c
> index fe5a44e..5461f86 100644
> --- a/util/buffer.c
> +++ b/util/buffer.c
> @@ -23,6 +23,7 @@
>   
>   #define BUFFER_MIN_INIT_SIZE     4096
>   #define BUFFER_MIN_SHRINK_SIZE  65536
> +#define BUFFER_AVG_SIZE_SHIFT       7
>   
>   static size_t buffer_req_size(Buffer *buffer, size_t len)
>   {
> @@ -37,6 +38,11 @@ static void buffer_adj_size(Buffer *buffer, size_t len)
>       buffer->buffer = g_realloc(buffer->buffer, buffer->capacity);
>       trace_buffer_resize(buffer->name ?: "unnamed",
>                           old, buffer->capacity);
> +
> +    /* make it even harder for the buffer to shrink, reset average size
> +     * to currenty capacity if it is larger than the average. */
> +    buffer->avg_size = MAX(buffer->avg_size,
> +                           buffer->capacity << BUFFER_AVG_SIZE_SHIFT);
>   }
>   
>   void buffer_init(Buffer *buffer, const char *name, ...)
> @@ -48,16 +54,30 @@ void buffer_init(Buffer *buffer, const char *name, ...)
>       va_end(ap);
>   }
>   
> +static uint64_t buffer_get_avg_size(Buffer *buffer)
> +{
> +    return buffer->avg_size >> BUFFER_AVG_SIZE_SHIFT;
> +}
> +
>   void buffer_shrink(Buffer *buffer)
>   {
> -    /*
> -     * Only shrink in case the used size is *much* smaller than the
> -     * capacity, to avoid bumping up & down the buffers all the time.
> +    size_t new;
> +
> +    /* Calculate the average size of the buffer as
> +     * avg_size = avg_size * ( 1 - a ) + required_size * a
> +     * where a is 1 / 2 ^ QIO_BUFFER_AVG_SIZE_SHIFT. */
> +    buffer->avg_size *= (1 << BUFFER_AVG_SIZE_SHIFT) - 1;
> +    buffer->avg_size >>= BUFFER_AVG_SIZE_SHIFT;
> +    buffer->avg_size += buffer_req_size(buffer, 0);
> +
> +    /* And then only shrink if the average size of the buffer is much
> +     * too big, to avoid bumping up & down the buffers all the time.
>        * realloc() isn't exactly cheap ...
>        */
> -    if (buffer->offset < (buffer->capacity >> 3) &&
> -        buffer->capacity > BUFFER_MIN_SHRINK_SIZE) {
> -        return;
> +    new = buffer_req_size(buffer, buffer_get_avg_size(buffer));
> +    if (new < buffer->capacity >> 3 &&
> +        new >= BUFFER_MIN_SHRINK_SIZE) {
> +        buffer_adj_size(buffer, buffer_get_avg_size(buffer));
>       }
>   
>       buffer_adj_size(buffer, 0);
> @@ -83,6 +103,7 @@ uint8_t *buffer_end(Buffer *buffer)
>   void buffer_reset(Buffer *buffer)
>   {
>       buffer->offset = 0;
> +    buffer_shrink(buffer);
>   }
>   
>   void buffer_free(Buffer *buffer)
> @@ -107,6 +128,7 @@ void buffer_advance(Buffer *buffer, size_t len)
>       memmove(buffer->buffer, buffer->buffer + len,
>               (buffer->offset - len));
>       buffer->offset -= len;
> +    buffer_shrink(buffer);
>   }
>   
>   void buffer_move_empty(Buffer *to, Buffer *from)

This isn't the last version of this patch. Please have a look at:

https://github.com/plieven/qemu/commit/e599748ab1ef381d4b1c88bf1ea1454dd89353fb

Peter
diff mbox

Patch

diff --git a/include/qemu/buffer.h b/include/qemu/buffer.h
index 0a69b3a..dead9b7 100644
--- a/include/qemu/buffer.h
+++ b/include/qemu/buffer.h
@@ -37,6 +37,7 @@  struct Buffer {
     char *name;
     size_t capacity;
     size_t offset;
+    uint64_t avg_size;
     uint8_t *buffer;
 };
 
diff --git a/util/buffer.c b/util/buffer.c
index fe5a44e..5461f86 100644
--- a/util/buffer.c
+++ b/util/buffer.c
@@ -23,6 +23,7 @@ 
 
 #define BUFFER_MIN_INIT_SIZE     4096
 #define BUFFER_MIN_SHRINK_SIZE  65536
+#define BUFFER_AVG_SIZE_SHIFT       7
 
 static size_t buffer_req_size(Buffer *buffer, size_t len)
 {
@@ -37,6 +38,11 @@  static void buffer_adj_size(Buffer *buffer, size_t len)
     buffer->buffer = g_realloc(buffer->buffer, buffer->capacity);
     trace_buffer_resize(buffer->name ?: "unnamed",
                         old, buffer->capacity);
+
+    /* make it even harder for the buffer to shrink, reset average size
+     * to currenty capacity if it is larger than the average. */
+    buffer->avg_size = MAX(buffer->avg_size,
+                           buffer->capacity << BUFFER_AVG_SIZE_SHIFT);
 }
 
 void buffer_init(Buffer *buffer, const char *name, ...)
@@ -48,16 +54,30 @@  void buffer_init(Buffer *buffer, const char *name, ...)
     va_end(ap);
 }
 
+static uint64_t buffer_get_avg_size(Buffer *buffer)
+{
+    return buffer->avg_size >> BUFFER_AVG_SIZE_SHIFT;
+}
+
 void buffer_shrink(Buffer *buffer)
 {
-    /*
-     * Only shrink in case the used size is *much* smaller than the
-     * capacity, to avoid bumping up & down the buffers all the time.
+    size_t new;
+
+    /* Calculate the average size of the buffer as
+     * avg_size = avg_size * ( 1 - a ) + required_size * a
+     * where a is 1 / 2 ^ QIO_BUFFER_AVG_SIZE_SHIFT. */
+    buffer->avg_size *= (1 << BUFFER_AVG_SIZE_SHIFT) - 1;
+    buffer->avg_size >>= BUFFER_AVG_SIZE_SHIFT;
+    buffer->avg_size += buffer_req_size(buffer, 0);
+
+    /* And then only shrink if the average size of the buffer is much
+     * too big, to avoid bumping up & down the buffers all the time.
      * realloc() isn't exactly cheap ...
      */
-    if (buffer->offset < (buffer->capacity >> 3) &&
-        buffer->capacity > BUFFER_MIN_SHRINK_SIZE) {
-        return;
+    new = buffer_req_size(buffer, buffer_get_avg_size(buffer));
+    if (new < buffer->capacity >> 3 &&
+        new >= BUFFER_MIN_SHRINK_SIZE) {
+        buffer_adj_size(buffer, buffer_get_avg_size(buffer));
     }
 
     buffer_adj_size(buffer, 0);
@@ -83,6 +103,7 @@  uint8_t *buffer_end(Buffer *buffer)
 void buffer_reset(Buffer *buffer)
 {
     buffer->offset = 0;
+    buffer_shrink(buffer);
 }
 
 void buffer_free(Buffer *buffer)
@@ -107,6 +128,7 @@  void buffer_advance(Buffer *buffer, size_t len)
     memmove(buffer->buffer, buffer->buffer + len,
             (buffer->offset - len));
     buffer->offset -= len;
+    buffer_shrink(buffer);
 }
 
 void buffer_move_empty(Buffer *to, Buffer *from)