Align with incoming cache_image branch changes: use the index list (instead of

the hash buckets) for scanning the entries during a flush, and also add in
counters for tracking operations during cache flushes.
This commit is contained in:
Quincey Koziol 2017-01-02 12:06:08 -08:00
parent baaf735015
commit ce3877d298
2 changed files with 150 additions and 104 deletions

237
src/H5C.c
View File

@ -438,6 +438,10 @@ H5C_create(size_t max_cache_size,
((cache_ptr->epoch_markers)[i]).type = &H5C__epoch_marker_class;
}
cache_ptr->entries_loaded_counter = 0;
cache_ptr->entries_inserted_counter = 0;
cache_ptr->entries_relocated_counter = 0;
if ( H5C_reset_cache_hit_rate_stats(cache_ptr) != SUCCEED ) {
/* this should be impossible... */
@ -2417,6 +2421,9 @@ H5C_protect(H5F_t * f,
*/
H5C__UPDATE_RP_FOR_INSERTION(cache_ptr, entry_ptr, NULL)
/* Update entries loaded in cache counter */
cache_ptr->entries_loaded_counter++;
/* Record that the entry was loaded, to trigger a notify callback later */
/* (After the entry is fully added to the cache) */
was_loaded = TRUE;
@ -3224,10 +3231,10 @@ H5C_unprotect(H5F_t * f,
/* Delete the entry from the skip list on destroy */
flush_flags |= H5C__DEL_FROM_SLIST_ON_DESTROY_FLAG;
HDassert(((!was_clean) || dirtied) == entry_ptr->in_slist);
if(H5C__flush_single_entry(f, dxpl_id, entry_ptr, flush_flags) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, "Can't flush entry")
}
} /* end if */
#ifdef H5_HAVE_PARALLEL
else if(clear_entry) {
@ -5146,22 +5153,22 @@ static herr_t
H5C_flush_invalidate_ring(const H5F_t * f, hid_t dxpl_id, H5C_ring_t ring,
unsigned flags)
{
H5C_t * cache_ptr;
H5C_t *cache_ptr;
hbool_t restart_slist_scan;
int32_t protected_entries = 0;
int32_t i;
int32_t cur_ring_pel_len;
int32_t old_ring_pel_len;
unsigned cooked_flags;
unsigned evict_flags;
H5SL_node_t * node_ptr = NULL;
H5C_cache_entry_t * entry_ptr = NULL;
H5C_cache_entry_t * next_entry_ptr = NULL;
int32_t protected_entries = 0;
int32_t i;
int32_t cur_ring_pel_len;
int32_t old_ring_pel_len;
unsigned cooked_flags;
unsigned evict_flags;
H5SL_node_t *node_ptr = NULL;
H5C_cache_entry_t *entry_ptr = NULL;
H5C_cache_entry_t *next_entry_ptr = NULL;
#if H5C_DO_SANITY_CHECKS
int64_t initial_slist_len = 0;
size_t initial_slist_size = 0;
#endif /* H5C_DO_SANITY_CHECKS */
herr_t ret_value = SUCCEED;
herr_t ret_value = SUCCEED;
FUNC_ENTER_NOAPI(FAIL)
@ -5407,77 +5414,92 @@ H5C_flush_invalidate_ring(const H5F_t * f, hid_t dxpl_id, H5C_ring_t ring,
*
* Writes to disk are possible here.
*/
for(i = 0; i < H5C__HASH_TABLE_LEN; i++) {
next_entry_ptr = cache_ptr->index[i];
while(next_entry_ptr != NULL) {
entry_ptr = next_entry_ptr;
HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
HDassert(entry_ptr->ring >= ring);
/* reset the counters so that we can detect insertions, loads,
* and moves caused by the pre_serialize and serialize calls.
*/
cache_ptr->entries_loaded_counter = 0;
cache_ptr->entries_inserted_counter = 0;
cache_ptr->entries_relocated_counter = 0;
next_entry_ptr = entry_ptr->ht_next;
HDassert((next_entry_ptr == NULL) ||
(next_entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC));
next_entry_ptr = cache_ptr->il_head;
while(next_entry_ptr != NULL) {
entry_ptr = next_entry_ptr;
HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
HDassert(entry_ptr->ring >= ring);
if(((!entry_ptr->flush_me_last) ||
((entry_ptr->flush_me_last) &&
(cache_ptr->num_last_entries >= cache_ptr->slist_len))) &&
(entry_ptr->flush_dep_nchildren == 0) &&
(entry_ptr->ring == ring)) {
next_entry_ptr = entry_ptr->il_next;
HDassert((next_entry_ptr == NULL) ||
(next_entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC));
if(entry_ptr->is_protected) {
/* we have major problems -- but lets flush and
* destroy everything we can before we flag an
* error.
*/
protected_entries++;
if(!entry_ptr->in_slist)
HDassert(!(entry_ptr->is_dirty));
} else if(!(entry_ptr->is_pinned)) {
/* if *entry_ptr is dirty, it is possible
* that one or more other entries may be
* either removed from the cache, loaded
* into the cache, or moved to a new location
* in the file as a side effect of the flush.
*
* It's also possible that removing a clean
* entry will remove the last child of a proxy
* entry, allowing it to be removed also and
* invalidating the next_entry_ptr.
*
* If either of these happen, and one of the target
* or proxy entries happens to be the next entry in
* the hash bucket, we could either find ourselves
* either scanning a non-existant entry, scanning
* through a different bucket, or skipping an entry.
*
* Neither of these are good, so restart the
* the scan at the head of the hash bucket
* after the flush if we detect that the next_entry_ptr
* becomes invalid.
*
* This is not as inefficient at it might seem,
* as hash buckets typically have at most two
* or three entries.
*/
cache_ptr->entry_watched_for_removal = next_entry_ptr;
if(H5C__flush_single_entry(f, dxpl_id, entry_ptr, (cooked_flags | H5C__DURING_FLUSH_FLAG | H5C__FLUSH_INVALIDATE_FLAG | H5C__DEL_FROM_SLIST_ON_DESTROY_FLAG)) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "Entry flush destroy failed.")
/* Check for the next entry getting removed */
if(NULL != next_entry_ptr && NULL == cache_ptr->entry_watched_for_removal) {
/* update stats for hash bucket scan restart here.
* -- JRM
*/
next_entry_ptr = cache_ptr->index[i];
H5C__UPDATE_STATS_FOR_INDEX_SCAN_RESTART(cache_ptr)
} /* end if */
else
cache_ptr->entry_watched_for_removal = NULL;
} /* end if */
if((!entry_ptr->flush_me_last || (entry_ptr->flush_me_last && cache_ptr->num_last_entries >= cache_ptr->slist_len))
&& entry_ptr->flush_dep_nchildren == 0 && entry_ptr->ring == ring) {
if(entry_ptr->is_protected) {
/* we have major problems -- but lets flush and
* destroy everything we can before we flag an
* error.
*/
protected_entries++;
if(!entry_ptr->in_slist)
HDassert(!(entry_ptr->is_dirty));
} /* end if */
} /* end while loop scanning hash table bin */
else if(!(entry_ptr->is_pinned)) {
/* if *entry_ptr is dirty, it is possible
* that one or more other entries may be
* either removed from the cache, loaded
* into the cache, or moved to a new location
* in the file as a side effect of the flush.
*
* It's also possible that removing a clean
* entry will remove the last child of a proxy
* entry, allowing it to be removed also and
* invalidating the next_entry_ptr.
*
* If either of these happen, and one of the target
* or proxy entries happens to be the next entry in
* the hash bucket, we could either find ourselves
* either scanning a non-existant entry, scanning
* through a different bucket, or skipping an entry.
*
* Neither of these are good, so restart the
* the scan at the head of the hash bucket
* after the flush if we detect that the next_entry_ptr
* becomes invalid.
*
* This is not as inefficient at it might seem,
* as hash buckets typically have at most two
* or three entries.
*/
cache_ptr->entry_watched_for_removal = next_entry_ptr;
if(H5C__flush_single_entry(f, dxpl_id, entry_ptr, (cooked_flags | H5C__DURING_FLUSH_FLAG | H5C__FLUSH_INVALIDATE_FLAG | H5C__DEL_FROM_SLIST_ON_DESTROY_FLAG)) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "Entry flush destroy failed.")
/* Restart the index list scan if necessary. Must
* do this if the next entry is evicted, and also if
* one or more entries are inserted, loaded, or moved
* as these operations can result in part of the scan
* being skipped -- which can cause a spurious failure
* if this results in the size of the pinned entry
* failing to decline during the pass.
*/
if((NULL != next_entry_ptr && NULL == cache_ptr->entry_watched_for_removal)
|| (cache_ptr->entries_loaded_counter > 0)
|| (cache_ptr->entries_inserted_counter > 0)
|| (cache_ptr->entries_relocated_counter > 0)) {
next_entry_ptr = cache_ptr->il_head;
cache_ptr->entries_loaded_counter = 0;
cache_ptr->entries_inserted_counter = 0;
cache_ptr->entries_relocated_counter = 0;
H5C__UPDATE_STATS_FOR_INDEX_SCAN_RESTART(cache_ptr)
} /* end if */
else
cache_ptr->entry_watched_for_removal = NULL;
} /* end if */
} /* end if */
} /* end for loop scanning hash table */
/* We can't do anything if entries are pinned. The
@ -5488,32 +5510,33 @@ H5C_flush_invalidate_ring(const H5F_t * f, hid_t dxpl_id, H5C_ring_t ring,
* of pinned entries from pass to pass. If it stops
* shrinking before it hits zero, we scream and die.
*/
old_ring_pel_len = cur_ring_pel_len;
old_ring_pel_len = cur_ring_pel_len;
entry_ptr = cache_ptr->pel_head_ptr;
cur_ring_pel_len = 0;
while(entry_ptr != NULL) {
HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
HDassert(entry_ptr->ring >= ring);
if(entry_ptr->ring == ring)
if(entry_ptr->ring == ring)
cur_ring_pel_len++;
entry_ptr = entry_ptr->next;
} /* end while */
if((cur_ring_pel_len > 0) && (cur_ring_pel_len >= old_ring_pel_len)) {
/* Check if the number of pinned entries in the ring is positive, and
* it is not declining. Scream and die if so.
*/
if(cur_ring_pel_len > 0 && cur_ring_pel_len >= old_ring_pel_len) {
/* Don't error if allowed to have pinned entries remaining */
if(evict_flags)
if(evict_flags)
HGOTO_DONE(TRUE)
/* The number of pinned entries in the ring is positive, and
* it is not declining. Scream and die.
*/
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "Pinned entry count not decreasing, cur_ring_pel_len = %d, old_ring_pel_len = %d, ring = %d", (int)cur_ring_pel_len, (int)old_ring_pel_len, (int)ring)
} /* end if */
HDassert(protected_entries == cache_ptr->pl_len);
if((protected_entries > 0) && (protected_entries == cache_ptr->index_len))
if(protected_entries > 0 && protected_entries == cache_ptr->index_len)
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "Only protected entries left in cache, protected_entries = %d", (int)protected_entries)
} /* main while loop */
@ -5719,7 +5742,7 @@ H5C_flush_ring(H5F_t *f, hid_t dxpl_id, H5C_ring_t ring, unsigned flags)
if(!flush_marked_entries || entry_ptr->flush_marker)
HDassert(entry_ptr->ring >= ring);
/* advance node pointer now, before we delete its target
/* Advance node pointer now, before we delete its target
* from the slist.
*/
node_ptr = H5SL_next(node_ptr);
@ -5728,19 +5751,26 @@ H5C_flush_ring(H5F_t *f, hid_t dxpl_id, H5C_ring_t ring, unsigned flags)
if(NULL == next_entry_ptr)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "next_entry_ptr == NULL ?!?!")
HDassert(next_entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
HDassert(next_entry_ptr->is_dirty);
HDassert(next_entry_ptr->in_slist);
if(!flush_marked_entries || next_entry_ptr->flush_marker)
HDassert(next_entry_ptr->ring >= ring);
HDassert(entry_ptr != next_entry_ptr);
} /* end if */
else
next_entry_ptr = NULL;
if(((!flush_marked_entries) || (entry_ptr->flush_marker)) &&
((!entry_ptr->flush_me_last) ||
(entry_ptr->flush_me_last &&
((cache_ptr->num_last_entries >= cache_ptr->slist_len) ||
(flush_marked_entries && entry_ptr->flush_marker)))) &&
( ( entry_ptr->flush_dep_nchildren == 0 ) ||
( entry_ptr->flush_dep_ndirty_children == 0 ) ) &&
(entry_ptr->ring == ring)) {
if((!flush_marked_entries || entry_ptr->flush_marker)
&& (!entry_ptr->flush_me_last ||
(entry_ptr->flush_me_last
&& (cache_ptr->num_last_entries >= cache_ptr->slist_len
|| (flush_marked_entries && entry_ptr->flush_marker))))
&& (entry_ptr->flush_dep_nchildren == 0
|| entry_ptr->flush_dep_ndirty_children == 0)
&& entry_ptr->ring == ring) {
if(entry_ptr->is_protected) {
/* we probably have major problems -- but lets
* flush everything we can before we decide
@ -5930,7 +5960,7 @@ H5C__flush_single_entry(const H5F_t *f, hid_t dxpl_id, H5C_cache_entry_t *entry_
#endif /* H5C_DO_SANITY_CHECKS */
if(entry_ptr->is_protected) {
HDassert(!entry_ptr->is_protected);
HDassert(!entry_ptr->is_protected);
/* Attempt to flush a protected entry -- scream and die. */
HGOTO_ERROR(H5E_CACHE, H5E_PROTECT, FAIL, "Attempt to flush a protected entry.")
@ -6117,11 +6147,11 @@ H5C__flush_single_entry(const H5F_t *f, hid_t dxpl_id, H5C_cache_entry_t *entry_
HGOTO_ERROR(H5E_CACHE, H5E_CANTNOTIFY, FAIL, "can't notify client about entry dirty flag cleared")
/* Propagate the clean flag up the flush dependency chain if appropriate */
HDassert(entry_ptr->flush_dep_ndirty_children == 0);
if(entry_ptr->flush_dep_nparents > 0)
if(H5C__mark_flush_dep_clean(entry_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTMARKDIRTY, FAIL, "Can't propagate flush dep clean flag")
} /* end if */
HDassert(entry_ptr->flush_dep_ndirty_children == 0);
if(entry_ptr->flush_dep_nparents > 0)
if(H5C__mark_flush_dep_clean(entry_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTMARKDIRTY, FAIL, "Can't propagate flush dep clean flag")
} /* end if */
} /* end else */
/* reset the flush_in progress flag */
@ -7836,7 +7866,8 @@ H5C__generate_image(const H5F_t *f, H5C_t *cache_ptr, H5C_cache_entry_t *entry_p
* for a move
*/
if(serialize_flags & H5C__SERIALIZE_MOVED_FLAG) {
H5C__UPDATE_STATS_FOR_MOVE(cache_ptr, entry_ptr);
/* Update stats and entries relocated counter */
H5C__UPDATE_STATS_FOR_MOVE(cache_ptr, entry_ptr)
/* We must update cache data structures for the change in address */
if(entry_ptr->addr == old_addr) {

View File

@ -656,7 +656,8 @@ if ( ( ( ( (head_ptr) == NULL ) || ( (tail_ptr) == NULL ) ) && \
((cache_ptr)->cache_flush_moves[(entry_ptr)->type->id])++; \
if ( entry_ptr->flush_in_progress ) \
((cache_ptr)->entry_flush_moves[(entry_ptr)->type->id])++; \
(((cache_ptr)->moves)[(entry_ptr)->type->id])++;
(((cache_ptr)->moves)[(entry_ptr)->type->id])++; \
(cache_ptr)->entries_relocated_counter++;
#define H5C__UPDATE_STATS_FOR_ENTRY_SIZE_CHANGE(cache_ptr, entry_ptr, new_size)\
if ( cache_ptr->flush_in_progress ) \
@ -782,6 +783,7 @@ if ( ( ( ( (head_ptr) == NULL ) || ( (tail_ptr) == NULL ) ) && \
((cache_ptr)->max_size)[(entry_ptr)->type->id] ) \
((cache_ptr)->max_size)[(entry_ptr)->type->id] \
= (entry_ptr)->size; \
cache_ptr->entries_inserted_counter++; \
}
#define H5C__UPDATE_STATS_FOR_PROTECT(cache_ptr, entry_ptr, hit) \
@ -866,6 +868,7 @@ if ( ( ( ( (head_ptr) == NULL ) || ( (tail_ptr) == NULL ) ) && \
(cache_ptr)->max_slist_len = (cache_ptr)->slist_len; \
if ( (cache_ptr)->slist_size > (cache_ptr)->max_slist_size ) \
(cache_ptr)->max_slist_size = (cache_ptr)->slist_size; \
cache_ptr->entries_inserted_counter++; \
}
#define H5C__UPDATE_STATS_FOR_PROTECT(cache_ptr, entry_ptr, hit) \
@ -4057,6 +4060,15 @@ typedef struct H5C_tag_info_t {
* this field will be reset every automatic resize epoch.
*
*
* entries_loaded_counter: Number of entries loaded into the cache
* since the last time this field was reset.
*
* entries_inserted_counter: Number of entries inserted into the cache
* since the last time this field was reset.
*
* entries relocated_counter: Number of entries whose base address has
* been changed since the last time this field was reset.
*
* Statistics collection fields:
*
* When enabled, these fields are used to collect statistics as described
@ -4439,6 +4451,9 @@ struct H5C_t {
int64_t cache_hits;
int64_t cache_accesses;
int64_t entries_loaded_counter;
int64_t entries_inserted_counter;
int64_t entries_relocated_counter;
#if H5C_COLLECT_CACHE_STATS
/* stats fields */
int64_t hits[H5C__MAX_NUM_TYPE_IDS + 1];