You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2013/02/14 11:20:58 UTC

svn commit: r1446100 - /subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/pack.c

Author: stefan2
Date: Thu Feb 14 10:20:58 2013
New Revision: 1446100

URL: http://svn.apache.org/r1446100
Log:
On the fsfs-format7 branch:  Teach pack code to prevent items from
overlapping block boundaries.

This significantly improves speed in particular for parsed items where
line-sized prefeches may cause APR files to jump back and forth between
blocks.  It also allows for many blocks to be read only once instead of
2 or 3 times due to overlapping items "pulling them in" again.

The optimization trades < 1% of size for ~30% savings in data transfer
depending on scenario and items sizes.

* subversion/libsvn_fs_fs/pack.c
  (write_null_bytes): new utility
  (copy_items_from_temp): prevent block boundary crossings where feasible;
   add docstrings

Modified:
    subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/pack.c

Modified: subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/pack.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/pack.c?rev=1446100&r1=1446099&r2=1446100&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/pack.c (original)
+++ subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/pack.c Thu Feb 14 10:20:58 2013
@@ -266,6 +266,28 @@ copy_file_data(pack_context_t *context,
   return SVN_NO_ERROR;
 }
 
+/* Writes SIZE bytes, all 0, to DEST.  Uses POOL for allocations.
+ */
+static svn_error_t *
+write_null_bytes(apr_file_t *dest,
+                 apr_off_t size,
+                 apr_pool_t *pool)
+{
+  /* Have a collection of high-quality, easy to access NUL bytes handy. */
+  enum { BUFFER_SIZE = 1024 };
+  static const char buffer[BUFFER_SIZE] = { 0 };
+
+  /* copy SIZE of them into the file's buffer */
+  while (size)
+    {
+      apr_size_t to_write = MIN(size, BUFFER_SIZE);
+      SVN_ERR(svn_io_file_write_full(dest, buffer, to_write, NULL, pool));
+      size -= to_write;
+    }
+
+  return SVN_NO_ERROR;
+}
+
 static svn_error_t *
 copy_item_to_temp(pack_context_t *context,
                   apr_array_header_t *entries,
@@ -588,24 +610,77 @@ sort_reps(pack_context_t *context)
     }
 }
 
+/* Copy (append) the items identified by svn_fs_fs__p2l_entry_t * elements
+ * in ENTRIES strictly in order from TEMP_FILE into CONTEXT->PACK_FILE.
+ * Use POOL for temporary allocations.
+ */
 static svn_error_t *
 copy_items_from_temp(pack_context_t *context,
                      apr_array_header_t *entries,
                      apr_file_t *temp_file,
                      apr_pool_t *pool)
 {
+  fs_fs_data_t *ffd = context->fs->fsap_data;
   apr_pool_t *iterpool = svn_pool_create(pool);
   int i;
+
+  /* To prevent items from overlapping a block boundary, we will usually
+   * put them into the next block and top up the old one with NUL bytes.
+   * This is the maximum number of bytes "wasted" that way per block.
+   * Larger items will cross the block boundaries. */
+  const apr_off_t alignment_limit = MAX(ffd->block_size / 50, 512);
+
+  /* copy all items in strict order */
   for (i = 0; i < entries->nelts; ++i)
     {
       svn_fs_fs__p2l_entry_t *entry
         = APR_ARRAY_IDX(entries, i, svn_fs_fs__p2l_entry_t *);
 
+      apr_off_t in_block_offset = context->pack_offset % ffd->block_size;
+
+      /* Determine how many bytes must still be available in the current
+       * block to be able to insert the current item without crossing the
+       * boundary.  Also, add 80 extra bytes (i.e. the size our line-based
+       * parser prefetch) for items that get parsed such that there will
+       * be no back-and-forth between blocks during parsing. */
+      apr_off_t safe_size = entry->size;
+      if (entry->type == SVN_FS_FS__ITEM_TYPE_NODEREV ||
+          entry->type == SVN_FS_FS__ITEM_TYPE_CHANGES)
+        safe_size += 80;
+
+      /* still enough space in current block? */
+      if (in_block_offset + safe_size > ffd->block_size)
+        {
+          /* No.  Is wasted space small enough to align the current item
+           * to the next block? */
+          apr_off_t bytes_to_alignment = ffd->block_size - in_block_offset;
+          if (bytes_to_alignment < alignment_limit)
+            {
+              /* Yes. To up with NUL bytes and don't forget to create
+               * an P2L index entry marking this section as unused. */
+              svn_fs_fs__p2l_entry_t null_entry;
+              null_entry.offset = context->pack_offset;
+              null_entry.size = bytes_to_alignment;
+              null_entry.type = SVN_FS_FS__ITEM_TYPE_UNUSED;
+              null_entry.revision = 0;
+              null_entry.item_index = SVN_FS_FS__ITEM_INDEX_UNUSED;
+              
+              SVN_ERR(write_null_bytes(context->pack_file,
+                                       bytes_to_alignment, iterpool));
+              SVN_ERR(svn_fs_fs__p2l_proto_index_add_entry
+                          (context->proto_p2l_index, &null_entry, iterpool));
+              context->pack_offset += bytes_to_alignment;
+            }
+        }
+
+      /* select the item in the source file and copy it into the target
+       * pack file */
       SVN_ERR(svn_io_file_seek(temp_file, SEEK_SET, &entry->offset,
                                iterpool));
       SVN_ERR(copy_file_data(context, context->pack_file, temp_file,
                              entry->size, pool));
 
+      /* write index entry and update current position */
       entry->offset = context->pack_offset;
       context->pack_offset += entry->size;