[gegl] buffer: don't count cloned tiles towards cache_total more than once
- From: N/A <ell src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gegl] buffer: don't count cloned tiles towards cache_total more than once
- Date: Wed, 13 Dec 2017 19:40:51 +0000 (UTC)
commit 67b58c73912a83ba2a1e07cae289eff70aa862a8
Author: Ell <ell_se yahoo com>
Date: Tue Dec 12 12:21:40 2017 -0500
buffer: don't count cloned tiles towards cache_total more than once
In GeglTileHandlerCache, we currently count the size of all tiles
individually towards the cache's total size, even when some of the
tiles are clones of each other, and hence share the same data.
Improve this by counting the size of groups of mutually cloned
tiles only once, to reflect their actual memory consumption. The
implementation uses a lock-free shared counter, similar to the way
we do COW, and has low overhead.
gegl/buffer/gegl-buffer-private.h | 11 +++-
gegl/buffer/gegl-tile-handler-cache.c | 117 ++++++++++++++++++++-------------
gegl/buffer/gegl-tile-handler-cache.h | 19 +++--
gegl/buffer/gegl-tile-handler-empty.c | 7 ++
gegl/buffer/gegl-tile.c | 53 +++++++++++----
5 files changed, 136 insertions(+), 71 deletions(-)
---
diff --git a/gegl/buffer/gegl-buffer-private.h b/gegl/buffer/gegl-buffer-private.h
index e08e9c3..f40b8d6 100644
--- a/gegl/buffer/gegl-buffer-private.h
+++ b/gegl/buffer/gegl-buffer-private.h
@@ -168,8 +168,12 @@ struct _GeglTile
gint is_zero_tile:1;
gint clone_state; /* tile clone/unclone state & spinlock */
- gint *n_clones; /* shared atomic counter among all tiles
- * sharing the same data
+ gint *n_clones; /* an array of two atomic counters, shared
+ * among all tiles sharing the same data.
+ * the first counter indicates the number of
+ * tiles sharing the data, and the second
+ * counter indicates how many of those tiles
+ * are in the cache.
*/
/* called when the tile is about to be destroyed */
@@ -200,6 +204,9 @@ gboolean gegl_buffer_scan_compatible (GeglBuffer *bufferA,
#define gegl_tile_get_data(tile) ((tile)->data)
#endif
+#define gegl_tile_n_clones(tile) (&(tile)->n_clones[0])
+#define gegl_tile_n_cached_clones(tile) (&(tile)->n_clones[1])
+
/* computes the positive integer remainder (also for negative dividends)
*/
#define GEGL_REMAINDER(dividend, divisor) \
diff --git a/gegl/buffer/gegl-tile-handler-cache.c b/gegl/buffer/gegl-tile-handler-cache.c
index becb7e3..3bc0132 100644
--- a/gegl/buffer/gegl-tile-handler-cache.c
+++ b/gegl/buffer/gegl-tile-handler-cache.c
@@ -87,13 +87,13 @@ static void gegl_tile_handler_cache_invalidate (GeglTileHandlerCache *cach
gint z);
-static GMutex mutex = { 0, };
-static GQueue *cache_queue = NULL;
-static gint cache_wash_percentage = 20;
-static guint64 cache_total = 0; /* approximate amount of bytes stored */
+static GMutex mutex = { 0, };
+static GQueue *cache_queue = NULL;
+static gint cache_wash_percentage = 20;
+static volatile guintptr cache_total = 0; /* approximate amount of bytes stored */
#ifdef GEGL_DEBUG_CACHE_HITS
-static gint cache_hits = 0;
-static gint cache_misses = 0;
+static gint cache_hits = 0;
+static gint cache_misses = 0;
#endif
@@ -117,6 +117,31 @@ gegl_tile_handler_cache_init (GeglTileHandlerCache *cache)
}
static void
+drop_hot_tile (GeglTile *tile)
+{
+ GeglTileStorage *storage = tile->tile_storage;
+
+ if (storage)
+ {
+#if 0 /* the storage's mutex should already be locked at this point */
+ if (gegl_config_threads()>1)
+ g_rec_mutex_lock (&storage->mutex);
+#endif
+
+ if (storage->hot_tile == tile)
+ {
+ gegl_tile_unref (storage->hot_tile);
+ storage->hot_tile = NULL;
+ }
+
+#if 0
+ if (gegl_config_threads()>1)
+ g_rec_mutex_unlock (&storage->mutex);
+#endif
+ }
+}
+
+static void
gegl_tile_handler_cache_reinit (GeglTileHandlerCache *cache)
{
CacheItem *item;
@@ -140,8 +165,11 @@ gegl_tile_handler_cache_reinit (GeglTileHandlerCache *cache)
item = (CacheItem *) value;
if (item->tile)
{
- cache_total -= item->tile->size;
- gegl_tile_mark_as_stored (item->tile); // to avoid saving
+ if (g_atomic_int_dec_and_test (gegl_tile_n_cached_clones (item->tile)))
+ g_atomic_pointer_add (&cache_total, -item->tile->size);
+ drop_hot_tile (item->tile);
+ gegl_tile_mark_as_stored (item->tile); // to avoid saving
+ item->tile->tile_storage = NULL;
gegl_tile_unref (item->tile);
cache->count--;
}
@@ -304,11 +332,10 @@ gegl_tile_handler_cache_wash (GeglTileHandlerCache *cache)
CacheItem *item = LINK_GET_ITEM (link);
GeglTile *tile = item->tile;
- if (!gegl_tile_is_stored (tile))
+ if (tile->tile_storage && ! gegl_tile_is_stored (tile))
{
last_dirty = tile;
- if (last_dirty->tile_storage)
- g_object_ref (last_dirty->tile_storage);
+ g_object_ref (last_dirty->tile_storage);
gegl_tile_ref (last_dirty);
break;
@@ -320,7 +347,7 @@ gegl_tile_handler_cache_wash (GeglTileHandlerCache *cache)
if (last_dirty != NULL)
{
gegl_tile_store (last_dirty);
- g_clear_object (&last_dirty->tile_storage);
+ g_object_unref (last_dirty->tile_storage);
gegl_tile_unref (last_dirty);
return TRUE;
}
@@ -397,31 +424,6 @@ gegl_tile_handler_cache_has_tile (GeglTileHandlerCache *cache,
return FALSE;
}
-static void
-drop_hot_tile (GeglTile *tile)
-{
- GeglTileStorage *storage = tile->tile_storage;
-
- if (storage)
- {
-#if 0 /* the storage's mutex should already be locked at this point */
- if (gegl_config_threads()>1)
- g_rec_mutex_lock (&storage->mutex);
-#endif
-
- if (storage->hot_tile == tile)
- {
- gegl_tile_unref (storage->hot_tile);
- storage->hot_tile = NULL;
- }
-
-#if 0
- if (gegl_config_threads()>1)
- g_rec_mutex_unlock (&storage->mutex);
-#endif
- }
-}
-
static gboolean
gegl_tile_handler_cache_trim (GeglTileHandlerCache *cache)
{
@@ -429,7 +431,8 @@ gegl_tile_handler_cache_trim (GeglTileHandlerCache *cache)
link = g_queue_peek_tail_link (cache_queue);
- while (cache_total > gegl_config()->tile_cache_size)
+ while ((guintptr) g_atomic_pointer_get (&cache_total) >
+ gegl_config ()->tile_cache_size)
{
CacheItem *last_writable;
GeglTile *tile;
@@ -480,13 +483,16 @@ gegl_tile_handler_cache_trim (GeglTileHandlerCache *cache)
prev_link = g_list_previous (link);
g_queue_unlink (cache_queue, link);
g_hash_table_remove (last_writable->handler->items, last_writable);
- cache_total -= tile->size;
+ if (g_atomic_int_dec_and_test (gegl_tile_n_cached_clones (tile)))
+ g_atomic_pointer_add (&cache_total, -tile->size);
last_writable->handler->count--;
/* drop_hot_tile (tile); */ /* XXX: no use in trying to drop the hot
* tile, since this tile can't be it --
* the hot tile will have a ref-count of
* at least two.
*/
+ gegl_tile_store (tile);
+ tile->tile_storage = NULL;
gegl_tile_unref (tile);
if (dirty && gegl_config_threads()>1)
@@ -511,7 +517,8 @@ gegl_tile_handler_cache_invalidate (GeglTileHandlerCache *cache,
item = cache_lookup (cache, x, y, z);
if (item)
{
- cache_total -= item->tile->size;
+ if (g_atomic_int_dec_and_test (gegl_tile_n_cached_clones (item->tile)))
+ g_atomic_pointer_add (&cache_total, -item->tile->size);
cache->count--;
g_queue_unlink (cache_queue, &item->link);
@@ -520,8 +527,8 @@ gegl_tile_handler_cache_invalidate (GeglTileHandlerCache *cache,
g_mutex_unlock (&mutex);
drop_hot_tile (item->tile);
- item->tile->tile_storage = NULL;
gegl_tile_mark_as_stored (item->tile); /* to cheat it out of being stored */
+ item->tile->tile_storage = NULL;
gegl_tile_unref (item->tile);
g_slice_free (CacheItem, item);
@@ -543,7 +550,8 @@ gegl_tile_handler_cache_void (GeglTileHandlerCache *cache,
item = cache_lookup (cache, x, y, z);
if (item)
{
- cache_total -= item->tile->size;
+ if (g_atomic_int_dec_and_test (gegl_tile_n_cached_clones (item->tile)))
+ g_atomic_pointer_add (&cache_total, -item->tile->size);
g_queue_unlink (cache_queue, &item->link);
g_hash_table_remove (cache->items, item);
cache->count--;
@@ -554,6 +562,7 @@ gegl_tile_handler_cache_void (GeglTileHandlerCache *cache,
{
drop_hot_tile (item->tile);
gegl_tile_void (item->tile);
+ item->tile->tile_storage = NULL;
gegl_tile_unref (item->tile);
}
@@ -578,18 +587,19 @@ gegl_tile_handler_cache_insert (GeglTileHandlerCache *cache,
item->y = y;
item->z = z;
+ // XXX : remove entry if it already exists
+ gegl_tile_handler_cache_void (cache, x, y, z);
+
tile->x = x;
tile->y = y;
tile->z = z;
tile->tile_storage = cache->tile_storage;
- // XXX : remove entry if it already exists
- gegl_tile_handler_cache_void (cache, x, y, z);
-
/* XXX: this is a window when the tile is a zero tile during update */
g_mutex_lock (&mutex);
- cache_total += item->tile->size;
+ if (g_atomic_int_add (gegl_tile_n_cached_clones (tile), 1) == 0)
+ g_atomic_pointer_add (&cache_total, tile->size);
g_queue_push_head_link (cache_queue, &item->link);
cache->count ++;
@@ -601,6 +611,21 @@ gegl_tile_handler_cache_insert (GeglTileHandlerCache *cache,
g_mutex_unlock (&mutex);
}
+void
+gegl_tile_handler_cache_tile_uncloned (GeglTileHandlerCache *cache,
+ GeglTile *tile)
+{
+ if ((guintptr) g_atomic_pointer_add (&cache_total, tile->size) + tile->size >
+ gegl_config ()->tile_cache_size)
+ {
+ g_mutex_lock (&mutex);
+
+ gegl_tile_handler_cache_trim (cache);
+
+ g_mutex_unlock (&mutex);
+ }
+}
+
GeglTileHandler *
gegl_tile_handler_cache_new (void)
{
diff --git a/gegl/buffer/gegl-tile-handler-cache.h b/gegl/buffer/gegl-tile-handler-cache.h
index 5ed4dda..00a69f1 100644
--- a/gegl/buffer/gegl-tile-handler-cache.h
+++ b/gegl/buffer/gegl-tile-handler-cache.h
@@ -49,13 +49,16 @@ struct _GeglTileHandlerCacheClass
GeglTileHandlerClass parent_class;
};
-GType gegl_tile_handler_cache_get_type (void) G_GNUC_CONST;
-
-GeglTileHandler * gegl_tile_handler_cache_new (void);
-void gegl_tile_handler_cache_insert (GeglTileHandlerCache *cache,
- GeglTile *tile,
- gint x,
- gint y,
- gint z);
+GType gegl_tile_handler_cache_get_type (void) G_GNUC_CONST;
+
+GeglTileHandler * gegl_tile_handler_cache_new (void);
+
+void gegl_tile_handler_cache_insert (GeglTileHandlerCache *cache,
+ GeglTile *tile,
+ gint x,
+ gint y,
+ gint z);
+void gegl_tile_handler_cache_tile_uncloned (GeglTileHandlerCache *cache,
+ GeglTile *tile);
#endif
diff --git a/gegl/buffer/gegl-tile-handler-empty.c b/gegl/buffer/gegl-tile-handler-empty.c
index 8bec7f6..5ce06a1 100644
--- a/gegl/buffer/gegl-tile-handler-empty.c
+++ b/gegl/buffer/gegl-tile-handler-empty.c
@@ -72,6 +72,13 @@ _new_empty_tile (const gint tile_size)
allocated_tile->size = common_empty_size;
allocated_tile->is_zero_tile = 1;
+ /* avoid counting duplicates of the empty tile towards the total
+ * cache size, both since this is unnecessary, and since they may
+ * have different sizes, which is inconsistent with the duplicate-
+ * tracking cache logic.
+ */
+ (*gegl_tile_n_cached_clones (allocated_tile))++;
+
g_once_init_leave (&common_tile, allocated_tile);
}
diff --git a/gegl/buffer/gegl-tile.c b/gegl/buffer/gegl-tile.c
index bce28e6..eef7c16 100644
--- a/gegl/buffer/gegl-tile.c
+++ b/gegl/buffer/gegl-tile.c
@@ -34,6 +34,7 @@
#include "gegl-buffer-private.h"
#include "gegl-tile-source.h"
#include "gegl-tile-storage.h"
+#include "gegl-tile-handler-cache.h"
#include "gegl-config.h"
/* the offset at which the tile data begins, when it shares the same buffer as
@@ -41,7 +42,7 @@
* that the tile data is similarly aligned.
*/
#define INLINE_N_ELEMENTS_DATA_OFFSET 16
-G_STATIC_ASSERT (INLINE_N_ELEMENTS_DATA_OFFSET >= sizeof (gint));
+G_STATIC_ASSERT (INLINE_N_ELEMENTS_DATA_OFFSET >= 2 * sizeof (gint));
enum
{
@@ -70,7 +71,7 @@ void gegl_tile_unref (GeglTile *tile)
*/
gegl_tile_store (tile);
- if (g_atomic_int_dec_and_test (tile->n_clones))
+ if (g_atomic_int_dec_and_test (gegl_tile_n_clones (tile)))
{ /* no clones */
if (tile->destroy_notify == (void*)&free_n_clones_directly)
{
@@ -93,7 +94,7 @@ void gegl_tile_unref (GeglTile *tile)
tile->destroy_notify (tile->destroy_notify_data);
}
- g_slice_free (gint, tile->n_clones);
+ g_slice_free1 (2 * sizeof (gint), tile->n_clones);
}
}
@@ -120,8 +121,9 @@ gegl_tile_new_bare (void)
{
GeglTile *tile = gegl_tile_new_bare_internal ();
- tile->n_clones = g_slice_new (gint);
- *tile->n_clones = 1;
+ tile->n_clones = g_slice_alloc (2 * sizeof (gint));
+ *gegl_tile_n_clones (tile) = 1;
+ *gegl_tile_n_cached_clones (tile) = 0;
tile->destroy_notify = (void*)&free_data_directly;
tile->destroy_notify_data = NULL;
@@ -138,7 +140,6 @@ gegl_tile_dup (GeglTile *src)
src->clone_state = CLONE_STATE_CLONED;
- tile->tile_storage = src->tile_storage;
tile->data = src->data;
tile->size = src->size;
tile->is_zero_tile = src->is_zero_tile;
@@ -148,7 +149,7 @@ gegl_tile_dup (GeglTile *src)
tile->destroy_notify = src->destroy_notify;
tile->destroy_notify_data = src->destroy_notify_data;
- g_atomic_int_inc (tile->n_clones);
+ g_atomic_int_inc (gegl_tile_n_clones (tile));
return tile;
}
@@ -159,8 +160,9 @@ gegl_tile_new (gint size)
GeglTile *tile = gegl_tile_new_bare_internal ();
/* allocate a single buffer for both tile->n_clones and tile->data */
- tile->n_clones = gegl_malloc (INLINE_N_ELEMENTS_DATA_OFFSET + size);
- *tile->n_clones = 1;
+ tile->n_clones = gegl_malloc (INLINE_N_ELEMENTS_DATA_OFFSET + size);
+ *gegl_tile_n_clones (tile) = 1;
+ *gegl_tile_n_cached_clones (tile) = 0;
tile->data = (guchar *) tile->n_clones + INLINE_N_ELEMENTS_DATA_OFFSET;
tile->size = size;
@@ -174,19 +176,33 @@ gegl_tile_new (gint size)
static void
gegl_tile_unclone (GeglTile *tile)
{
- if (*tile->n_clones > 1)
+ if (*gegl_tile_n_clones (tile) > 1)
{
+ GeglTileHandlerCache *notify_cache = NULL;
+ gboolean cached;
+
+ cached = tile->tile_storage && tile->tile_storage->cache;
+
+ if (cached)
+ {
+ if (! g_atomic_int_dec_and_test (gegl_tile_n_cached_clones (tile)))
+ notify_cache = tile->tile_storage->cache;
+ }
+
/* the tile data is shared with other tiles,
* create a local copy
*/
if (tile->is_zero_tile)
{
- if (g_atomic_int_dec_and_test (tile->n_clones))
+ if (g_atomic_int_dec_and_test (gegl_tile_n_clones (tile)))
{
/* someone else uncloned the tile in the meantime, and we're now
* the last copy; bail.
*/
- *tile->n_clones = 1;
+ *gegl_tile_n_clones (tile) = 1;
+ *gegl_tile_n_cached_clones (tile) = cached;
+ if (notify_cache)
+ gegl_tile_handler_cache_tile_uncloned (notify_cache, tile);
return;
}
@@ -201,25 +217,32 @@ gegl_tile_unclone (GeglTile *tile)
buf = gegl_malloc (INLINE_N_ELEMENTS_DATA_OFFSET + tile->size);
memcpy (buf + INLINE_N_ELEMENTS_DATA_OFFSET, tile->data, tile->size);
- if (g_atomic_int_dec_and_test (tile->n_clones))
+ if (g_atomic_int_dec_and_test (gegl_tile_n_clones (tile)))
{
/* someone else uncloned the tile in the meantime, and we're now
* the last copy; bail.
*/
gegl_free (buf);
- *tile->n_clones = 1;
+ *gegl_tile_n_clones (tile) = 1;
+ *gegl_tile_n_cached_clones (tile) = cached;
+ if (notify_cache)
+ gegl_tile_handler_cache_tile_uncloned (notify_cache, tile);
return;
}
tile->n_clones = (gint *) buf;
}
- *tile->n_clones = 1;
+ *gegl_tile_n_clones (tile) = 1;
+ *gegl_tile_n_cached_clones (tile) = cached;
tile->data = (guchar *) tile->n_clones +
INLINE_N_ELEMENTS_DATA_OFFSET;
tile->destroy_notify = (void*)&free_n_clones_directly;
tile->destroy_notify_data = NULL;
+
+ if (notify_cache)
+ gegl_tile_handler_cache_tile_uncloned (notify_cache, tile);
}
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]