From ce3877d298b23e9007424e0707206fc91f8a1328 Mon Sep 17 00:00:00 2001 From: Quincey Koziol Date: Mon, 2 Jan 2017 12:06:08 -0800 Subject: 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. --- src/H5C.c | 235 +++++++++++++++++++++++++++++++++-------------------------- src/H5Cpkg.h | 17 ++++- 2 files changed, 149 insertions(+), 103 deletions(-) diff --git a/src/H5C.c b/src/H5C.c index e753da4..03d7b7a 100644 --- a/src/H5C.c +++ b/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; + 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 */ + 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 while loop scanning hash table bin */ + } /* 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) { diff --git a/src/H5Cpkg.h b/src/H5Cpkg.h index e30ca8d..0782b09 100644 --- a/src/H5Cpkg.h +++ b/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]; -- cgit v0.12