mirror of
https://github.com/HDFGroup/hdf5.git
synced 2025-02-05 15:42:32 +08:00
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:
parent
baaf735015
commit
ce3877d298
237
src/H5C.c
237
src/H5C.c
@ -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) {
|
||||
|
17
src/H5Cpkg.h
17
src/H5Cpkg.h
@ -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];
|
||||
|
Loading…
Reference in New Issue
Block a user