Author: stefan2
Date: Sat Apr 27 18:36:23 2013
New Revision: 1476656
URL: http://svn.apache.org/r1476656
Log:
On the fsfs-format7 branch: optimize P2L index read code. Open the input
stream only once and fine-tune prefetching.
* subversion/libsvn_fs_fs/index.c
(get_p2l_page_info): add STREAM_POOL parameter; delay result allocation
(get_p2l_page): add STREAM_POOL parameter and pass it through
(prefetch_p2l_page): ditto; use "leaking bucket" pattern to determine
whether we should continue to prefetch data
(svn_fs_fs__p2l_index_lookup): update caller; read more linearly
Modified:
subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/index.c
Modified: subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/index.c
URL:
http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/index.c?rev=1476656&r1=1476655&r2=1476656&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/index.c (original)
+++ subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/index.c Sat Apr 27
18:36:23 2013
@@ -1931,19 +1931,21 @@ p2l_page_info_func(void **out,
* index page for the rev / pack file offset BATON->OFFSET.
*
* To maximize efficiency, use or return the data stream in *STREAM.
+ * If *STREAM is yet to be constructed, do so in STREAM_POOL.
* Use POOL for allocations.
*/
static svn_error_t *
get_p2l_page_info(p2l_page_info_baton_t *baton,
packed_number_stream_t **stream,
svn_fs_t *fs,
+ apr_pool_t *stream_pool,
apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
apr_uint64_t value;
apr_size_t i;
apr_off_t offset;
- p2l_header_t *result = apr_pcalloc(pool, sizeof(*result));
+ p2l_header_t *result;
svn_boolean_t is_cached = FALSE;
void *dummy = NULL;
@@ -1962,10 +1964,13 @@ get_p2l_page_info(p2l_page_info_baton_t
if (*stream == NULL)
SVN_ERR(packed_stream_open(stream,
path_p2l_index(fs, baton->revision, pool),
- ffd->block_size, pool));
+ ffd->block_size, stream_pool));
else
packed_stream_seek(*stream, 0);
+ /* allocate result data structure */
+ result = apr_pcalloc(pool, sizeof(*result));
+
/* read table sizes and allocate page array */
SVN_ERR(packed_stream_get(&value, *stream));
result->first_revision = (svn_revnum_t)value;
@@ -2057,7 +2062,8 @@ read_entry(packed_number_stream_t *strea
* can be found in the index page beginning at START_OFFSET with the next
* page beginning at NEXT_OFFSET. Return the relevant index entries in
* *ENTRIES. To maximize efficiency, use or return the data stream in
- * STREAM. Use POOL for allocations.
+ * STREAM. If the latter is yet to be constructed, do so in STREAM_POOL.
+ * Use POOL for other allocations.
*/
static svn_error_t *
get_p2l_page(apr_array_header_t **entries,
@@ -2068,6 +2074,7 @@ get_p2l_page(apr_array_header_t **entrie
apr_off_t next_offset,
apr_off_t page_start,
apr_uint64_t page_size,
+ apr_pool_t *stream_pool,
apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
@@ -2081,7 +2088,7 @@ get_p2l_page(apr_array_header_t **entrie
if (*stream == NULL)
SVN_ERR(packed_stream_open(stream,
path_p2l_index(fs, start_revision, pool),
- ffd->block_size, pool));
+ ffd->block_size, stream_pool));
packed_stream_seek(*stream, start_offset);
/* read rev file offset of the first page entry (all page entries will
@@ -2114,28 +2121,35 @@ get_p2l_page(apr_array_header_t **entrie
}
/* If it cannot be found in FS's caches, read the p2l index page selected
- * by BATON->OFFSET from STREAM. Don't read it, if it precedes MIN_OFFSET.
+ * by BATON->OFFSET from *STREAM. If the latter is yet to be constructed,
+ * do so in STREAM_POOL. Don't read the page if it precedes MIN_OFFSET.
* Set *END to TRUE if the caller should stop refeching.
*
* *BATON will be updated with the selected page's info and SCRATCH_POOL
- * will be used for temporary allocations.
+ * will be used for temporary allocations. If the data is alread in the
+ * cache, descrease *LEAKING_BUCKET and increase it otherwise. With that
+ * pattern we will still read all pages from the block even if some of
+ * them survived in the cached.
*/
static svn_error_t *
prefetch_p2l_page(svn_boolean_t *end,
+ int *leaking_bucket,
svn_fs_t *fs,
- packed_number_stream_t *stream,
+ packed_number_stream_t **stream,
p2l_page_info_baton_t *baton,
apr_off_t min_offset,
+ apr_pool_t *stream_pool,
apr_pool_t *scratch_pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
+ svn_boolean_t already_cached;
apr_array_header_t *page;
svn_fs_fs__page_cache_key_t key = { 0 };
/* fetch the page info */
*end = FALSE;
baton->revision = baton->first_revision;
- SVN_ERR(get_p2l_page_info(baton, &stream, fs, scratch_pool));
+ SVN_ERR(get_p2l_page_info(baton, stream, fs, stream_pool, scratch_pool));
if (baton->start_offset < min_offset)
{
/* page outside limits -> stop prefetching */
@@ -2147,21 +2161,30 @@ prefetch_p2l_page(svn_boolean_t *end,
key.revision = baton->first_revision;
key.is_packed = is_packed_rev(fs, baton->first_revision);
key.page = baton->page_no;
- SVN_ERR(svn_cache__has_key(end, ffd->p2l_page_cache,
+ SVN_ERR(svn_cache__has_key(&already_cached, ffd->p2l_page_cache,
&key, scratch_pool));
- if (*end)
+
+ /* yes, already cached */
+ if (already_cached)
{
- /* yes, already cached -> stop prefetching */
+ /* stop prefetching if most pages are already cached. */
+ if (!--*leaking_bucket)
+ *end = TRUE;
+
return SVN_NO_ERROR;
}
+ ++*leaking_bucket;
+
/* read from disk */
- SVN_ERR(get_p2l_page(&page, &stream, fs,
+ SVN_ERR(get_p2l_page(&page, stream, fs,
baton->first_revision,
baton->start_offset,
baton->next_offset,
baton->page_start,
- baton->page_size, scratch_pool));
+ baton->page_size,
+ stream_pool,
+ scratch_pool));
/* and put it into our cache */
SVN_ERR(svn_cache__set(ffd->p2l_page_cache, &key, page, scratch_pool));
@@ -2186,7 +2209,7 @@ svn_fs_fs__p2l_index_lookup(apr_array_he
* contents at pack / rev file position OFFSET. */
page_info.offset = offset;
page_info.revision = revision;
- SVN_ERR(get_p2l_page_info(&page_info, &stream, fs, pool));
+ SVN_ERR(get_p2l_page_info(&page_info, &stream, fs, pool, pool));
/* if the offset refers to a non-existent page, bail out */
if (page_info.page_count <= page_info.page_no)
@@ -2212,43 +2235,54 @@ svn_fs_fs__p2l_index_lookup(apr_array_he
svn_boolean_t end;
apr_pool_t *iterpool = svn_pool_create(pool);
apr_off_t original_page_start = page_info.page_start;
+ int leaking_bucket = 4;
+ p2l_page_info_baton_t prefetch_info = page_info;
- /* fetch page from disk and put it into the cache */
- SVN_ERR(get_p2l_page(entries, &stream, fs,
- page_info.first_revision,
- page_info.start_offset,
- page_info.next_offset,
- page_info.page_start,
- page_info.page_size, pool));
-
- SVN_ERR(svn_cache__set(ffd->p2l_page_cache, &key, *entries, pool));
+ apr_off_t max_offset
+ = APR_ALIGN(page_info.next_offset, ffd->block_size);
+ apr_off_t min_offset
+ = APR_ALIGN(page_info.start_offset, ffd->block_size) - ffd->block_size;
/* Since we read index data in larger chunks, we probably got more
* page data than we requested. Parse & cache that until either we
* encounter pages already cached or reach the end of the buffer.
*/
- /* pre-fetch following pages */
+ /* pre-fetch preceding pages */
end = FALSE;
- page_info.offset = original_page_start;
- while ( page_info.next_offset < max_offset
- && page_info.page_no + 1 < page_info.page_count
- && !end)
+ prefetch_info.offset = original_page_start;
+ while (prefetch_info.offset >= prefetch_info.page_size && !end)
{
- page_info.offset += page_info.page_size;
- SVN_ERR(prefetch_p2l_page(&end, fs, stream, &page_info,
- min_offset, iterpool));
+ prefetch_info.offset -= prefetch_info.page_size;
+ SVN_ERR(prefetch_p2l_page(&end, &leaking_bucket, fs, &stream,
+ &prefetch_info, min_offset,
+ pool, iterpool));
svn_pool_clear(iterpool);
}
- /* pre-fetch preceding pages */
+ /* fetch page from disk and put it into the cache */
+ SVN_ERR(get_p2l_page(entries, &stream, fs,
+ page_info.first_revision,
+ page_info.start_offset,
+ page_info.next_offset,
+ page_info.page_start,
+ page_info.page_size, pool, pool));
+
+ SVN_ERR(svn_cache__set(ffd->p2l_page_cache, &key, *entries, pool));
+
+ /* pre-fetch following pages */
end = FALSE;
- page_info.offset = original_page_start;
- while (page_info.offset >= page_info.page_size && !end)
+ leaking_bucket = 4;
+ prefetch_info = page_info;
+ prefetch_info.offset = original_page_start;
+ while ( prefetch_info.next_offset < max_offset
+ && prefetch_info.page_no + 1 < prefetch_info.page_count
+ && !end)
{
- page_info.offset -= page_info.page_size;
- SVN_ERR(prefetch_p2l_page(&end, fs, stream, &page_info,
- min_offset, iterpool));
+ prefetch_info.offset += prefetch_info.page_size;
+ SVN_ERR(prefetch_p2l_page(&end, &leaking_bucket, fs, &stream,
+ &prefetch_info, min_offset,
+ pool, iterpool));
svn_pool_clear(iterpool);
}