Author: stefan2
Date: Tue May 17 13:31:47 2011
New Revision: 1104236

URL: http://svn.apache.org/viewvc?rev=1104236&view=rev
Log:
Following a proposal by Ivan, defer cache index initialization
inside cache-membuffer to the first write access. That allows
for multi-GB caches to be created virtually instantly (assuming
the OS reserves memory sufficiently quickly).

* subversion/libsvn_subr/cache-membuffer.c
  (GROUP_INIT_GRANULARITY): new constant
  (svn_membuffer_t): add group_initialized bit vector
  (is_group_initialized): new utility to check for a groups' init. status
  (initialize_group): new function to initialize a group
  (find_entry): auto-initialize groups upon access
  (svn_cache__membuffer_cache_create): only initialize the group 
   init. status instead of the actual directory

Modified:
    subversion/trunk/subversion/libsvn_subr/cache-membuffer.c

Modified: subversion/trunk/subversion/libsvn_subr/cache-membuffer.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_subr/cache-membuffer.c?rev=1104236&r1=1104235&r2=1104236&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_subr/cache-membuffer.c (original)
+++ subversion/trunk/subversion/libsvn_subr/cache-membuffer.c Tue May 17 
13:31:47 2011
@@ -117,6 +117,15 @@
  */
 #define MIN_SEGMENT_SIZE 0x2000000ull
 
+/* We don't mark the initialization status for every group but initialize
+ * a number of groups at once. That will allow for a very small init flags
+ * vector that is likely to fit into the CPU caches even for fairly large
+ * caches. For instance, the default of 32 means 8x32 groups per byte, i.e.
+ * 8 flags/byte x 32 groups/flag x 4 entries/group x 40 index bytes/entry 
+ * x 16 cache bytes/index byte = 1kB init vector / 640MB cache.
+ */
+#define GROUP_INIT_GRANULARITY 32
+
 /* Invalid index reference value. Equivalent to APR_UINT32_T(-1)
  */
 #define NO_INDEX APR_UINT32_MAX
@@ -337,6 +346,11 @@ struct svn_membuffer_t
   /* The dictionary, GROUP_SIZE * group_count entries long. Never NULL.
    */
   entry_group_t *directory;
+  
+  /* Flag array with group_count / GROUP_INIT_GRANULARITY _bit_ elements.
+   * Allows for efficiently marking groups as "not initialized".
+   */
+  unsigned char *group_initialized;
 
   /* Size of dictionary in groups. Must be > 0.
    */
@@ -644,6 +658,48 @@ let_entry_age(svn_membuffer_t *cache, en
   entry->hit_count -= hits_removed;
 }
 
+/* Returns 0 if the entry group idenified by GROUP_INDEX in CACHE has not
+ * been intialized, yet. In that case, this group can not data. Otherwise,
+ * a non-zero value is returned.
+ */
+static APR_INLINE unsigned char
+is_group_initialized(svn_membuffer_t *cache, apr_uint32_t group_index)
+{
+  unsigned char flags = cache->group_initialized
+                          [group_index / (8 * GROUP_INIT_GRANULARITY)];
+  unsigned char bit_mask = 1 << ((group_index / GROUP_INIT_GRANULARITY) % 8);
+  return flags & bit_mask;
+}
+
+/* Initializes the section of the directory in CACHE that contains
+ * the entry group indentified by GROUP_INDEX. */
+static void
+initialize_group(svn_membuffer_t *cache, apr_uint32_t group_index)
+{
+  unsigned char bit_mask;
+  entry_t *entry;
+  apr_uint32_t i;
+
+  /* range of groups to initialize due to GROUP_INIT_GRANULARITY */
+  apr_uint32_t first_index = group_index & GROUP_INIT_GRANULARITY;
+  apr_uint32_t last_index = group_index + GROUP_INIT_GRANULARITY;
+  if (last_index > cache->group_count)
+    last_index = cache->group_count;
+  
+  /* initialize their entries */
+  first_index *= GROUP_SIZE;
+  last_index *= GROUP_SIZE;
+
+  entry = &cache->directory[0][0];
+  for (i = first_index; i < last_index; ++i)
+    entry[i].offset = -1;
+
+  /* set the "initialized" bit for these groups */
+  bit_mask = 1 << ((group_index / GROUP_INIT_GRANULARITY) % 8);
+  cache->group_initialized[group_index / (8 * GROUP_INIT_GRANULARITY)]
+    |= bit_mask;
+}
+
 /* Given the GROUP_INDEX that shall contain an entry with the hash key
  * TO_FIND, find that entry in the specified group. 
  *
@@ -670,6 +726,19 @@ find_entry(svn_membuffer_t *cache,
    */
   group = &cache->directory[group_index][0];
 
+  /* If the entry group has not been initialized, yet, there is no data. 
+   */
+  if (! is_group_initialized(cache, group_index))
+    {
+      if (find_empty)
+        {
+          initialize_group(cache, group_index);
+          entry = group;
+        }
+        
+      return entry;
+    }
+  
   /* try to find the matching entry 
    */
   for (i = 0; i < GROUP_SIZE; ++i)
@@ -977,6 +1046,9 @@ svn_cache__membuffer_cache_create(svn_me
           secure_aligned_alloc(pool,
                                group_count * sizeof(entry_group_t),
                                FALSE);
+      c[seg].group_initialized = secure_aligned_alloc(pool, 
+                                                      group_count, 
+                                                      FALSE);
       c[seg].first = NO_INDEX;
       c[seg].last = NO_INDEX;
       c[seg].next = NO_INDEX;
@@ -1004,12 +1076,7 @@ svn_cache__membuffer_cache_create(svn_me
 
       /* initialize directory entries as "unused"
        */
-      memset(c[seg].directory,
-             0xff,
-             (apr_size_t)c->group_count * sizeof(entry_group_t));
-
-      /* this may fail on exotic platforms (1s complement) only */
-      assert(c[seg].directory[0][0].offset == -1);
+      memset(c[seg].group_initialized, FALSE, (apr_size_t)c->group_count);
 
 #if APR_HAS_THREADS
       /* A lock for intra-process synchronization to the cache, or NULL if


Reply via email to