[gegl] buffer: add gegl-scratch allocator



commit b99032d799dda3436ffa8c1cc28f8b0d34fb965d
Author: Ell <ell_se yahoo com>
Date:   Sun Jan 6 06:40:25 2019 -0500

    buffer: add gegl-scratch allocator
    
    gegl-scratch is a fast memory allocator, suitable for small (up to
    a few megabytes), short-lived (usually, but not necessarily, bound
    to the current stack-frame) allocations.  Unlike alloca(), gimp-
    scratch doesn't use the stack, and is therefore safer, may outlive
    the current stackframe, and will also serve bigger requests, by
    falling-back to malloc().
    
    The allocator itself is very simple:  We keep a per-thread stack of
    cached memory blocks (allocated using the normal allocator).  When
    serving an allocation request, we simply pop the top block off the
    stack, and return it.  If the block is too small, we replace it
    with a big-enough block; if the stack is empty, we allocate a new
    block.  When the block is freed, we push it back to the top of the
    stack.  The idea is that the stacks will ultimately stabalize to
    contain blocks that can serve all the encountered allocation
    patterns, without needing to reisze any of the blocks; as a
    consequence, the amount of scratch memory allocated at any given
    time should really be kept to a minimum.
    
    Note that blocks may be allocated on one thread, and freed on
    another thread, without leaking memory, however, this is not an
    intended usage pattern, and may lead to suboptimal performance.
    
    This code was migrated from GIMP.

 gegl/Makefile.am                   |   1 +
 gegl/buffer/Makefile.am            |   3 +
 gegl/buffer/gegl-buffer.h          |   1 +
 gegl/buffer/gegl-scratch-private.h |  26 ++++
 gegl/buffer/gegl-scratch.c         | 237 +++++++++++++++++++++++++++++++++++++
 gegl/buffer/gegl-scratch.h         | 107 +++++++++++++++++
 gegl/gegl-stats.c                  |  17 ++-
 7 files changed, 390 insertions(+), 2 deletions(-)
---
diff --git a/gegl/Makefile.am b/gegl/Makefile.am
index 5866faf82..f8e069079 100644
--- a/gegl/Makefile.am
+++ b/gegl/Makefile.am
@@ -65,6 +65,7 @@ GEGL_introspectable_headers = \
        buffer/gegl-buffer-swap.h       \
        buffer/gegl-memory.h            \
        buffer/gegl-rectangle.h         \
+       buffer/gegl-scratch.h           \
        buffer/gegl-tile-backend.h              \
        buffer/gegl-tile-handler.h              \
        buffer/gegl-tile-source.h               \
diff --git a/gegl/buffer/Makefile.am b/gegl/buffer/Makefile.am
index f2b21c644..557aca99e 100644
--- a/gegl/buffer/Makefile.am
+++ b/gegl/buffer/Makefile.am
@@ -53,6 +53,7 @@ libbuffer_la_SOURCES = \
     gegl-sampler-nearest.c     \
     gegl-sampler-nohalo.c       \
     gegl-sampler-lohalo.c       \
+    gegl-scratch.c             \
     gegl-tile.c                        \
     gegl-tile-source.c         \
     gegl-tile-storage.c                \
@@ -90,6 +91,8 @@ libbuffer_la_SOURCES = \
     gegl-sampler-nearest.h     \
     gegl-sampler-nohalo.h       \
     gegl-sampler-lohalo.h       \
+    gegl-scratch.h             \
+    gegl-scratch-private.h     \
     gegl-tile.h                        \
     gegl-tile-source.h         \
     gegl-tile-storage.h                \
diff --git a/gegl/buffer/gegl-buffer.h b/gegl/buffer/gegl-buffer.h
index f962bc1a8..88fd069e3 100644
--- a/gegl/buffer/gegl-buffer.h
+++ b/gegl/buffer/gegl-buffer.h
@@ -736,6 +736,7 @@ gegl_buffer_flush_ext (GeglBuffer *buffer, const GeglRectangle *rect);
 #include "gegl-buffer-iterator.h"
 #include "gegl-rectangle.h"
 #include "gegl-memory.h"
+#include "gegl-scratch.h"
 
 
 GType gegl_buffer_get_type  (void) G_GNUC_CONST;
diff --git a/gegl/buffer/gegl-scratch-private.h b/gegl/buffer/gegl-scratch-private.h
new file mode 100644
index 000000000..98a2b4819
--- /dev/null
+++ b/gegl/buffer/gegl-scratch-private.h
@@ -0,0 +1,26 @@
+/* This file is part of GEGL
+ *
+ * GEGL is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * GEGL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GEGL; if not, see <https://www.gnu.org/licenses/>.
+ *
+ * Copyright 2019 Ell
+ */
+
+#ifndef __GEGL_SCRATCH_PRIVATE_H__
+#define __GEGL_SCRATCH_PRIVATE_H__
+
+
+guint64   gegl_scratch_get_total (void);
+
+
+#endif /* __GEGL_SCRATCH_PRIVATE_H__ */
diff --git a/gegl/buffer/gegl-scratch.c b/gegl/buffer/gegl-scratch.c
new file mode 100644
index 000000000..f3225b487
--- /dev/null
+++ b/gegl/buffer/gegl-scratch.c
@@ -0,0 +1,237 @@
+/* This file is part of GEGL
+ *
+ * GEGL is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * GEGL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GEGL; if not, see <https://www.gnu.org/licenses/>.
+ *
+ * Copyright 2019 Ell
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <glib-object.h>
+
+#include "gegl-memory-private.h"
+#include "gegl-scratch.h"
+#include "gegl-scratch-private.h"
+
+
+#define GEGL_SCRATCH_ALIGNMENT         GEGL_ALIGN
+#define GEGL_SCRATCH_MAX_BLOCK_SIZE    (1 << 20)
+#define GEGL_SCRATCH_BLOCK_DATA_OFFSET ((sizeof (GeglScratchBlockHeader) + \
+                                         (GEGL_SCRATCH_ALIGNMENT - 1))   / \
+                                        GEGL_SCRATCH_ALIGNMENT           * \
+                                        GEGL_SCRATCH_ALIGNMENT)
+
+
+G_STATIC_ASSERT (GEGL_SCRATCH_ALIGNMENT <= G_MAXUINT8);
+
+
+/*  private types  */
+
+typedef struct _GeglScratchBlockHeader GeglScratchBlockHeader;
+typedef struct _GeglScratchBlock       GeglScratchBlock;
+typedef struct _GeglScratchContext     GeglScratchContext;
+
+struct _GeglScratchBlockHeader
+{
+  GeglScratchContext *context;
+  gsize               size;
+  guint8              offset;
+};
+
+struct _GeglScratchBlock
+{
+  GeglScratchBlockHeader header;
+  guint8                 padding[GEGL_SCRATCH_BLOCK_DATA_OFFSET -
+                                 sizeof (GeglScratchBlockHeader)];
+  guint8                 data[];
+};
+
+struct _GeglScratchContext
+{
+  GeglScratchBlock **blocks;
+  gint               n_blocks;
+  gint               n_available_blocks;
+};
+
+
+/*  local function prototypes  */
+
+static GeglScratchContext * gegl_scratch_context_new  (void);
+static void                 gegl_scratch_context_free (GeglScratchContext *context);
+
+static GeglScratchBlock   * gegl_scratch_block_new    (GeglScratchContext *context,
+                                                       gsize               size);
+static void                 gegl_scratch_block_free   (GeglScratchBlock   *block);
+
+
+/*  local variables  */
+
+GPrivate                 gegl_scratch_context =
+  G_PRIVATE_INIT ((GDestroyNotify) gegl_scratch_context_free);
+static volatile guintptr gegl_scratch_total;
+
+
+/*  private functions  */
+
+GeglScratchContext *
+gegl_scratch_context_new (void)
+{
+  return g_slice_new0 (GeglScratchContext);
+}
+
+void
+gegl_scratch_context_free (GeglScratchContext *context)
+{
+  gint i;
+
+  for (i = 0; i < context->n_available_blocks; i++)
+    gegl_scratch_block_free (context->blocks[i]);
+
+  g_free (context->blocks);
+
+  g_slice_free (GeglScratchContext, context);
+}
+
+GeglScratchBlock *
+gegl_scratch_block_new (GeglScratchContext *context,
+                        gsize               size)
+{
+  GeglScratchBlock *block;
+  gint              offset;
+
+  g_atomic_pointer_add (&gegl_scratch_total, +size);
+
+  block = g_malloc ((GEGL_SCRATCH_ALIGNMENT - 1) +
+                    sizeof (GeglScratchBlock)    +
+                    size);
+
+  offset  = GEGL_SCRATCH_ALIGNMENT -
+            ((guintptr) block) % GEGL_SCRATCH_ALIGNMENT;
+  offset %= GEGL_SCRATCH_ALIGNMENT;
+
+  block = (GeglScratchBlock *) ((guint8 *) block + offset);
+
+  block->header.context = context;
+  block->header.size    = size;
+  block->header.offset  = offset;
+
+  return block;
+}
+
+void
+gegl_scratch_block_free (GeglScratchBlock *block)
+{
+  g_atomic_pointer_add (&gegl_scratch_total, -block->header.size);
+
+  g_free ((guint8 *) block - block->header.offset);
+}
+
+
+/*  public functions  */
+
+gpointer
+gegl_scratch_alloc (gsize size)
+{
+  GeglScratchContext *context;
+  GeglScratchBlock   *block;
+
+  if (G_UNLIKELY (! size))
+    return NULL;
+
+  if (G_UNLIKELY (size > GEGL_SCRATCH_MAX_BLOCK_SIZE))
+    {
+      block = gegl_scratch_block_new (NULL, size);
+
+      return block->data;
+    }
+
+  context = g_private_get (&gegl_scratch_context);
+
+  if (G_UNLIKELY (! context))
+    {
+      context = gegl_scratch_context_new ();
+
+      g_private_set (&gegl_scratch_context, context);
+    }
+
+  if (G_LIKELY (context->n_available_blocks))
+    {
+      block = context->blocks[--context->n_available_blocks];
+
+      if (G_LIKELY (size <= block->header.size))
+        return block->data;
+
+      gegl_scratch_block_free (block);
+    }
+
+  block = gegl_scratch_block_new (context, size);
+
+  return block->data;
+}
+
+gpointer
+gegl_scratch_alloc0 (gsize size)
+{
+  gpointer ptr;
+
+  if (G_UNLIKELY (! size))
+    return NULL;
+
+  ptr = gegl_scratch_alloc (size);
+
+  memset (ptr, 0, size);
+
+  return ptr;
+}
+
+void
+gegl_scratch_free (gpointer ptr)
+{
+  GeglScratchContext *context;
+  GeglScratchBlock   *block;
+
+  if (G_UNLIKELY (! ptr))
+    return;
+
+  context = g_private_get (&gegl_scratch_context);
+  block   = (GeglScratchBlock *) ((guint8 *) ptr -
+                                  GEGL_SCRATCH_BLOCK_DATA_OFFSET);
+
+  if (G_UNLIKELY (block->header.context != context))
+    {
+      gegl_scratch_block_free (block);
+
+      return;
+    }
+
+  if (G_UNLIKELY (context->n_available_blocks == context->n_blocks))
+    {
+      context->n_blocks = MAX (2 * context->n_blocks, 1);
+      context->blocks   = g_renew (GeglScratchBlock *, context->blocks,
+                                   context->n_blocks);
+    }
+
+  context->blocks[context->n_available_blocks++] = block;
+}
+
+
+/*   public functions (stats)  */
+
+guint64
+gegl_scratch_get_total (void)
+{
+  return gegl_scratch_total;
+}
diff --git a/gegl/buffer/gegl-scratch.h b/gegl/buffer/gegl-scratch.h
new file mode 100644
index 000000000..74b2af435
--- /dev/null
+++ b/gegl/buffer/gegl-scratch.h
@@ -0,0 +1,107 @@
+/* This file is part of GEGL
+ *
+ * GEGL is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * GEGL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GEGL; if not, see <https://www.gnu.org/licenses/>.
+ *
+ * Copyright 2019 Ell
+ */
+
+#ifndef __GEGL_SCRATCH_H__
+#define __GEGL_SCRATCH_H__
+
+
+/**
+ * gegl_scratch_alloc: (skip)
+ * @size: the number of bytes to allocte.
+ *
+ * Allocates @size bytes of scratch memory.
+ * If @size is 0 it returns NULL.
+ *
+ * Returns a pointer to the allocated memory.
+ */
+gpointer   gegl_scratch_alloc  (gsize    size) G_GNUC_MALLOC;
+
+/**
+ * gegl_scratch_alloc0: (skip)
+ * @size: the number of bytes to allocte.
+ *
+ * Allocates @size bytes of scratch memory, initialized to zero.
+ * If @size is 0 it returns NULL.
+ *
+ * Returns a pointer to the allocated memory.
+ */
+gpointer   gegl_scratch_alloc0 (gsize    size) G_GNUC_MALLOC;
+
+/**
+ * gegl_scratch_free: (skip)
+ * @ptr: the memory to free.
+ *
+ * Frees the memory pointed to by @ptr.
+ * If @ptr is NULL, does nothing.
+ *
+ * The memory must have been allocated using one of the scratch-memory
+ * allocation functions.
+ */
+void       gegl_scratch_free   (gpointer ptr);
+
+
+#define _GEGL_SCRATCH_MUL(x, y) \
+  (G_LIKELY ((y) == 0 || (x) <= G_MAXSIZE / (y)) ? (x) * (y) : G_MAXSIZE)
+
+/**
+ * gegl_scratch_new: (skip)
+ * @type: the type of the elements to allocate
+ * @n: the number of elements to allocate
+ *
+ * Allocates @n elements of type @type using scratch memory.
+ * The returned pointer is cast to a pointer to the given type.
+ * If @n is 0 it returns %NULL.
+ * Care is taken to avoid overflow when calculating the size of
+ * the allocated block.
+ *
+ * Since the returned pointer is already cast to the right type,
+ * it is normally unnecessary to cast it explicitly, and doing
+ * so might hide memory allocation errors.
+ *
+ * Returns: a pointer to the allocated memory, cast to a pointer
+ * to @type.
+ */
+#define gegl_scratch_new(type, n)                                   \
+  ((type *) (gegl_scratch_alloc (_GEGL_SCRATCH_MUL (sizeof (type),  \
+                                                    (gsize) (n)))))
+
+/**
+ * gegl_scratch_new0: (skip)
+ * @type: the type of the elements to allocate
+ * @n: the number of elements to allocate
+ *
+ * Allocates @n elements of type @type using scratch memory,
+ * initialized to 0.
+ * The returned pointer is cast to a pointer to the given type.
+ * If @n is 0 it returns %NULL.
+ * Care is taken to avoid overflow when calculating the size of
+ * the allocated block.
+ *
+ * Since the returned pointer is already cast to the right type,
+ * it is normally unnecessary to cast it explicitly, and doing
+ * so might hide memory allocation errors.
+ *
+ * Returns: a pointer to the allocated memory, cast to a pointer
+ * to @type.
+ */
+#define gegl_scratch_new0(type, n)                                  \
+  ((type *) (gegl_scratch_alloc0 (_GEGL_SCRATCH_MUL (sizeof (type), \
+                                                    (gsize) (n)))))
+
+
+#endif /* __GEGL_SCRATCH_H__ */
diff --git a/gegl/gegl-stats.c b/gegl/gegl-stats.c
index dafdb3045..33ce25b8e 100644
--- a/gegl/gegl-stats.c
+++ b/gegl/gegl-stats.c
@@ -23,9 +23,10 @@
 #include "gegl.h"
 #include "gegl-types-internal.h"
 #include "buffer/gegl-buffer-types.h"
+#include "buffer/gegl-scratch-private.h"
 #include "buffer/gegl-tile-handler-cache.h"
-#include "buffer/gegl-tile-handler-zoom.h"
 #include "buffer/gegl-tile-backend-swap.h"
+#include "buffer/gegl-tile-handler-zoom.h"
 #include "gegl-stats.h"
 
 
@@ -48,7 +49,8 @@ enum
   PROP_SWAP_READ_TOTAL,
   PROP_SWAP_WRITING,
   PROP_SWAP_WRITE_TOTAL,
-  PROP_ZOOM_TOTAL
+  PROP_ZOOM_TOTAL,
+  PROP_SCRATCH_TOTAL
 };
 
 
@@ -196,6 +198,13 @@ gegl_stats_class_init (GeglStatsClass *klass)
                                                         "Total size of data processed by the zoom tile 
handler",
                                                         0, G_MAXUINT64, 0,
                                                         G_PARAM_READABLE));
+
+  g_object_class_install_property (object_class, PROP_SCRATCH_TOTAL,
+                                   g_param_spec_uint64 ("scratch-total",
+                                                        "Scratch total",
+                                                        "Total size of scratch memory",
+                                                        0, G_MAXUINT64, 0,
+                                                        G_PARAM_READABLE));
 }
 
 static void
@@ -293,6 +302,10 @@ gegl_stats_get_property (GObject    *object,
         g_value_set_uint64 (value, gegl_tile_handler_zoom_get_total ());
         break;
 
+      case PROP_SCRATCH_TOTAL:
+        g_value_set_uint64 (value, gegl_scratch_get_total ());
+        break;
+
       default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
         break;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]