[gimp/gimp-2-10] app: add gimp-scratch allocator



commit 8593eb88fdeb8233d88f54b8ec49a2914133475a
Author: Ell <ell_se yahoo com>
Date:   Sat Dec 1 05:18:24 2018 -0500

    app: add gimp-scratch allocator
    
    gimp-scratch is a fast memory allocator (on the order of magnitude
    of alloca()), suitable for small (up to a few megabytes), short-
    lived (usually, bound to the current stack-frame) allocations.
    Unlike alloca(), gimp-scratch doesn't use the stack, and is
    therefore safer, 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.  When the block is freed, we push it back to
    the top of the stack (note that even though each thread uses a
    separate stack, blocks can be migrated between threads, i.e.,
    allocated on one thread, and freed on another thread, although this
    is not really an intended usage pattern.)  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.
    
    (cherry picked from commit a8a86552851f23b2f976e1a6f26982cf2fcb7f9d)

 app/core/Makefile.am    |   2 +
 app/core/gimp-scratch.c |  90 ++++++++++++++++++++++++++
 app/core/gimp-scratch.h | 164 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 256 insertions(+)
---
diff --git a/app/core/Makefile.am b/app/core/Makefile.am
index b0bc375838..48382f4747 100644
--- a/app/core/Makefile.am
+++ b/app/core/Makefile.am
@@ -70,6 +70,8 @@ libappcore_a_sources = \
        gimp-parallel.h                         \
        gimp-parasites.c                        \
        gimp-parasites.h                        \
+       gimp-scratch.c                          \
+       gimp-scratch.h                          \
        gimp-spawn.c                            \
        gimp-spawn.h                            \
        gimp-tags.c                             \
diff --git a/app/core/gimp-scratch.c b/app/core/gimp-scratch.c
new file mode 100644
index 0000000000..4dcef4ed5c
--- /dev/null
+++ b/app/core/gimp-scratch.c
@@ -0,0 +1,90 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimp-scratch.c
+ * Copyright (C) 2018 Ell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gegl.h>
+
+#include "core-types.h"
+
+#include "gimp-scratch.h"
+
+
+/*  local variables  */
+
+GPrivate                 gimp_scratch_context =
+  G_PRIVATE_INIT ((GDestroyNotify) gimp_scratch_context_free);
+static volatile guintptr gimp_scratch_total;
+
+
+/*  private functions  */
+
+
+GimpScratchBlock *
+gimp_scratch_block_new (gsize size)
+{
+  GimpScratchBlock *block;
+  gint              offset;
+
+  g_atomic_pointer_add (&gimp_scratch_total, +size);
+
+  block = g_malloc ((GIMP_SCRATCH_ALIGNMENT - 1) +
+                    sizeof (GimpScratchBlock)    +
+                    size);
+
+  offset  = GIMP_SCRATCH_ALIGNMENT -
+            ((guintptr) block) % GIMP_SCRATCH_ALIGNMENT;
+  offset %= GIMP_SCRATCH_ALIGNMENT;
+
+  block = (GimpScratchBlock *) ((guint8 *) block + offset);
+
+  block->size   = size;
+  block->offset = offset;
+
+  return block;
+}
+
+void
+gimp_scratch_block_free (GimpScratchBlock *block)
+{
+  g_atomic_pointer_add (&gimp_scratch_total, -block->size);
+
+  g_free ((guint8 *) block - block->offset);
+}
+
+GimpScratchContext *
+gimp_scratch_context_new (void)
+{
+  return g_slice_new0 (GimpScratchContext);
+}
+
+void
+gimp_scratch_context_free (GimpScratchContext *context)
+{
+  gint i;
+
+  for (i = 0; i < context->n_available_blocks; i++)
+    gimp_scratch_block_free (context->blocks[i]);
+
+  g_free (context->blocks);
+
+  g_slice_free (GimpScratchContext, context);
+}
diff --git a/app/core/gimp-scratch.h b/app/core/gimp-scratch.h
new file mode 100644
index 0000000000..e47f372a56
--- /dev/null
+++ b/app/core/gimp-scratch.h
@@ -0,0 +1,164 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimp-scratch.h
+ * Copyright (C) 2018 Ell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_SCRATCH_H__
+#define __GIMP_SCRATCH_H__
+
+
+#define GIMP_SCRATCH_ALIGNMENT      16
+#define GIMP_SCRATCH_MAX_BLOCK_SIZE (1 << 20)
+
+
+/*  private types  */
+
+typedef struct
+{
+  gsize  size;
+  guint8 offset;
+  guint8 padding[GIMP_SCRATCH_ALIGNMENT - (sizeof (gsize) + 1)];
+  guint8 data[];
+} GimpScratchBlock;
+
+typedef struct
+{
+  GimpScratchBlock **blocks;
+  gint               n_blocks;
+  gint               n_available_blocks;
+} GimpScratchContext;
+
+
+/*  private variables  */
+
+extern GPrivate gimp_scratch_context;
+
+
+/*  private functions  */
+
+GimpScratchBlock   * gimp_scratch_block_new    (gsize               size);
+void                 gimp_scratch_block_free   (GimpScratchBlock   *block);
+
+GimpScratchContext * gimp_scratch_context_new  (void);
+void                 gimp_scratch_context_free (GimpScratchContext *context);
+
+
+/*  public functions  */
+
+
+inline gpointer
+gimp_scratch_alloc (gsize size)
+{
+  GimpScratchContext *context;
+  GimpScratchBlock   *block;
+
+  if (! size)
+    return NULL;
+
+  if (size > GIMP_SCRATCH_MAX_BLOCK_SIZE)
+    {
+      block       = gimp_scratch_block_new (size);
+      block->size = 0;
+
+      return block->data;
+    }
+
+  context = g_private_get (&gimp_scratch_context);
+
+  if (! context)
+    {
+      context = gimp_scratch_context_new ();
+
+      g_private_set (&gimp_scratch_context, context);
+    }
+
+  if (context->n_available_blocks)
+    {
+      block = context->blocks[--context->n_available_blocks];
+
+      if (block->size < size)
+        {
+          gimp_scratch_block_free (block);
+
+          block = gimp_scratch_block_new (size);
+
+          context->blocks[context->n_available_blocks] = block;
+        }
+    }
+  else
+    {
+      block = gimp_scratch_block_new (size);
+    }
+
+  return block->data;
+}
+
+inline gpointer
+gimp_scratch_alloc0 (gsize size)
+{
+  gpointer ptr;
+
+  if (! size)
+    return NULL;
+
+  ptr = gimp_scratch_alloc (size);
+
+  memset (ptr, 0, size);
+
+  return ptr;
+}
+
+inline void
+gimp_scratch_free (gpointer ptr)
+{
+  GimpScratchContext *context;
+  GimpScratchBlock   *block;
+
+  if (! ptr)
+    return;
+
+  block = (GimpScratchBlock *) ((guint8 *) ptr - GIMP_SCRATCH_ALIGNMENT);
+
+  if (! block->size)
+    {
+      gimp_scratch_block_free (block);
+
+      return;
+    }
+
+  context = g_private_get (&gimp_scratch_context);
+
+  if (context->n_available_blocks == context->n_blocks)
+    {
+      context->n_blocks = MAX (2 * context->n_blocks, 1);
+      context->blocks   = g_renew (GimpScratchBlock *, context->blocks,
+                                   context->n_blocks);
+    }
+
+  context->blocks[context->n_available_blocks++] = block;
+}
+
+
+#define gimp_scratch_new(type, n) \
+  ((type *) (gimp_scratch_alloc (sizeof (type) * (n))))
+
+#define gimp_scratch_new0(type, n) \
+  ((type *) (gimp_scratch_alloc0 (sizeof (type) * (n))))
+
+
+#endif /* __GIMP_SCRATCH_H__ */


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