From 8265779dc0cf1612f0d19b22956c30e9c851826c Mon Sep 17 00:00:00 2001 From: John Mainzer Date: Thu, 5 Aug 2004 13:13:49 -0500 Subject: [svn-r9022] Purpose: Optimization of the cache code in H5C. Description: Cache was running too slowly. Solution: Added a hash table for indexing. Retained the tree, but only for dirty entries. As we need to flush dirty entries in increasing address order, this is sufficient. Updated statistics collection code for the above. Converted a number of local functions into macros to avoid the function call overhead. Added code to disable the clean and dirty LRU lists in serial mode. Updated test code to account for the above changes. Platforms tested: h5committested + serial, parallel, and fp on Eirene. Misc. update: --- src/H5ACprivate.h | 19 + src/H5C.c | 5469 ++++++++++++++++++++++++++++++----------------------- src/H5Cprivate.h | 72 +- src/H5F.c | 4 +- 4 files changed, 3177 insertions(+), 2387 deletions(-) diff --git a/src/H5ACprivate.h b/src/H5ACprivate.h index 6acc3e3..bf1cffe 100644 --- a/src/H5ACprivate.h +++ b/src/H5ACprivate.h @@ -46,6 +46,25 @@ #define H5AC_OHDR_ID 4 /*object header */ #define H5AC_NTYPES 5 +/* H5AC_DUMP_STATS_ON_CLOSE should always be FALSE when + * H5C_COLLECT_CACHE_STATS is FALSE. + * + * When H5C_COLLECT_CACHE_STATS is TRUE, H5AC_DUMP_STATS_ON_CLOSE must + * be FALSE for "make check" to succeed, but may be set to TRUE at other + * times for debugging purposes. + * + * Hence the following, somewhat odd set of #defines. + */ +#if H5C_COLLECT_CACHE_STATS + +#define H5AC_DUMP_STATS_ON_CLOSE 0 + +#else /* H5C_COLLECT_CACHE_STATS */ + +#define H5AC_DUMP_STATS_ON_CLOSE 0 + +#endif /* H5C_COLLECT_CACHE_STATS */ + /* * Class methods pertaining to caching. Each type of cached object will * have a constant variable with permanent life-span that describes how diff --git a/src/H5C.c b/src/H5C.c index c014ddd..8c9c74b 100644 --- a/src/H5C.c +++ b/src/H5C.c @@ -95,7 +95,6 @@ #include "H5Pprivate.h" /* Property lists */ #include "H5TBprivate.h" /* Threaded, Balanced, Binary Trees */ - /**************************************************************************** * @@ -121,29 +120,29 @@ #if H5C_DO_SANITY_CHECKS -#define H5C__DLL_PRE_REMOVE_SC(entry_ptr, head_ptr, tail_ptr, len, Size) \ -if ( ( (head_ptr) == NULL ) || \ - ( (tail_ptr) == NULL ) || \ - ( (entry_ptr) == NULL ) || \ - ( (len) <= 0 ) || \ - ( (Size) < (entry_ptr)->size ) || \ - ( ( (Size) == (entry_ptr)->size ) && ( (len) != 1 ) ) || \ - ( ( (entry_ptr)->prev == NULL ) && ( (head_ptr) != (entry_ptr) ) ) || \ - ( ( (entry_ptr)->next == NULL ) && ( (tail_ptr) != (entry_ptr) ) ) || \ - ( ( (len) == 1 ) && \ - ( ! ( ( (head_ptr) == (entry_ptr) ) && \ - ( (tail_ptr) == (entry_ptr) ) && \ - ( (entry_ptr)->next == NULL ) && \ - ( (entry_ptr)->prev == NULL ) && \ - ( (Size) == (entry_ptr)->size ) \ - ) \ - ) \ - ) \ - ) { \ - HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "DLL pre remove SC failed") \ +#define H5C__DLL_PRE_REMOVE_SC(entry_ptr, head_ptr, tail_ptr, len, Size, fv) \ +if ( ( (head_ptr) == NULL ) || \ + ( (tail_ptr) == NULL ) || \ + ( (entry_ptr) == NULL ) || \ + ( (len) <= 0 ) || \ + ( (Size) < (entry_ptr)->size ) || \ + ( ( (Size) == (entry_ptr)->size ) && ( (len) != 1 ) ) || \ + ( ( (entry_ptr)->prev == NULL ) && ( (head_ptr) != (entry_ptr) ) ) || \ + ( ( (entry_ptr)->next == NULL ) && ( (tail_ptr) != (entry_ptr) ) ) || \ + ( ( (len) == 1 ) && \ + ( ! ( ( (head_ptr) == (entry_ptr) ) && \ + ( (tail_ptr) == (entry_ptr) ) && \ + ( (entry_ptr)->next == NULL ) && \ + ( (entry_ptr)->prev == NULL ) && \ + ( (Size) == (entry_ptr)->size ) \ + ) \ + ) \ + ) \ + ) { \ + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, (fv), "DLL pre remove SC failed") \ } -#define H5C__DLL_SC(head_ptr, tail_ptr, len, Size) \ +#define H5C__DLL_SC(head_ptr, tail_ptr, len, Size, fv) \ if ( ( ( ( (head_ptr) == NULL ) || ( (tail_ptr) == NULL ) ) && \ ( (head_ptr) != (tail_ptr) ) \ ) || \ @@ -160,119 +159,122 @@ if ( ( ( ( (head_ptr) == NULL ) || ( (tail_ptr) == NULL ) ) && \ ) \ ) \ ) { \ - HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "DLL sanity check failed") \ + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, (fv), "DLL sanity check failed") \ } -#define H5C__DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size) \ -if ( ( (entry_ptr) == NULL ) || \ - ( (entry_ptr)->next != NULL ) || \ - ( (entry_ptr)->prev != NULL ) || \ - ( ( ( (head_ptr) == NULL ) || ( (tail_ptr) == NULL ) ) && \ - ( (head_ptr) != (tail_ptr) ) \ - ) || \ - ( (len) < 0 ) || \ - ( ( (len) == 1 ) && \ - ( ( (head_ptr) != (tail_ptr) ) || ( (Size) <= 0 ) || \ - ( (head_ptr) == NULL ) || ( (head_ptr)->size != (Size) ) \ - ) \ - ) || \ - ( ( (len) >= 1 ) && \ - ( ( (head_ptr) == NULL ) || ( (head_ptr)->prev != NULL ) || \ - ( (tail_ptr) == NULL ) || ( (tail_ptr)->next != NULL ) \ - ) \ - ) \ - ) { \ - HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "DLL pre insert SC failed") \ +#define H5C__DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size, fv) \ +if ( ( (entry_ptr) == NULL ) || \ + ( (entry_ptr)->next != NULL ) || \ + ( (entry_ptr)->prev != NULL ) || \ + ( ( ( (head_ptr) == NULL ) || ( (tail_ptr) == NULL ) ) && \ + ( (head_ptr) != (tail_ptr) ) \ + ) || \ + ( (len) < 0 ) || \ + ( ( (len) == 1 ) && \ + ( ( (head_ptr) != (tail_ptr) ) || ( (Size) <= 0 ) || \ + ( (head_ptr) == NULL ) || ( (head_ptr)->size != (Size) ) \ + ) \ + ) || \ + ( ( (len) >= 1 ) && \ + ( ( (head_ptr) == NULL ) || ( (head_ptr)->prev != NULL ) || \ + ( (tail_ptr) == NULL ) || ( (tail_ptr)->next != NULL ) \ + ) \ + ) \ + ) { \ + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, (fv), "DLL pre insert SC failed") \ } #else /* H5C_DO_SANITY_CHECKS */ -#define H5C__DLL_PRE_REMOVE_SC(entry_ptr, head_ptr, tail_ptr, len, Size) -#define H5C__DLL_SC(head_ptr, tail_ptr, len, Size) -#define H5C__DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size) +#define H5C__DLL_PRE_REMOVE_SC(entry_ptr, head_ptr, tail_ptr, len, Size, fv) +#define H5C__DLL_SC(head_ptr, tail_ptr, len, Size, fv) +#define H5C__DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size, fv) #endif /* H5C_DO_SANITY_CHECKS */ -#define H5C__DLL_APPEND(entry_ptr, head_ptr, tail_ptr, len, Size) \ - H5C__DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size) \ - if ( (head_ptr) == NULL ) \ - { \ - (head_ptr) = (entry_ptr); \ - (tail_ptr) = (entry_ptr); \ - } \ - else \ - { \ - (tail_ptr)->next = (entry_ptr); \ - (entry_ptr)->prev = (tail_ptr); \ - (tail_ptr) = (entry_ptr); \ - } \ - (len)++; \ +#define H5C__DLL_APPEND(entry_ptr, head_ptr, tail_ptr, len, Size, fail_val) \ + H5C__DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size, \ + fail_val) \ + if ( (head_ptr) == NULL ) \ + { \ + (head_ptr) = (entry_ptr); \ + (tail_ptr) = (entry_ptr); \ + } \ + else \ + { \ + (tail_ptr)->next = (entry_ptr); \ + (entry_ptr)->prev = (tail_ptr); \ + (tail_ptr) = (entry_ptr); \ + } \ + (len)++; \ (Size) += (entry_ptr)->size; -#define H5C__DLL_PREPEND(entry_ptr, head_ptr, tail_ptr, len, Size) \ - H5C__DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size) \ - if ( (head_ptr) == NULL ) \ - { \ - (head_ptr) = (entry_ptr); \ - (tail_ptr) = (entry_ptr); \ - } \ - else \ - { \ - (head_ptr)->prev = (entry_ptr); \ - (entry_ptr)->next = (head_ptr); \ - (head_ptr) = (entry_ptr); \ - } \ - (len)++; \ +#define H5C__DLL_PREPEND(entry_ptr, head_ptr, tail_ptr, len, Size, fail_val) \ + H5C__DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size, \ + fail_val) \ + if ( (head_ptr) == NULL ) \ + { \ + (head_ptr) = (entry_ptr); \ + (tail_ptr) = (entry_ptr); \ + } \ + else \ + { \ + (head_ptr)->prev = (entry_ptr); \ + (entry_ptr)->next = (head_ptr); \ + (head_ptr) = (entry_ptr); \ + } \ + (len)++; \ (Size) += entry_ptr->size; -#define H5C__DLL_REMOVE(entry_ptr, head_ptr, tail_ptr, len, Size) \ - H5C__DLL_PRE_REMOVE_SC(entry_ptr, head_ptr, tail_ptr, len, Size) \ - { \ - if ( (head_ptr) == (entry_ptr) ) \ - { \ - (head_ptr) = (entry_ptr)->next; \ - if ( (head_ptr) != NULL ) \ - { \ - (head_ptr)->prev = NULL; \ - } \ - } \ - else \ - { \ - (entry_ptr)->prev->next = (entry_ptr)->next; \ - } \ - if ( (tail_ptr) == (entry_ptr) ) \ - { \ - (tail_ptr) = (entry_ptr)->prev; \ - if ( (tail_ptr) != NULL ) \ - { \ - (tail_ptr)->next = NULL; \ - } \ - } \ - else \ - { \ - (entry_ptr)->next->prev = (entry_ptr)->prev; \ - } \ - entry_ptr->next = NULL; \ - entry_ptr->prev = NULL; \ - (len)--; \ - (Size) -= entry_ptr->size; \ +#define H5C__DLL_REMOVE(entry_ptr, head_ptr, tail_ptr, len, Size, fail_val) \ + H5C__DLL_PRE_REMOVE_SC(entry_ptr, head_ptr, tail_ptr, len, Size, \ + fail_val) \ + { \ + if ( (head_ptr) == (entry_ptr) ) \ + { \ + (head_ptr) = (entry_ptr)->next; \ + if ( (head_ptr) != NULL ) \ + { \ + (head_ptr)->prev = NULL; \ + } \ + } \ + else \ + { \ + (entry_ptr)->prev->next = (entry_ptr)->next; \ + } \ + if ( (tail_ptr) == (entry_ptr) ) \ + { \ + (tail_ptr) = (entry_ptr)->prev; \ + if ( (tail_ptr) != NULL ) \ + { \ + (tail_ptr)->next = NULL; \ + } \ + } \ + else \ + { \ + (entry_ptr)->next->prev = (entry_ptr)->prev; \ + } \ + entry_ptr->next = NULL; \ + entry_ptr->prev = NULL; \ + (len)--; \ + (Size) -= entry_ptr->size; \ } #if H5C_DO_SANITY_CHECKS -#define H5C__AUX_DLL_PRE_REMOVE_SC(entry_ptr, head_ptr, tail_ptr, len, Size) \ -if ( ( (head_ptr) == NULL ) || \ +#define H5C__AUX_DLL_PRE_REMOVE_SC(entry_ptr, hd_ptr, tail_ptr, len, Size, fv) \ +if ( ( (hd_ptr) == NULL ) || \ ( (tail_ptr) == NULL ) || \ ( (entry_ptr) == NULL ) || \ ( (len) <= 0 ) || \ ( (Size) < (entry_ptr)->size ) || \ ( ( (Size) == (entry_ptr)->size ) && ( ! ( (len) == 1 ) ) ) || \ - ( ( (entry_ptr)->aux_prev == NULL ) && ( (head_ptr) != (entry_ptr) ) ) || \ + ( ( (entry_ptr)->aux_prev == NULL ) && ( (hd_ptr) != (entry_ptr) ) ) || \ ( ( (entry_ptr)->aux_next == NULL ) && ( (tail_ptr) != (entry_ptr) ) ) || \ ( ( (len) == 1 ) && \ - ( ! ( ( (head_ptr) == (entry_ptr) ) && ( (tail_ptr) == (entry_ptr) ) && \ + ( ! ( ( (hd_ptr) == (entry_ptr) ) && ( (tail_ptr) == (entry_ptr) ) && \ ( (entry_ptr)->aux_next == NULL ) && \ ( (entry_ptr)->aux_prev == NULL ) && \ ( (Size) == (entry_ptr)->size ) \ @@ -280,10 +282,10 @@ if ( ( (head_ptr) == NULL ) || \ ) \ ) \ ) { \ - HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "aux DLL pre remove SC failed") \ + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, (fv), "aux DLL pre remove SC failed") \ } -#define H5C__AUX_DLL_SC(head_ptr, tail_ptr, len, Size) \ +#define H5C__AUX_DLL_SC(head_ptr, tail_ptr, len, Size, fv) \ if ( ( ( ( (head_ptr) == NULL ) || ( (tail_ptr) == NULL ) ) && \ ( (head_ptr) != (tail_ptr) ) \ ) || \ @@ -300,58 +302,60 @@ if ( ( ( ( (head_ptr) == NULL ) || ( (tail_ptr) == NULL ) ) && \ ) \ ) \ ) { \ - HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "AUX DLL sanity check failed") \ + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, (fv), "AUX DLL sanity check failed") \ } -#define H5C__AUX_DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size) \ -if ( ( (entry_ptr) == NULL ) || \ - ( (entry_ptr)->aux_next != NULL ) || \ - ( (entry_ptr)->aux_prev != NULL ) || \ - ( ( ( (head_ptr) == NULL ) || ( (tail_ptr) == NULL ) ) && \ - ( (head_ptr) != (tail_ptr) ) \ - ) || \ - ( (len) < 0 ) || \ - ( ( (len) == 1 ) && \ - ( ( (head_ptr) != (tail_ptr) ) || ( (Size) <= 0 ) || \ - ( (head_ptr) == NULL ) || ( (head_ptr)->size != (Size) ) \ - ) \ - ) || \ - ( ( (len) >= 1 ) && \ - ( ( (head_ptr) == NULL ) || ( (head_ptr)->aux_prev != NULL ) || \ - ( (tail_ptr) == NULL ) || ( (tail_ptr)->aux_next != NULL ) \ - ) \ - ) \ - ) { \ - HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "AUX DLL pre insert SC failed") \ +#define H5C__AUX_DLL_PRE_INSERT_SC(entry_ptr, hd_ptr, tail_ptr, len, Size, fv) \ +if ( ( (entry_ptr) == NULL ) || \ + ( (entry_ptr)->aux_next != NULL ) || \ + ( (entry_ptr)->aux_prev != NULL ) || \ + ( ( ( (hd_ptr) == NULL ) || ( (tail_ptr) == NULL ) ) && \ + ( (hd_ptr) != (tail_ptr) ) \ + ) || \ + ( (len) < 0 ) || \ + ( ( (len) == 1 ) && \ + ( ( (hd_ptr) != (tail_ptr) ) || ( (Size) <= 0 ) || \ + ( (hd_ptr) == NULL ) || ( (hd_ptr)->size != (Size) ) \ + ) \ + ) || \ + ( ( (len) >= 1 ) && \ + ( ( (hd_ptr) == NULL ) || ( (hd_ptr)->aux_prev != NULL ) || \ + ( (tail_ptr) == NULL ) || ( (tail_ptr)->aux_next != NULL ) \ + ) \ + ) \ + ) { \ + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, (fv), "AUX DLL pre insert SC failed") \ } #else /* H5C_DO_SANITY_CHECKS */ -#define H5C__AUX_DLL_PRE_REMOVE_SC(entry_ptr, head_ptr, tail_ptr, len, Size) -#define H5C__AUX_DLL_SC(head_ptr, tail_ptr, len, Size) -#define H5C__AUX_DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size) +#define H5C__AUX_DLL_PRE_REMOVE_SC(entry_ptr, hd_ptr, tail_ptr, len, Size, fv) +#define H5C__AUX_DLL_SC(head_ptr, tail_ptr, len, Size, fv) +#define H5C__AUX_DLL_PRE_INSERT_SC(entry_ptr, hd_ptr, tail_ptr, len, Size, fv) #endif /* H5C_DO_SANITY_CHECKS */ -#define H5C__AUX_DLL_APPEND(entry_ptr, head_ptr, tail_ptr, len, Size) \ - H5C__AUX_DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size) \ - if ( (head_ptr) == NULL ) \ - { \ - (head_ptr) = (entry_ptr); \ - (tail_ptr) = (entry_ptr); \ - } \ - else \ - { \ - (tail_ptr)->aux_next = (entry_ptr); \ - (entry_ptr)->aux_prev = (tail_ptr); \ - (tail_ptr) = (entry_ptr); \ - } \ - (len)++; \ +#define H5C__AUX_DLL_APPEND(entry_ptr, head_ptr, tail_ptr, len, Size, fail_val)\ + H5C__AUX_DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size, \ + fail_val) \ + if ( (head_ptr) == NULL ) \ + { \ + (head_ptr) = (entry_ptr); \ + (tail_ptr) = (entry_ptr); \ + } \ + else \ + { \ + (tail_ptr)->aux_next = (entry_ptr); \ + (entry_ptr)->aux_prev = (tail_ptr); \ + (tail_ptr) = (entry_ptr); \ + } \ + (len)++; \ (Size) += entry_ptr->size; -#define H5C__AUX_DLL_PREPEND(entry_ptr, head_ptr, tail_ptr, len, Size) \ - H5C__AUX_DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size) \ +#define H5C__AUX_DLL_PREPEND(entry_ptr, head_ptr, tail_ptr, len, Size, fv) \ + H5C__AUX_DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size, \ + fv) \ if ( (head_ptr) == NULL ) \ { \ (head_ptr) = (entry_ptr); \ @@ -366,8 +370,9 @@ if ( ( (entry_ptr) == NULL ) || \ (len)++; \ (Size) += entry_ptr->size; -#define H5C__AUX_DLL_REMOVE(entry_ptr, head_ptr, tail_ptr, len, Size) \ - H5C__AUX_DLL_PRE_REMOVE_SC(entry_ptr, head_ptr, tail_ptr, len, Size) \ +#define H5C__AUX_DLL_REMOVE(entry_ptr, head_ptr, tail_ptr, len, Size, fv) \ + H5C__AUX_DLL_PRE_REMOVE_SC(entry_ptr, head_ptr, tail_ptr, len, Size, \ + fv) \ { \ if ( (head_ptr) == (entry_ptr) ) \ { \ @@ -405,7 +410,7 @@ if ( ( (entry_ptr) == NULL ) || \ * Stats collection macros * * The following macros must handle stats collection when this collection - * is encabled, and evaluate to the empty string when it is not. + * is enabled, and evaluate to the empty string when it is not. * ***********************************************************************/ @@ -416,11 +421,41 @@ if ( ( (entry_ptr) == NULL ) || \ if ( (cache_ptr)->index_len > (cache_ptr)->max_index_len ) \ (cache_ptr)->max_index_len = (cache_ptr)->index_len; \ if ( (cache_ptr)->index_size > (cache_ptr)->max_index_size ) \ - (cache_ptr)->max_index_size = (cache_ptr)->index_size; + (cache_ptr)->max_index_size = (cache_ptr)->index_size; \ + if ( (cache_ptr)->tree_len > (cache_ptr)->max_tree_len ) \ + (cache_ptr)->max_tree_len = (cache_ptr)->tree_len; \ + if ( (cache_ptr)->tree_size > (cache_ptr)->max_tree_size ) \ + (cache_ptr)->max_tree_size = (cache_ptr)->tree_size; \ + if ( (entry_ptr)->size > \ + ((cache_ptr)->max_size)[(entry_ptr)->type->id] ) { \ + ((cache_ptr)->max_size)[(entry_ptr)->type->id] \ + = (entry_ptr)->size; \ + } + +#define H5C__UPDATE_STATS_FOR_UNPROTECT(cache_ptr) \ + if ( (cache_ptr)->tree_len > (cache_ptr)->max_tree_len ) \ + (cache_ptr)->max_tree_len = (cache_ptr)->tree_len; \ + if ( (cache_ptr)->tree_size > (cache_ptr)->max_tree_size ) \ + (cache_ptr)->max_tree_size = (cache_ptr)->tree_size; #define H5C__UPDATE_STATS_FOR_RENAME(cache_ptr, entry_ptr) \ (((cache_ptr)->renames)[(entry_ptr)->type->id])++; +#define H5C__UPDATE_STATS_FOR_HT_INSERTION(cache_ptr) \ + (cache_ptr)->total_ht_insertions++; + +#define H5C__UPDATE_STATS_FOR_HT_DELETION(cache_ptr) \ + (cache_ptr)->total_ht_deletions++; + +#define H5C__UPDATE_STATS_FOR_HT_SEARCH(cache_ptr, success, depth) \ + if ( success ) { \ + (cache_ptr)->successful_ht_searches++; \ + (cache_ptr)->total_successful_ht_search_depth += depth; \ + } else { \ + (cache_ptr)->failed_ht_searches++; \ + (cache_ptr)->total_failed_ht_search_depth += depth; \ + } + #if H5C_COLLECT_CACHE_ENTRY_STATS #define H5C__RESET_CACHE_ENTRY_STATS(entry_ptr) \ @@ -458,6 +493,11 @@ if ( ( (entry_ptr) == NULL ) || \ ((cache_ptr)->max_flushes)[(entry_ptr)->type->id] \ = (entry_ptr)->flushes; \ } \ + if ( (entry_ptr)->size > \ + ((cache_ptr)->max_size)[(entry_ptr)->type->id] ) { \ + ((cache_ptr)->max_size)[(entry_ptr)->type->id] \ + = (entry_ptr)->size; \ + } \ #define H5C__UPDATE_STATS_FOR_PROTECT(cache_ptr, entry_ptr, hit) \ if ( hit ) \ @@ -472,6 +512,11 @@ if ( ( (entry_ptr) == NULL ) || \ (cache_ptr)->max_pl_len = (cache_ptr)->pl_len; \ if ( (cache_ptr)->pl_size > (cache_ptr)->max_pl_size ) \ (cache_ptr)->max_pl_size = (cache_ptr)->pl_size; \ + if ( (entry_ptr)->size > \ + ((cache_ptr)->max_size)[(entry_ptr)->type->id] ) { \ + ((cache_ptr)->max_size)[(entry_ptr)->type->id] \ + = (entry_ptr)->size; \ + } \ ((entry_ptr)->accesses)++; #else /* H5C_COLLECT_CACHE_ENTRY_STATS */ @@ -506,7 +551,11 @@ if ( ( (entry_ptr) == NULL ) || \ #else /* H5C_COLLECT_CACHE_STATS */ #define H5C__RESET_CACHE_ENTRY_STATS(entry_ptr) +#define H5C__UPDATE_STATS_FOR_UNPROTECT(cache_ptr) #define H5C__UPDATE_STATS_FOR_RENAME(cache_ptr, entry_ptr) +#define H5C__UPDATE_STATS_FOR_HT_INSERTION(cache_ptr) +#define H5C__UPDATE_STATS_FOR_HT_DELETION(cache_ptr) +#define H5C__UPDATE_STATS_FOR_HT_SEARCH(cache_ptr, success, depth) #define H5C__UPDATE_STATS_FOR_INSERTION(cache_ptr, entry_ptr) #define H5C__UPDATE_STATS_FOR_CLEAR(cache_ptr, entry_ptr) #define H5C__UPDATE_STATS_FOR_FLUSH(cache_ptr, entry_ptr) @@ -516,1542 +565,2232 @@ if ( ( (entry_ptr) == NULL ) || \ #endif /* H5C_COLLECT_CACHE_STATS */ -/**************************************************************************** +/*********************************************************************** * - * structure H5C_t + * Hash table access and manipulation macros: * - * Catchall structure for all variables specific to an instance of the cache. + * The following macros handle searches, insertions, and deletion in + * the hash table. * - * While the individual fields of the structure are discussed below, the - * following overview may be helpful. + * When modifying these macros, remember to modify the similar macros + * in tst/cache.c * - * Entries in the cache are stored in an instance of H5TB_TREE, indexed on - * the entry's disk address. While the H5TB_TREE is less efficient than - * hash table, it keeps the entries in address sorted order. As flushes - * in parallel mode are more efficient if they are issued in increasing - * address order, this is a significant benefit. Also the H5TB_TREE code - * was readily available, which reduced development time. + ***********************************************************************/ + +#define H5C__HASH_TABLE_LEN (32 * 1024) /* must be a power of 2 */ + +#define H5C__HASH_MASK ((size_t)(H5C__HASH_TABLE_LEN - 1) << 3) + +#define H5C__HASH_FCN(x) (int)(((x) & H5C__HASH_MASK) >> 3) + +#if H5C_DO_SANITY_CHECKS + +#define H5C__PRE_HT_INSERT_SC(cache_ptr, entry_ptr, fail_val) \ +if ( ( (cache_ptr) == NULL ) || \ + ( (cache_ptr)->magic != H5C__H5C_T_MAGIC ) || \ + ( (entry_ptr) == NULL ) || \ + ( ! H5F_addr_defined((entry_ptr)->addr) ) || \ + ( (entry_ptr)->ht_next != NULL ) || \ + ( (entry_ptr)->ht_prev != NULL ) || \ + ( (entry_ptr)->size <= 0 ) || \ + ( (k = H5C__HASH_FCN((entry_ptr)->addr)) < 0 ) || \ + ( k >= H5C__HASH_TABLE_LEN ) ) { \ + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, fail_val, \ + "Pre HT insert SC failed") \ +} + +#define H5C__PRE_HT_REMOVE_SC(cache_ptr, entry_ptr) \ +if ( ( (cache_ptr) == NULL ) || \ + ( (cache_ptr)->magic != H5C__H5C_T_MAGIC ) || \ + ( (cache_ptr)->index_len < 1 ) || \ + ( (entry_ptr) == NULL ) || \ + ( (cache_ptr)->index_size < (entry_ptr)->size ) || \ + ( ! H5F_addr_defined((entry_ptr)->addr) ) || \ + ( (entry_ptr)->size <= 0 ) || \ + ( H5C__HASH_FCN((entry_ptr)->addr) < 0 ) || \ + ( H5C__HASH_FCN((entry_ptr)->addr) >= H5C__HASH_TABLE_LEN ) || \ + ( ((cache_ptr)->index)[(H5C__HASH_FCN((entry_ptr)->addr))] \ + == NULL ) || \ + ( ( ((cache_ptr)->index)[(H5C__HASH_FCN((entry_ptr)->addr))] \ + != (entry_ptr) ) && \ + ( (entry_ptr)->ht_prev == NULL ) ) || \ + ( ( ((cache_ptr)->index)[(H5C__HASH_FCN((entry_ptr)->addr))] == \ + (entry_ptr) ) && \ + ( (entry_ptr)->ht_prev != NULL ) ) ) { \ + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Pre HT remove SC failed") \ +} + +#define H5C__PRE_HT_SEARCH_SC(cache_ptr, Addr, fail_val) \ +if ( ( (cache_ptr) == NULL ) || \ + ( (cache_ptr)->magic != H5C__H5C_T_MAGIC ) || \ + ( ! H5F_addr_defined(Addr) ) || \ + ( H5C__HASH_FCN(Addr) < 0 ) || \ + ( H5C__HASH_FCN(Addr) >= H5C__HASH_TABLE_LEN ) ) { \ + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, fail_val, "Pre HT search SC failed") \ +} + +#define H5C__POST_SUC_HT_SEARCH_SC(cache_ptr, entry_ptr, Addr, k, fail_val) \ +if ( ( (cache_ptr) == NULL ) || \ + ( (cache_ptr)->magic != H5C__H5C_T_MAGIC ) || \ + ( (cache_ptr)->index_len < 1 ) || \ + ( (entry_ptr) == NULL ) || \ + ( (cache_ptr)->index_size < (entry_ptr)->size ) || \ + ( H5F_addr_ne((entry_ptr)->addr, (Addr)) ) || \ + ( (entry_ptr)->size <= 0 ) || \ + ( ((cache_ptr)->index)[k] == NULL ) || \ + ( ( ((cache_ptr)->index)[k] != (entry_ptr) ) && \ + ( (entry_ptr)->ht_prev == NULL ) ) || \ + ( ( ((cache_ptr)->index)[k] == (entry_ptr) ) && \ + ( (entry_ptr)->ht_prev != NULL ) ) || \ + ( ( (entry_ptr)->ht_prev != NULL ) && \ + ( (entry_ptr)->ht_prev->ht_next != (entry_ptr) ) ) || \ + ( ( (entry_ptr)->ht_next != NULL ) && \ + ( (entry_ptr)->ht_next->ht_prev != (entry_ptr) ) ) ) { \ + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, fail_val, \ + "Post successful HT search SC failed") \ +} + +#define H5C__POST_HT_SHIFT_TO_FRONT(cache_ptr, entry_ptr, k, fail_val) \ +if ( ( (cache_ptr) == NULL ) || \ + ( ((cache_ptr)->index)[k] != (entry_ptr) ) || \ + ( (entry_ptr)->ht_prev != NULL ) ) { \ + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, fail_val, \ + "Post HT shift to front SC failed") \ +} + + +#else /* H5C_DO_SANITY_CHECKS */ + +#define H5C__PRE_HT_INSERT_SC(cache_ptr, entry_ptr, fail_val) +#define H5C__PRE_HT_REMOVE_SC(cache_ptr, entry_ptr) +#define H5C__PRE_HT_SEARCH_SC(cache_ptr, Addr, fail_val) +#define H5C__POST_SUC_HT_SEARCH_SC(cache_ptr, entry_ptr, Addr, k, fail_val) +#define H5C__POST_HT_SHIFT_TO_FRONT(cache_ptr, entry_ptr, k, fail_val) + +#endif /* H5C_DO_SANITY_CHECKS */ + + +#define H5C__INSERT_IN_INDEX(cache_ptr, entry_ptr, fail_val) \ +{ \ + int k; \ + H5C__PRE_HT_INSERT_SC(cache_ptr, entry_ptr, fail_val) \ + k = H5C__HASH_FCN((entry_ptr)->addr); \ + if ( ((cache_ptr)->index)[k] == NULL ) \ + { \ + ((cache_ptr)->index)[k] = (entry_ptr); \ + } \ + else \ + { \ + (entry_ptr)->ht_next = ((cache_ptr)->index)[k]; \ + (entry_ptr)->ht_next->ht_prev = (entry_ptr); \ + ((cache_ptr)->index)[k] = (entry_ptr); \ + } \ + (cache_ptr)->index_len++; \ + (cache_ptr)->index_size += (entry_ptr)->size; \ + H5C__UPDATE_STATS_FOR_HT_INSERTION(cache_ptr) \ +} + +#define H5C__DELETE_FROM_INDEX(cache_ptr, entry_ptr) \ +{ \ + int k; \ + H5C__PRE_HT_REMOVE_SC(cache_ptr, entry_ptr) \ + k = H5C__HASH_FCN((entry_ptr)->addr); \ + if ( (entry_ptr)->ht_next ) \ + { \ + (entry_ptr)->ht_next->ht_prev = (entry_ptr)->ht_prev; \ + } \ + if ( (entry_ptr)->ht_prev ) \ + { \ + (entry_ptr)->ht_prev->ht_next = (entry_ptr)->ht_next; \ + } \ + if ( ((cache_ptr)->index)[k] == (entry_ptr) ) \ + { \ + ((cache_ptr)->index)[k] = (entry_ptr)->ht_next; \ + } \ + (entry_ptr)->ht_next = NULL; \ + (entry_ptr)->ht_prev = NULL; \ + (cache_ptr)->index_len--; \ + (cache_ptr)->index_size -= (entry_ptr)->size; \ + H5C__UPDATE_STATS_FOR_HT_DELETION(cache_ptr) \ +} + +#define H5C__SEARCH_INDEX(cache_ptr, Addr, entry_ptr, fail_val) \ +{ \ + int k; \ + int depth = 0; \ + H5C__PRE_HT_SEARCH_SC(cache_ptr, Addr, fail_val) \ + k = H5C__HASH_FCN(Addr); \ + entry_ptr = ((cache_ptr)->index)[k]; \ + while ( ( entry_ptr ) && ( H5F_addr_ne(Addr, (entry_ptr)->addr) ) ) \ + { \ + (entry_ptr) = (entry_ptr)->ht_next; \ + (depth)++; \ + } \ + if ( entry_ptr ) \ + { \ + H5C__POST_SUC_HT_SEARCH_SC(cache_ptr, entry_ptr, Addr, k, fail_val) \ + if ( entry_ptr != ((cache_ptr)->index)[k] ) \ + { \ + if ( (entry_ptr)->ht_next ) \ + { \ + (entry_ptr)->ht_next->ht_prev = (entry_ptr)->ht_prev; \ + } \ + HDassert( (entry_ptr)->ht_prev != NULL ); \ + (entry_ptr)->ht_prev->ht_next = (entry_ptr)->ht_next; \ + ((cache_ptr)->index)[k]->ht_prev = (entry_ptr); \ + (entry_ptr)->ht_next = ((cache_ptr)->index)[k]; \ + (entry_ptr)->ht_prev = NULL; \ + ((cache_ptr)->index)[k] = (entry_ptr); \ + H5C__POST_HT_SHIFT_TO_FRONT(cache_ptr, entry_ptr, k, fail_val) \ + } \ + } \ + H5C__UPDATE_STATS_FOR_HT_SEARCH(cache_ptr, (entry_ptr != NULL), depth) \ +} + + +/************************************************************************** * - * While the cache was designed with multiple replacement policies in mind, - * at present only a modified form of LRU is supported. + * Tree insertion and deletion macros: * - * JRM - 4/26/04 + * These used to be functions, but I converted them to macros to avoid some + * function call overhead. * - * magic: Unsigned 32 bit integer always set to H5C__H5C_T_MAGIC. This - * field is used to validate pointers to instances of H5C_t. + **************************************************************************/ + +/*------------------------------------------------------------------------- * - * max_type_id: Integer field containing the maximum type id number assigned - * to a type of entry in the cache. All type ids from 0 to - * max_type_id inclusive must be defined. The names of the - * types are stored in the type_name_table discussed below, and - * indexed by the ids. + * Macro: H5C__INSERT_ENTRY_IN_TREE * - * type_name_table_ptr: Pointer to an array of pointer to char of length - * max_type_id + 1. The strings pointed to by the entries - * in the array are the names of the entry types associated - * with the indexing type IDs. + * Purpose: Insert the specified instance of H5C_cache_entry_t into + * the tree in the specified instance of H5C_t. Update + * the associated length and size fields. * - * max_cache_size: Nominal maximum number of bytes that may be stored in the - * cache. This value should be viewed as a soft limit, as the - * cache can exceed this value under the following circumstances: + * Return: N/A * - * a) All entries in the cache are protected, and the cache is - * asked to insert a new entry. In this case the new entry - * will be created. If this causes the cache to exceed - * max_cache_size, it will do so. The cache will attempt - * to reduce its size as entries are unprotected. + * Programmer: John Mainzer, 5/10/04 * - * b) When running in parallel mode, the cache may not be - * permitted to flush a dirty entry in response to a read. - * If there are no clean entries available to evict, the - * cache will exceed its maximum size. Again the cache - * will attempt to reduce its size to the max_cache_size - * limit on the next cache write. + * Modifications: * - * min_clean_size: Nominal minimum number of clean bytes in the cache. - * The cache attempts to maintain this number of bytes of - * clean data so as to avoid case b) above. Again, this is - * a soft limit. + * JRM -- 7/21/04 + * Updated function to set the in_tree flag when inserting + * an entry into the tree. Also modified the function to + * update the tree size and len fields instead of the similar + * index fields. * + * All of this is part of the modifications to support the + * hash table. * - * In addition to the call back functions required for each entry, the - * cache requires the following call back functions for this instance of - * the cache as a whole: - * - * check_write_permitted: In certain applications, the cache may not - * be allowed to write to disk at certain time. If specified, - * the check_write_permitted function is used to determine if - * a write is permissible at any given point in time. + * JRM -- 7/27/04 + * Converted the function H5C_insert_entry_in_tree() into + * the macro H5C__INSERT_ENTRY_IN_TREE in the hopes of + * wringing a little more speed out of the cache. * - * If no such function is specified (i.e. this field is NULL), - * the cache will presume that writes are always permissable. + * Note that we don't bother to check if the entry is already + * in the tree -- if it is, H5TB_dins() will fail. * + *------------------------------------------------------------------------- + */ + +#define H5C__INSERT_ENTRY_IN_TREE(cache_ptr, entry_ptr) \ +{ \ + H5TB_NODE * loc_node_ptr = NULL; \ + \ + HDassert( (cache_ptr) ); \ + HDassert( (cache_ptr)->magic == H5C__H5C_T_MAGIC ); \ + HDassert( (entry_ptr) ); \ + HDassert( (entry_ptr)->size > 0 ); \ + HDassert( H5F_addr_defined((entry_ptr)->addr) ); \ + HDassert( !((entry_ptr)->in_tree) ); \ + \ + loc_node_ptr = H5TB_dins((cache_ptr)->tree_ptr, (void *)(entry_ptr), NULL);\ + \ + if ( loc_node_ptr == NULL ) { \ + \ + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Can't insert entry in tree") \ + } \ + \ + (entry_ptr)->in_tree = TRUE; \ + (cache_ptr)->tree_len++; \ + (cache_ptr)->tree_size += (entry_ptr)->size; \ + \ + HDassert( (cache_ptr)->tree_len > 0 ); \ + HDassert( (cache_ptr)->tree_size > 0 ); \ + \ +} /* H5C__INSERT_ENTRY_IN_TREE */ + + +/*------------------------------------------------------------------------- * - * The cache requires an index to facilitate searching for entries. The - * following fields support that index. + * Function: H5C__REMOVE_ENTRY_FROM_TREE * - * index_len: Number of entries currently in the threaded binary B-tree - * used to index the cache. + * Purpose: Remove the specified instance of H5C_cache_entry_t from the + * index tree in the specified instance of H5C_t. Update + * the associated length and size fields. * - * index_size: Number of bytes of cache entries currently stored in the - * threaded binary B-tree used to index the cache. + * Return: N/A * - * This value should not be mistaken for footprint of the - * cache in memory. The average cache entry is small, and - * the cache has a considerable overhead. Multiplying the - * index_size by two should yield a conservative estimate - * of the cache's memory footprint. + * Programmer: John Mainzer, 5/10/04 * - * index_tree_ptr: pointer to the instance of H5TB_TREE used to index - * the cache. I use an instance of H5TB_TREE instead of - * a more conventional hash table based design for two - * reasons: + * Modifications: * - * a) the code is already present and tested. + * JRM -- 7/21/04 + * Updated function for the addition of the hash table. * - * b) the H5TB_TREE makes it easy to check for adjacent - * cache entries so that writes can be combined and - * thus optimized. + * JRM - 7/27/04 + * Converted from the function H5C_remove_entry_from_tree() + * to the macro H5C__REMOVE_ENTRY_FROM_TREE in the hopes of + * wringing a little more performance out of the cache. * - * If time permitted, a more efficient index could be - * constructed. However, this should do for now. If the - * additional lookup overhead proves excessive, I will - * write specialized code. + *------------------------------------------------------------------------- + */ + +#define H5C__REMOVE_ENTRY_FROM_TREE(cache_ptr, entry_ptr, node_ptr) \ +{ \ + HDassert( (cache_ptr) ); \ + HDassert( (cache_ptr)->magic == H5C__H5C_T_MAGIC ); \ + HDassert( (entry_ptr) ); \ + HDassert( !((entry_ptr)->is_protected) ); \ + HDassert( (node_ptr) ); \ + HDassert( (node_ptr)->data == (entry_ptr) ); \ + HDassert( (entry_ptr)->size > 0 ); \ + HDassert( (entry_ptr)->in_tree ); \ + HDassert( (cache_ptr)->tree_ptr ); \ + HDassert( (cache_ptr)->tree_ptr->root ); \ + \ + if ( H5TB_rem(&((cache_ptr)->tree_ptr->root), (node_ptr), NULL) \ + != (entry_ptr) ) { \ + \ + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \ + "Can't delete entry from tree.") \ + \ + } else { \ + \ + HDassert( (cache_ptr)->tree_len > 0 ); \ + (cache_ptr)->tree_len--; \ + HDassert( (cache_ptr)->tree_size >= (entry_ptr)->size ); \ + (cache_ptr)->tree_size -= (entry_ptr)->size; \ + (entry_ptr)->in_tree = FALSE; \ + } \ +} /* H5C__REMOVE_ENTRY_FROM_TREE */ + + +/************************************************************************** * + * Replacement policy update macros: * - * When a cache entry is protected, it must be removed from the LRU - * list(s) as it cannot be either flushed or evicted until it is unprotected. - * The following fields are used to implement the protected list (pl). + * These used to be functions, but I converted them to macros to avoid some + * function call overhead. * - * pl_len: Number of entries currently residing on the protected list. + **************************************************************************/ + +/*------------------------------------------------------------------------- * - * pl_size: Number of bytes of cache entries currently residing on the - * protected list. + * Macro: H5C__UPDATE_RP_FOR_EVICTION * - * pl_head_ptr: Pointer to the head of the doubly linked list of protected - * entries. Note that cache entries on this list are linked - * by their next and prev fields. + * Purpose: Update the replacement policy data structures for an + * eviction of the specified cache entry. * - * This field is NULL if the list is empty. + * At present, we only support the modified LRU policy, so + * this function deals with that case unconditionally. If + * we ever support other replacement policies, the function + * should switch on the current policy and act accordingly. * - * pl_tail_ptr: Pointer to the tail of the doubly linked list of protected - * entries. Note that cache entries on this list are linked - * by their next and prev fields. + * Return: Non-negative on success/Negative on failure. * - * This field is NULL if the list is empty. + * Programmer: John Mainzer, 5/10/04 * + * Modifications: * - * The cache must have a replacement policy, and the fields supporting this - * policy must be accessible from this structure. + * JRM - 7/27/04 + * Converted the function H5C_update_rp_for_eviction() to the + * macro H5C__UPDATE_RP_FOR_EVICTION in an effort to squeeze + * a bit more performance out of the cache. * - * While there has been interest in several replacement policies for - * this cache, the initial development schedule is tight. Thus I have - * elected to support only a modified LRU policy for the first cut. + * At least for the first cut, I am leaving the comments and + * white space in the macro. If they cause dificulties with + * the pre-processor, I'll have to remove them. * - * To further simplify matters, I have simply included the fields needed - * by the modified LRU in this structure. When and if we add support for - * other policies, it will probably be easiest to just add the necessary - * fields to this structure as well -- we only create one instance of this - * structure per file, so the overhead is not excessive. + * JRM - 7/28/04 + * Split macro into two version, one supporting the clean and + * dirty LRU lists, and the other not. Yet another attempt + * at optimization. * + *------------------------------------------------------------------------- + */ + +#if H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS + +#define H5C__UPDATE_RP_FOR_EVICTION(cache_ptr, entry_ptr, fail_val) \ +{ \ + HDassert( (cache_ptr) ); \ + HDassert( (cache_ptr)->magic == H5C__H5C_T_MAGIC ); \ + HDassert( (entry_ptr) ); \ + HDassert( !((entry_ptr)->is_protected) ); \ + HDassert( (entry_ptr)->size > 0 ); \ + \ + /* modified LRU specific code */ \ + \ + /* remove the entry from the LRU list. */ \ + \ + H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ + \ + /* If the entry is clean when it is evicted, it should be on the \ + * clean LRU list, if it was dirty, it should be on the dirty LRU list. \ + * Remove it from the appropriate list according to the value of the \ + * dirty flag. \ + */ \ + \ + if ( (entry_ptr)->is_dirty ) { \ + \ + H5C__AUX_DLL_REMOVE((entry_ptr), (cache_ptr)->dLRU_head_ptr, \ + (cache_ptr)->dLRU_tail_ptr, \ + (cache_ptr)->dLRU_list_len, \ + (cache_ptr)->dLRU_list_size, (fail_val)) \ + } else { \ + H5C__AUX_DLL_REMOVE((entry_ptr), (cache_ptr)->cLRU_head_ptr, \ + (cache_ptr)->cLRU_tail_ptr, \ + (cache_ptr)->cLRU_list_len, \ + (cache_ptr)->cLRU_list_size, (fail_val)) \ + } \ + \ +} /* H5C__UPDATE_RP_FOR_EVICTION */ + +#else /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */ + +#define H5C__UPDATE_RP_FOR_EVICTION(cache_ptr, entry_ptr, fail_val) \ +{ \ + HDassert( (cache_ptr) ); \ + HDassert( (cache_ptr)->magic == H5C__H5C_T_MAGIC ); \ + HDassert( (entry_ptr) ); \ + HDassert( !((entry_ptr)->is_protected) ); \ + HDassert( (entry_ptr)->size > 0 ); \ + \ + /* modified LRU specific code */ \ + \ + /* remove the entry from the LRU list. */ \ + \ + H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ + \ +} /* H5C__UPDATE_RP_FOR_EVICTION */ + +#endif /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */ + + +/*------------------------------------------------------------------------- * - * Fields supporting the modified LRU policy: + * Macro: H5C__UPDATE_RP_FOR_FLUSH * - * See most any OS text for a discussion of the LRU replacement policy. + * Purpose: Update the replacement policy data structures for a flush + * of the specified cache entry. * - * When operating in parallel mode, we must ensure that a read does not - * cause a write. If it does, the process will hang, as the write will - * be collective and the other processes will not know to participate. + * At present, we only support the modified LRU policy, so + * this function deals with that case unconditionally. If + * we ever support other replacement policies, the function + * should switch on the current policy and act accordingly. * - * To deal with this issue, I have modified the usual LRU policy by adding - * clean and dirty LRU lists to the usual LRU list. + * Return: N/A * - * The clean LRU list is simply the regular LRU list with all dirty cache - * entries removed. + * Programmer: John Mainzer, 5/6/04 * - * Similarly, the dirty LRU list is the regular LRU list with all the clean - * cache entries removed. + * Modifications: * - * When reading in parallel mode, we evict from the clean LRU list only. - * This implies that we must try to ensure that the clean LRU list is - * reasonably well stocked at all times. + * JRM - 7/27/04 + * Converted the function H5C_update_rp_for_flush() to the + * macro H5C__UPDATE_RP_FOR_FLUSH in an effort to squeeze + * a bit more performance out of the cache. * - * We attempt to do this by trying to flush enough entries on each write - * to keep the cLRU_list_size >= min_clean_size. + * At least for the first cut, I am leaving the comments and + * white space in the macro. If they cause dificulties with + * pre-processor, I'll have to remove them. * - * Even if we start with a completely clean cache, a sequence of protects - * without unprotects can empty the clean LRU list. In this case, the - * cache must grow temporarily. At the next write, we will attempt to - * evict enough entries to reduce index_size to less than max_cache_size. - * While this will usually be possible, all bets are off if enough entries - * are protected. + * JRM - 7/28/04 + * Split macro into two version, one supporting the clean and + * dirty LRU lists, and the other not. Yet another attempt + * at optimization. * - * Discussions of the individual fields used by the modified LRU replacement - * policy follow: + *------------------------------------------------------------------------- + */ + +#if H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS + +#define H5C__UPDATE_RP_FOR_FLUSH(cache_ptr, entry_ptr, fail_val) \ +{ \ + HDassert( (cache_ptr) ); \ + HDassert( (cache_ptr)->magic == H5C__H5C_T_MAGIC ); \ + HDassert( (entry_ptr) ); \ + HDassert( !((entry_ptr)->is_protected) ); \ + HDassert( (entry_ptr)->size > 0 ); \ + \ + /* modified LRU specific code */ \ + \ + /* remove the entry from the LRU list, and re-insert it at the head. */ \ + \ + H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ + \ + H5C__DLL_PREPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ + \ + /* since the entry is being flushed or cleared, one would think that it \ + * must be dirty -- but that need not be the case. Use the dirty flag \ + * to infer whether the entry is on the clean or dirty LRU list, and \ + * remove it. Then insert it at the head of the clean LRU list. \ + * \ + * The function presumes that a dirty entry will be either cleared or \ + * flushed shortly, so it is OK if we put a dirty entry on the clean \ + * LRU list. \ + */ \ + \ + if ( (entry_ptr)->is_dirty ) { \ + H5C__AUX_DLL_REMOVE((entry_ptr), (cache_ptr)->dLRU_head_ptr, \ + (cache_ptr)->dLRU_tail_ptr, \ + (cache_ptr)->dLRU_list_len, \ + (cache_ptr)->dLRU_list_size, (fail_val)) \ + } else { \ + H5C__AUX_DLL_REMOVE((entry_ptr), (cache_ptr)->cLRU_head_ptr, \ + (cache_ptr)->cLRU_tail_ptr, \ + (cache_ptr)->cLRU_list_len, \ + (cache_ptr)->cLRU_list_size, (fail_val)) \ + } \ + \ + H5C__AUX_DLL_PREPEND((entry_ptr), (cache_ptr)->cLRU_head_ptr, \ + (cache_ptr)->cLRU_tail_ptr, \ + (cache_ptr)->cLRU_list_len, \ + (cache_ptr)->cLRU_list_size, (fail_val)) \ + \ + /* End modified LRU specific code. */ \ + \ +} /* H5C__UPDATE_RP_FOR_FLUSH */ + +#else /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */ + +#define H5C__UPDATE_RP_FOR_FLUSH(cache_ptr, entry_ptr, fail_val) \ +{ \ + HDassert( (cache_ptr) ); \ + HDassert( (cache_ptr)->magic == H5C__H5C_T_MAGIC ); \ + HDassert( (entry_ptr) ); \ + HDassert( !((entry_ptr)->is_protected) ); \ + HDassert( (entry_ptr)->size > 0 ); \ + \ + /* modified LRU specific code */ \ + \ + /* remove the entry from the LRU list, and re-insert it at the head. */ \ + \ + H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ + \ + H5C__DLL_PREPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ + \ + /* End modified LRU specific code. */ \ + \ +} /* H5C__UPDATE_RP_FOR_FLUSH */ + +#endif /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */ + + +/*------------------------------------------------------------------------- * - * LRU_list_len: Number of cache entries currently on the LRU list. + * Macro: H5C__UPDATE_RP_FOR_INSERTION * - * Observe that LRU_list_len + pl_len must always equal - * index_len. + * Purpose: Update the replacement policy data structures for an + * insertion of the specified cache entry. * - * LRU_list_size: Number of bytes of cache entries currently residing on the - * LRU list. + * At present, we only support the modified LRU policy, so + * this function deals with that case unconditionally. If + * we ever support other replacement policies, the function + * should switch on the current policy and act accordingly. * - * Observe that LRU_list_size + pl_size must always equal - * index_size. + * Return: N/A * - * LRU_head_ptr: Pointer to the head of the doubly linked LRU list. Cache - * entries on this list are linked by their next and prev fields. + * Programmer: John Mainzer, 5/17/04 * - * This field is NULL if the list is empty. + * Modifications: * - * LRU_tail_ptr: Pointer to the tail of the doubly linked LRU list. Cache - * entries on this list are linked by their next and prev fields. + * JRM - 7/27/04 + * Converted the function H5C_update_rp_for_insertion() to the + * macro H5C__UPDATE_RP_FOR_INSERTION in an effort to squeeze + * a bit more performance out of the cache. * - * This field is NULL if the list is empty. + * At least for the first cut, I am leaving the comments and + * white space in the macro. If they cause dificulties with + * pre-processor, I'll have to remove them. * - * cLRU_list_len: Number of cache entries currently on the clean LRU list. + * JRM - 7/28/04 + * Split macro into two version, one supporting the clean and + * dirty LRU lists, and the other not. Yet another attempt + * at optimization. * - * Observe that cLRU_list_len + dLRU_list_len must always - * equal LRU_list_len. + *------------------------------------------------------------------------- + */ + +#if H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS + +#define H5C__UPDATE_RP_FOR_INSERTION(cache_ptr, entry_ptr, fail_val) \ +{ \ + HDassert( (cache_ptr) ); \ + HDassert( (cache_ptr)->magic == H5C__H5C_T_MAGIC ); \ + HDassert( (entry_ptr) ); \ + HDassert( !((entry_ptr)->is_protected) ); \ + HDassert( (entry_ptr)->size > 0 ); \ + \ + /* modified LRU specific code */ \ + \ + /* insert the entry at the head of the LRU list. */ \ + \ + H5C__DLL_PREPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ + \ + /* insert the entry at the head of the clean or dirty LRU list as \ + * appropriate. \ + */ \ + \ + if ( entry_ptr->is_dirty ) { \ + H5C__AUX_DLL_PREPEND((entry_ptr), (cache_ptr)->dLRU_head_ptr, \ + (cache_ptr)->dLRU_tail_ptr, \ + (cache_ptr)->dLRU_list_len, \ + (cache_ptr)->dLRU_list_size, (fail_val)) \ + } else { \ + H5C__AUX_DLL_PREPEND((entry_ptr), (cache_ptr)->cLRU_head_ptr, \ + (cache_ptr)->cLRU_tail_ptr, \ + (cache_ptr)->cLRU_list_len, \ + (cache_ptr)->cLRU_list_size, (fail_val)) \ + } \ + \ + /* End modified LRU specific code. */ \ + \ +} + +#else /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */ + +#define H5C__UPDATE_RP_FOR_INSERTION(cache_ptr, entry_ptr, fail_val) \ +{ \ + HDassert( (cache_ptr) ); \ + HDassert( (cache_ptr)->magic == H5C__H5C_T_MAGIC ); \ + HDassert( (entry_ptr) ); \ + HDassert( !((entry_ptr)->is_protected) ); \ + HDassert( (entry_ptr)->size > 0 ); \ + \ + /* modified LRU specific code */ \ + \ + /* insert the entry at the head of the LRU list. */ \ + \ + H5C__DLL_PREPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ + \ + /* End modified LRU specific code. */ \ + \ +} + +#endif /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */ + + +/*------------------------------------------------------------------------- * - * cLRU_list_size: Number of bytes of cache entries currently residing on - * the clean LRU list. + * Macro: H5C__UPDATE_RP_FOR_PROTECT * - * Observe that cLRU_list_size + dLRU_list_size must always - * equal LRU_list_size. + * Purpose: Update the replacement policy data structures for a + * protect of the specified cache entry. * - * cLRU_head_ptr: Pointer to the head of the doubly linked clean LRU list. - * Cache entries on this list are linked by their aux_next and - * aux_prev fields. + * To do this, unlink the specified entry from any data + * structures used by the replacement policy, and add the + * entry to the protected list. * - * This field is NULL if the list is empty. + * At present, we only support the modified LRU policy, so + * this function deals with that case unconditionally. If + * we ever support other replacement policies, the function + * should switch on the current policy and act accordingly. * - * cLRU_tail_ptr: Pointer to the tail of the doubly linked clean LRU list. - * Cache entries on this list are linked by their aux_next and - * aux_prev fields. + * Return: N/A * - * This field is NULL if the list is empty. + * Programmer: John Mainzer, 5/17/04 * - * dLRU_list_len: Number of cache entries currently on the dirty LRU list. + * Modifications: * - * Observe that cLRU_list_len + dLRU_list_len must always - * equal LRU_list_len. + * JRM - 7/27/04 + * Converted the function H5C_update_rp_for_protect() to the + * macro H5C__UPDATE_RP_FOR_PROTECT in an effort to squeeze + * a bit more performance out of the cache. * - * dLRU_list_size: Number of cache entries currently on the dirty LRU list. + * At least for the first cut, I am leaving the comments and + * white space in the macro. If they cause dificulties with + * pre-processor, I'll have to remove them. * - * Observe that cLRU_list_len + dLRU_list_len must always - * equal LRU_list_len. + * JRM - 7/28/04 + * Split macro into two version, one supporting the clean and + * dirty LRU lists, and the other not. Yet another attempt + * at optimization. * - * dLRU_head_ptr: Pointer to the head of the doubly linked dirty LRU list. - * Cache entries on this list are linked by their aux_next and - * aux_prev fields. + *------------------------------------------------------------------------- + */ + +#if H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS + +#define H5C__UPDATE_RP_FOR_PROTECT(cache_ptr, entry_ptr, fail_val) \ +{ \ + HDassert( (cache_ptr) ); \ + HDassert( (cache_ptr)->magic == H5C__H5C_T_MAGIC ); \ + HDassert( (entry_ptr) ); \ + HDassert( !((entry_ptr)->is_protected) ); \ + HDassert( (entry_ptr)->size > 0 ); \ + \ + /* modified LRU specific code */ \ + \ + /* remove the entry from the LRU list. */ \ + \ + H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ + \ + /* Similarly, remove the entry from the clean or dirty LRU list \ + * as appropriate. \ + */ \ + \ + if ( (entry_ptr)->is_dirty ) { \ + \ + H5C__AUX_DLL_REMOVE((entry_ptr), (cache_ptr)->dLRU_head_ptr, \ + (cache_ptr)->dLRU_tail_ptr, \ + (cache_ptr)->dLRU_list_len, \ + (cache_ptr)->dLRU_list_size, (fail_val)) \ + \ + } else { \ + \ + H5C__AUX_DLL_REMOVE((entry_ptr), (cache_ptr)->cLRU_head_ptr, \ + (cache_ptr)->cLRU_tail_ptr, \ + (cache_ptr)->cLRU_list_len, \ + (cache_ptr)->cLRU_list_size, (fail_val)) \ + } \ + \ + /* End modified LRU specific code. */ \ + \ + /* Regardless of the replacement policy, now add the entry to the \ + * protected list. \ + */ \ + \ + H5C__DLL_APPEND((entry_ptr), (cache_ptr)->pl_head_ptr, \ + (cache_ptr)->pl_tail_ptr, \ + (cache_ptr)->pl_len, \ + (cache_ptr)->pl_size, (fail_val)) \ +} /* H5C__UPDATE_RP_FOR_PROTECT */ + +#else /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */ + +#define H5C__UPDATE_RP_FOR_PROTECT(cache_ptr, entry_ptr, fail_val) \ +{ \ + HDassert( (cache_ptr) ); \ + HDassert( (cache_ptr)->magic == H5C__H5C_T_MAGIC ); \ + HDassert( (entry_ptr) ); \ + HDassert( !((entry_ptr)->is_protected) ); \ + HDassert( (entry_ptr)->size > 0 ); \ + \ + /* modified LRU specific code */ \ + \ + /* remove the entry from the LRU list. */ \ + \ + H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ + \ + /* End modified LRU specific code. */ \ + \ + /* Regardless of the replacement policy, now add the entry to the \ + * protected list. \ + */ \ + \ + H5C__DLL_APPEND((entry_ptr), (cache_ptr)->pl_head_ptr, \ + (cache_ptr)->pl_tail_ptr, \ + (cache_ptr)->pl_len, \ + (cache_ptr)->pl_size, (fail_val)) \ +} /* H5C__UPDATE_RP_FOR_PROTECT */ + +#endif /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */ + + +/*------------------------------------------------------------------------- * - * This field is NULL if the list is empty. + * Macro: H5C__UPDATE_RP_FOR_RENAME * - * dLRU_tail_ptr: Pointer to the tail of the doubly linked dirty LRU list. - * Cache entries on this list are linked by their aux_next and - * aux_prev fields. + * Purpose: Update the replacement policy data structures for a + * rename of the specified cache entry. * - * This field is NULL if the list is empty. + * At present, we only support the modified LRU policy, so + * this function deals with that case unconditionally. If + * we ever support other replacement policies, the function + * should switch on the current policy and act accordingly. * + * Return: N/A * - * Statistics collection fields: + * Programmer: John Mainzer, 5/17/04 * - * When enabled, these fields are used to collect statistics as described - * below. The first set are collected only when H5C_COLLECT_CACHE_STATS - * is true. + * Modifications: * - * hits: Array of int64 of length H5C__MAX_NUM_TYPE_IDS. The cells - * are used to record the number of times an entry with type id - * equal to the array index has been in cache when requested in - * the current epoch. + * JRM - 7/27/04 + * Converted the function H5C_update_rp_for_rename() to the + * macro H5C__UPDATE_RP_FOR_RENAME in an effort to squeeze + * a bit more performance out of the cache. * - * misses: Array of int64 of length H5C__MAX_NUM_TYPE_IDS. The cells - * are used to record the number of times an entry with type id - * equal to the array index has not been in cache when - * requested in the current epoch. + * At least for the first cut, I am leaving the comments and + * white space in the macro. If they cause dificulties with + * pre-processor, I'll have to remove them. * - * insertions: Array of int64 of length H5C__MAX_NUM_TYPE_IDS. The cells - * are used to record the number of times an entry with type - * id equal to the array index has been inserted into the - * cache in the current epoch. + * JRM - 7/28/04 + * Split macro into two version, one supporting the clean and + * dirty LRU lists, and the other not. Yet another attempt + * at optimization. * - * clears: Array of int64 of length H5C__MAX_NUM_TYPE_IDS. The cells - * are used to record the number of times an entry with type - * id equal to the array index has been cleared in the current - * epoch. + *------------------------------------------------------------------------- + */ + +#if H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS + +#define H5C__UPDATE_RP_FOR_RENAME(cache_ptr, entry_ptr, fail_val) \ +{ \ + HDassert( (cache_ptr) ); \ + HDassert( (cache_ptr)->magic == H5C__H5C_T_MAGIC ); \ + HDassert( (entry_ptr) ); \ + HDassert( !((entry_ptr)->is_protected) ); \ + HDassert( (entry_ptr)->size > 0 ); \ + \ + /* modified LRU specific code */ \ + \ + /* remove the entry from the LRU list, and re-insert it at the head. */ \ + \ + H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ + \ + H5C__DLL_PREPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ + \ + /* move the entry to the head of either the clean or dirty LRU list \ + * as appropriate. \ + */ \ + \ + if ( (entry_ptr)->is_dirty ) { \ + \ + H5C__AUX_DLL_REMOVE((entry_ptr), (cache_ptr)->dLRU_head_ptr, \ + (cache_ptr)->dLRU_tail_ptr, \ + (cache_ptr)->dLRU_list_len, \ + (cache_ptr)->dLRU_list_size, (fail_val)) \ + \ + H5C__AUX_DLL_PREPEND((entry_ptr), (cache_ptr)->dLRU_head_ptr, \ + (cache_ptr)->dLRU_tail_ptr, \ + (cache_ptr)->dLRU_list_len, \ + (cache_ptr)->dLRU_list_size, (fail_val)) \ + \ + } else { \ + \ + H5C__AUX_DLL_REMOVE((entry_ptr), (cache_ptr)->cLRU_head_ptr, \ + (cache_ptr)->cLRU_tail_ptr, \ + (cache_ptr)->cLRU_list_len, \ + (cache_ptr)->cLRU_list_size, (fail_val)) \ + \ + H5C__AUX_DLL_PREPEND((entry_ptr), (cache_ptr)->cLRU_head_ptr, \ + (cache_ptr)->cLRU_tail_ptr, \ + (cache_ptr)->cLRU_list_len, \ + (cache_ptr)->cLRU_list_size, (fail_val)) \ + } \ + \ + /* End modified LRU specific code. */ \ + \ +} /* H5C__UPDATE_RP_FOR_RENAME */ + +#else /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */ + +#define H5C__UPDATE_RP_FOR_RENAME(cache_ptr, entry_ptr, fail_val) \ +{ \ + HDassert( (cache_ptr) ); \ + HDassert( (cache_ptr)->magic == H5C__H5C_T_MAGIC ); \ + HDassert( (entry_ptr) ); \ + HDassert( !((entry_ptr)->is_protected) ); \ + HDassert( (entry_ptr)->size > 0 ); \ + \ + /* modified LRU specific code */ \ + \ + /* remove the entry from the LRU list, and re-insert it at the head. */ \ + \ + H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ + \ + H5C__DLL_PREPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ + \ + /* End modified LRU specific code. */ \ + \ +} /* H5C__UPDATE_RP_FOR_RENAME */ + +#endif /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */ + + +/*------------------------------------------------------------------------- * - * flushes: Array of int64 of length H5C__MAX_NUM_TYPE_IDS. The cells - * are used to record the number of times an entry with type id - * equal to the array index has been written to disk in the - * current epoch. + * Macro: H5C__UPDATE_RP_FOR_UNPROTECT * - * evictions: Array of int64 of length H5C__MAX_NUM_TYPE_IDS. The cells - * are used to record the number of times an entry with type id - * equal to the array index has been evicted from the cache in - * the current epoch. + * Purpose: Update the replacement policy data structures for an + * unprotect of the specified cache entry. * - * renames: Array of int64 of length H5C__MAX_NUM_TYPE_IDS. The cells - * are used to record the number of times an entry with type - * id equal to the array index has been renamed in the current - * epoch. + * To do this, unlink the specified entry from the protected + * list, and re-insert it in the data structures used by the + * current replacement policy. * - * max_index_len: Largest value attained by the index_len field in the - * current epoch. + * At present, we only support the modified LRU policy, so + * this function deals with that case unconditionally. If + * we ever support other replacement policies, the function + * should switch on the current policy and act accordingly. * - * max_index_size: Largest value attained by the index_size field in the - * current epoch. + * Return: N/A * - * max_pl_len: Largest value attained by the pl_len field in the - * current epoch. + * Programmer: John Mainzer, 5/19/04 * - * max_pl_size: Largest value attained by the pl_size field in the - * current epoch. + * Modifications: * - * The remaining stats are collected only when both H5C_COLLECT_CACHE_STATS - * and H5C_COLLECT_CACHE_ENTRY_STATS are true. + * JRM - 7/27/04 + * Converted the function H5C_update_rp_for_unprotect() to + * the macro H5C__UPDATE_RP_FOR_UNPROTECT in an effort to + * squeeze a bit more performance out of the cache. * - * max_accesses: Array of int32 of length H5C__MAX_NUM_TYPE_IDS. The cells - * are used to record the maximum number of times any single - * entry with type id equal to the array index has been - * accessed in the current epoch. + * At least for the first cut, I am leaving the comments and + * white space in the macro. If they cause dificulties with + * pre-processor, I'll have to remove them. * - * min_accesses: Array of int32 of length H5C__MAX_NUM_TYPE_IDS. The cells - * are used to record the minimum number of times any single - * entry with type id equal to the array index has been - * accessed in the current epoch. + * JRM - 7/28/04 + * Split macro into two version, one supporting the clean and + * dirty LRU lists, and the other not. Yet another attempt + * at optimization. * - * max_clears: Array of int32 of length H5C__MAX_NUM_TYPE_IDS. The cells - * are used to record the maximum number of times any single - * entry with type id equal to the array index has been cleared - * in the current epoch. + *------------------------------------------------------------------------- + */ + +#if H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS + +#define H5C__UPDATE_RP_FOR_UNPROTECT(cache_ptr, entry_ptr, fail_val) \ +{ \ + HDassert( (cache_ptr) ); \ + HDassert( (cache_ptr)->magic == H5C__H5C_T_MAGIC ); \ + HDassert( (entry_ptr) ); \ + HDassert( (entry_ptr)->is_protected); \ + HDassert( (entry_ptr)->size > 0 ); \ + \ + /* Regardless of the replacement policy, remove the entry from the \ + * protected list. \ + */ \ + H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->pl_head_ptr, \ + (cache_ptr)->pl_tail_ptr, (cache_ptr)->pl_len, \ + (cache_ptr)->pl_size, (fail_val)) \ + \ + /* modified LRU specific code */ \ + \ + /* insert the entry at the head of the LRU list. */ \ + \ + H5C__DLL_PREPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, \ + (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ + \ + /* Similarly, insert the entry at the head of either the clean or \ + * dirty LRU list as appropriate. \ + */ \ + \ + if ( (entry_ptr)->is_dirty ) { \ + \ + H5C__AUX_DLL_PREPEND((entry_ptr), (cache_ptr)->dLRU_head_ptr, \ + (cache_ptr)->dLRU_tail_ptr, \ + (cache_ptr)->dLRU_list_len, \ + (cache_ptr)->dLRU_list_size, (fail_val)) \ + \ + } else { \ + \ + H5C__AUX_DLL_PREPEND((entry_ptr), (cache_ptr)->cLRU_head_ptr, \ + (cache_ptr)->cLRU_tail_ptr, \ + (cache_ptr)->cLRU_list_len, \ + (cache_ptr)->cLRU_list_size, (fail_val)) \ + } \ + \ + /* End modified LRU specific code. */ \ + \ +} /* H5C__UPDATE_RP_FOR_UNPROTECT */ + +#else /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */ + +#define H5C__UPDATE_RP_FOR_UNPROTECT(cache_ptr, entry_ptr, fail_val) \ +{ \ + HDassert( (cache_ptr) ); \ + HDassert( (cache_ptr)->magic == H5C__H5C_T_MAGIC ); \ + HDassert( (entry_ptr) ); \ + HDassert( (entry_ptr)->is_protected); \ + HDassert( (entry_ptr)->size > 0 ); \ + \ + /* Regardless of the replacement policy, remove the entry from the \ + * protected list. \ + */ \ + H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->pl_head_ptr, \ + (cache_ptr)->pl_tail_ptr, (cache_ptr)->pl_len, \ + (cache_ptr)->pl_size, (fail_val)) \ + \ + /* modified LRU specific code */ \ + \ + /* insert the entry at the head of the LRU list. */ \ + \ + H5C__DLL_PREPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, \ + (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ + \ + /* End modified LRU specific code. */ \ + \ +} /* H5C__UPDATE_RP_FOR_UNPROTECT */ + +#endif /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */ + + +/**************************************************************************** * - * max_flushes: Array of int32 of length H5C__MAX_NUM_TYPE_IDS. The cells - * are used to record the maximum number of times any single - * entry with type id equal to the array index has been - * flushed in the current epoch. + * structure H5C_t * + * Catchall structure for all variables specific to an instance of the cache. * - * Fields supporting testing: + * While the individual fields of the structure are discussed below, the + * following overview may be helpful. * - * For test purposes, it is useful to turn off some asserts and sanity - * checks. The following flags support this. + * Entries in the cache are stored in an instance of H5TB_TREE, indexed on + * the entry's disk address. While the H5TB_TREE is less efficient than + * hash table, it keeps the entries in address sorted order. As flushes + * in parallel mode are more efficient if they are issued in increasing + * address order, this is a significant benefit. Also the H5TB_TREE code + * was readily available, which reduced development time. * - * skip_file_checks: Boolean flag used to skip sanity checks on file - * parameters passed to the cache. In the test bed, there - * is no reason to have a file open, as the cache proper - * just passes these parameters through without using them. + * While the cache was designed with multiple replacement policies in mind, + * at present only a modified form of LRU is supported. * - * When this flag is set, all sanity checks on the file - * parameters are skipped. The field defaults to FALSE. + * JRM - 4/26/04 * - * skip_dxpl_id_checks: Boolean flag used to skip sanity checks on the - * dxpl_id parameters passed to the cache. These are not - * used directly by the cache, so skipping the checks - * simplifies the test bed. + * Profiling has indicated that searches in the instance of H5TB_TREE are + * too expensive. To deal with this issue, I have augmented the cache + * with a hash table in which all entries will be stored. Given the + * advantages of flushing entries in increasing address order, the TBBT + * is retained, but only dirty entries are stored in it. At least for + * now, we will leave entries in the TBBT after they are flushed. * - * When this flag is set, all sanity checks on the dxpl_id - * parameters are skipped. The field defaults to FALSE. + * Note that index_size and index_len now refer to the total size of + * and number of entries in the hash table. * - ****************************************************************************/ - -#define H5C__H5C_T_MAGIC 0x005CAC0E -#define H5C__MAX_NUM_TYPE_IDS 9 - -struct H5C_t -{ - uint32_t magic; - - int32_t max_type_id; - const char * (* type_name_table_ptr); - - size_t max_cache_size; - size_t min_clean_size; - - H5C_write_permitted_func_t check_write_permitted; - - int32_t index_len; - size_t index_size; - H5TB_TREE * index_tree_ptr; - - int32_t pl_len; - size_t pl_size; - H5C_cache_entry_t * pl_head_ptr; - H5C_cache_entry_t * pl_tail_ptr; - - int32_t LRU_list_len; - size_t LRU_list_size; - H5C_cache_entry_t * LRU_head_ptr; - H5C_cache_entry_t * LRU_tail_ptr; - - int32_t cLRU_list_len; - size_t cLRU_list_size; - H5C_cache_entry_t * cLRU_head_ptr; - H5C_cache_entry_t * cLRU_tail_ptr; - - int32_t dLRU_list_len; - size_t dLRU_list_size; - H5C_cache_entry_t * dLRU_head_ptr; - H5C_cache_entry_t * dLRU_tail_ptr; - -#if H5C_COLLECT_CACHE_STATS - - /* stats fields */ - int64_t hits[H5C__MAX_NUM_TYPE_IDS]; - int64_t misses[H5C__MAX_NUM_TYPE_IDS]; - int64_t insertions[H5C__MAX_NUM_TYPE_IDS]; - int64_t clears[H5C__MAX_NUM_TYPE_IDS]; - int64_t flushes[H5C__MAX_NUM_TYPE_IDS]; - int64_t evictions[H5C__MAX_NUM_TYPE_IDS]; - int64_t renames[H5C__MAX_NUM_TYPE_IDS]; - - int32_t max_index_len; - size_t max_index_size; - - int32_t max_pl_len; - size_t max_pl_size; - -#if H5C_COLLECT_CACHE_ENTRY_STATS - - int32_t max_accesses[H5C__MAX_NUM_TYPE_IDS]; - int32_t min_accesses[H5C__MAX_NUM_TYPE_IDS]; - int32_t max_clears[H5C__MAX_NUM_TYPE_IDS]; - int32_t max_flushes[H5C__MAX_NUM_TYPE_IDS]; - -#endif /* H5C_COLLECT_CACHE_ENTRY_STATS */ - -#endif /* H5C_COLLECT_CACHE_STATS */ - - hbool_t skip_file_checks; - hbool_t skip_dxpl_id_checks; - -}; - - -/* - * Private file-scope variables. - */ - -/* Declare a free list to manage the H5C_t struct */ -H5FL_DEFINE_STATIC(H5C_t); - -/* - * Private file-scope function declarations: - */ - -static herr_t H5C_flush_single_entry(H5F_t * f, - hid_t primary_dxpl_id, - hid_t secondary_dxpl_id, - H5C_t * cache_ptr, - const H5C_class_t * type_ptr, - haddr_t addr, - unsigned flags, - H5TB_NODE * tgt_node_ptr, - hbool_t * first_flush_ptr, - hbool_t remove_entry_from_tree_on_destroy); - -static herr_t H5C_insert_entry_in_tree(H5C_t * cache_ptr, - H5C_cache_entry_t * entry_ptr); - -static void * H5C_load_entry(H5F_t * f, - hid_t dxpl_id, - const H5C_class_t * type, - haddr_t addr, - const void * udata1, - void * udata2, - hbool_t skip_file_checks); - -static herr_t H5C_make_space_in_cache(H5F_t * f, - hid_t primary_dxpl_id, - hid_t secondary_dxpl_id, - H5C_t * cache_ptr, - size_t space_needed, - hbool_t write_permitted); - -static herr_t H5C_remove_entry_from_tree(H5C_t * cache_ptr, - H5C_cache_entry_t * entry_ptr, - H5TB_NODE * node_ptr); - -static herr_t H5C_update_rp_for_eviction(H5C_t * cache_ptr, - H5C_cache_entry_t * entry_ptr); - -static herr_t H5C_update_rp_for_flush(H5C_t * cache_ptr, - H5C_cache_entry_t * entry_ptr); - -static herr_t H5C_update_rp_for_insertion(H5C_t * cache_ptr, - H5C_cache_entry_t * entry_ptr); - -static herr_t H5C_update_rp_for_protect(H5C_t * cache_ptr, - H5C_cache_entry_t * entry_ptr); - -static herr_t H5C_update_rp_for_rename(H5C_t * cache_ptr, - H5C_cache_entry_t * entry_ptr); - -static herr_t H5C_update_rp_for_unprotect(H5C_t * cache_ptr, - H5C_cache_entry_t * entry_ptr); - - -/*------------------------------------------------------------------------- - * Function: H5C_create + * JRM - 7/19/04 * - * Purpose: Allocate, initialize, and return the address of a new - * instance of H5C_t. + * ********************************************* * - * In general, the max_cache_size parameter must be positive, - * and the min_clean_size parameter must lie in the closed - * interval [0, max_cache_size]. + * WARNING: A copy of H5C_t is in tst/cache.c (under the name "local_H5C_t" + * to allow the test code to access the internal fields of the + * cache. If you modify H5C_t, be sure to update local_H5C_t + * in cache.c as well. * - * The check_write_permitted parameter must either be NULL, - * or point to a function of type H5C_write_permitted_func_t. - * If it is NULL, the cache will presume that writes are - * always permitted. + * ********************************************* * - * Return: Success: Pointer to the new instance. + * magic: Unsigned 32 bit integer always set to H5C__H5C_T_MAGIC. This + * field is used to validate pointers to instances of H5C_t. * - * Failure: NULL + * max_type_id: Integer field containing the maximum type id number assigned + * to a type of entry in the cache. All type ids from 0 to + * max_type_id inclusive must be defined. The names of the + * types are stored in the type_name_table discussed below, and + * indexed by the ids. * - * Programmer: John Mainzer - * 6/2/04 + * type_name_table_ptr: Pointer to an array of pointer to char of length + * max_type_id + 1. The strings pointed to by the entries + * in the array are the names of the entry types associated + * with the indexing type IDs. * - * Modifications: + * max_cache_size: Nominal maximum number of bytes that may be stored in the + * cache. This value should be viewed as a soft limit, as the + * cache can exceed this value under the following circumstances: * - *------------------------------------------------------------------------- - */ - -H5C_t * -H5C_create(size_t max_cache_size, - size_t min_clean_size, - int max_type_id, - const char * (* type_name_table_ptr), - H5C_write_permitted_func_t check_write_permitted) -{ - int i; - H5C_t * cache_ptr = NULL; - H5C_t * ret_value = NULL; /* Return value */ - - FUNC_ENTER_NOAPI(H5C_create, NULL) - - HDassert( max_cache_size > 0 ); - HDassert( min_clean_size <= max_cache_size ); - - HDassert( max_type_id >= 0 ); - HDassert( max_type_id < H5C__MAX_NUM_TYPE_IDS ); - HDassert( type_name_table_ptr ); - - for ( i = 0; i <= max_type_id; i++ ) { - - HDassert( (type_name_table_ptr)[i] ); - HDassert( HDstrlen(( type_name_table_ptr)[i]) > 0 ); - } - - - if ( NULL == (cache_ptr = H5FL_CALLOC(H5C_t)) ) { - - HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, \ - "memory allocation failed") - } - - if ( (cache_ptr->index_tree_ptr = H5TB_fast_dmake(H5TB_FAST_HADDR_COMPARE)) - == NULL ) { - - HGOTO_ERROR(H5E_CACHE, H5E_CANTCREATE, NULL, "can't create TBBT.") - } - - /* If we get this far, we should succeed. Go ahead and initialize all - * the fields. - */ - - cache_ptr->magic = H5C__H5C_T_MAGIC; - - cache_ptr->max_type_id = max_type_id; - cache_ptr->type_name_table_ptr = type_name_table_ptr; - - cache_ptr->max_cache_size = max_cache_size; - cache_ptr->min_clean_size = min_clean_size; - - cache_ptr->check_write_permitted = check_write_permitted; - - cache_ptr->index_len = 0; - cache_ptr->index_size = (size_t)0; - - cache_ptr->pl_len = 0; - cache_ptr->pl_size = (size_t)0; - cache_ptr->pl_head_ptr = NULL; - cache_ptr->pl_tail_ptr = NULL; - - cache_ptr->LRU_list_len = 0; - cache_ptr->LRU_list_size = (size_t)0; - cache_ptr->LRU_head_ptr = NULL; - cache_ptr->LRU_tail_ptr = NULL; - - cache_ptr->cLRU_list_len = 0; - cache_ptr->cLRU_list_size = (size_t)0; - cache_ptr->cLRU_head_ptr = NULL; - cache_ptr->cLRU_tail_ptr = NULL; - - cache_ptr->dLRU_list_len = 0; - cache_ptr->dLRU_list_size = (size_t)0; - cache_ptr->dLRU_head_ptr = NULL; - cache_ptr->dLRU_tail_ptr = NULL; - - H5C_stats__reset(cache_ptr); - - cache_ptr->skip_file_checks = FALSE; - cache_ptr->skip_dxpl_id_checks = FALSE; - - /* Set return value */ - ret_value = cache_ptr; - -done: - - if ( ret_value == 0 ) { - - if ( cache_ptr != NULL ) { - - if ( cache_ptr->index_tree_ptr != NULL ) { - - /* the index tree should be empty, so we can pass in - * NULL for the fd & fk parameters. - */ - H5TB_dfree(cache_ptr->index_tree_ptr, NULL, NULL); - } - - cache_ptr->magic = 0; - H5FL_FREE(H5C_t, cache_ptr); - cache_ptr = NULL; - - } /* end if */ - - } /* end if */ - - FUNC_LEAVE_NOAPI(ret_value) - -} /* H5C_create() */ - - -/*------------------------------------------------------------------------- - * Function: H5C_dest + * a) All entries in the cache are protected, and the cache is + * asked to insert a new entry. In this case the new entry + * will be created. If this causes the cache to exceed + * max_cache_size, it will do so. The cache will attempt + * to reduce its size as entries are unprotected. + * + * b) When running in parallel mode, the cache may not be + * permitted to flush a dirty entry in response to a read. + * If there are no clean entries available to evict, the + * cache will exceed its maximum size. Again the cache + * will attempt to reduce its size to the max_cache_size + * limit on the next cache write. + * + * min_clean_size: Nominal minimum number of clean bytes in the cache. + * The cache attempts to maintain this number of bytes of + * clean data so as to avoid case b) above. Again, this is + * a soft limit. + * + * + * In addition to the call back functions required for each entry, the + * cache requires the following call back functions for this instance of + * the cache as a whole: + * + * check_write_permitted: In certain applications, the cache may not + * be allowed to write to disk at certain time. If specified, + * the check_write_permitted function is used to determine if + * a write is permissible at any given point in time. + * + * If no such function is specified (i.e. this field is NULL), + * the cache will presume that writes are always permissable. + * + * + * The cache requires an index to facilitate searching for entries. The + * following fields support that index. + * + * index_len: Number of entries currently in the hash table used to index + * the cache. + * + * index_size: Number of bytes of cache entries currently stored in the + * hash table used to index the cache. + * + * This value should not be mistaken for footprint of the + * cache in memory. The average cache entry is small, and + * the cache has a considerable overhead. Multiplying the + * index_size by two should yield a conservative estimate + * of the cache's memory footprint. + * + * index: Array of pointer to H5C_cache_entry_t of size + * H5C__HASH_TABLE_LEN. At present, this value is a power + * of two, not the usual prime number. + * + * I hope that the variable size of cache elements, the large + * hash table size, and the way in which HDF5 allocates space + * will combine to avoid problems with periodicity. If so, we + * can use a trivial hash function (a bit-and and a 3 bit left + * shift) with some small savings. + * + * If not, it will become evident in the statistics. Changing + * to the usual prime number length hash table will require + * changing the H5C__HASH_FCN macro and the deletion of the + * H5C__HASH_MASK #define. No other changes should be required. + * + * + * When we flush the cache, we need to write entries out in increasing + * address order. An instance of TBBT is used to store dirty entries in + * sorted order. Whether it is cheaper to sort the dirty entries as needed, + * or to maintain the tree is an open question. At a guess, it depends + * on how frequently the cache is flushed. We will see how it goes. + * + * For now at least, I will not remove dirty entries from the tree as they + * are flushed. + * + * tree_len: Number of entries currently in the threaded binary B-tree + * used to maintain a sorted list of dirty entries in the + * cache. + * + * tree_size: Number of bytes of cache entries currently stored in the + * threaded binary B-tree used to maintain a sorted list of + * dirty entries in the cache. + * + * tree_ptr: pointer to the instance of H5TB_TREE used maintain a sorted + * list of dirty entries in the cache. This sorted list has + * two uses: + * + * a) It allows us to flush dirty entries in increasing address + * order, which results in significant savings. + * + * b) It facilitates checking for adjacent dirty entries when + * attempting to evict entries from the cache. While we + * don't use this at present, I hope that this will allow + * some optimizations when I get to it. + * + * + * When a cache entry is protected, it must be removed from the LRU + * list(s) as it cannot be either flushed or evicted until it is unprotected. + * The following fields are used to implement the protected list (pl). + * + * pl_len: Number of entries currently residing on the protected list. + * + * pl_size: Number of bytes of cache entries currently residing on the + * protected list. + * + * pl_head_ptr: Pointer to the head of the doubly linked list of protected + * entries. Note that cache entries on this list are linked + * by their next and prev fields. + * + * This field is NULL if the list is empty. + * + * pl_tail_ptr: Pointer to the tail of the doubly linked list of protected + * entries. Note that cache entries on this list are linked + * by their next and prev fields. + * + * This field is NULL if the list is empty. + * + * + * The cache must have a replacement policy, and the fields supporting this + * policy must be accessible from this structure. + * + * While there has been interest in several replacement policies for + * this cache, the initial development schedule is tight. Thus I have + * elected to support only a modified LRU policy for the first cut. + * + * To further simplify matters, I have simply included the fields needed + * by the modified LRU in this structure. When and if we add support for + * other policies, it will probably be easiest to just add the necessary + * fields to this structure as well -- we only create one instance of this + * structure per file, so the overhead is not excessive. + * + * + * Fields supporting the modified LRU policy: + * + * See most any OS text for a discussion of the LRU replacement policy. + * + * When operating in parallel mode, we must ensure that a read does not + * cause a write. If it does, the process will hang, as the write will + * be collective and the other processes will not know to participate. + * + * To deal with this issue, I have modified the usual LRU policy by adding + * clean and dirty LRU lists to the usual LRU list. + * + * The clean LRU list is simply the regular LRU list with all dirty cache + * entries removed. + * + * Similarly, the dirty LRU list is the regular LRU list with all the clean + * cache entries removed. + * + * When reading in parallel mode, we evict from the clean LRU list only. + * This implies that we must try to ensure that the clean LRU list is + * reasonably well stocked at all times. + * + * We attempt to do this by trying to flush enough entries on each write + * to keep the cLRU_list_size >= min_clean_size. + * + * Even if we start with a completely clean cache, a sequence of protects + * without unprotects can empty the clean LRU list. In this case, the + * cache must grow temporarily. At the next write, we will attempt to + * evict enough entries to reduce index_size to less than max_cache_size. + * While this will usually be possible, all bets are off if enough entries + * are protected. + * + * Discussions of the individual fields used by the modified LRU replacement + * policy follow: + * + * LRU_list_len: Number of cache entries currently on the LRU list. + * + * Observe that LRU_list_len + pl_len must always equal + * index_len. + * + * LRU_list_size: Number of bytes of cache entries currently residing on the + * LRU list. + * + * Observe that LRU_list_size + pl_size must always equal + * index_size. + * + * LRU_head_ptr: Pointer to the head of the doubly linked LRU list. Cache + * entries on this list are linked by their next and prev fields. + * + * This field is NULL if the list is empty. + * + * LRU_tail_ptr: Pointer to the tail of the doubly linked LRU list. Cache + * entries on this list are linked by their next and prev fields. + * + * This field is NULL if the list is empty. + * + * cLRU_list_len: Number of cache entries currently on the clean LRU list. + * + * Observe that cLRU_list_len + dLRU_list_len must always + * equal LRU_list_len. + * + * cLRU_list_size: Number of bytes of cache entries currently residing on + * the clean LRU list. + * + * Observe that cLRU_list_size + dLRU_list_size must always + * equal LRU_list_size. + * + * cLRU_head_ptr: Pointer to the head of the doubly linked clean LRU list. + * Cache entries on this list are linked by their aux_next and + * aux_prev fields. + * + * This field is NULL if the list is empty. + * + * cLRU_tail_ptr: Pointer to the tail of the doubly linked clean LRU list. + * Cache entries on this list are linked by their aux_next and + * aux_prev fields. + * + * This field is NULL if the list is empty. + * + * dLRU_list_len: Number of cache entries currently on the dirty LRU list. + * + * Observe that cLRU_list_len + dLRU_list_len must always + * equal LRU_list_len. + * + * dLRU_list_size: Number of cache entries currently on the dirty LRU list. + * + * Observe that cLRU_list_len + dLRU_list_len must always + * equal LRU_list_len. + * + * dLRU_head_ptr: Pointer to the head of the doubly linked dirty LRU list. + * Cache entries on this list are linked by their aux_next and + * aux_prev fields. + * + * This field is NULL if the list is empty. + * + * dLRU_tail_ptr: Pointer to the tail of the doubly linked dirty LRU list. + * Cache entries on this list are linked by their aux_next and + * aux_prev fields. + * + * This field is NULL if the list is empty. * - * Purpose: Flush all data to disk and destroy the cache. * - * This function fails if any object are protected since the - * resulting file might not be consistent. + * Statistics collection fields: * - * The primary_dxpl_id and secondary_dxpl_id parameters - * specify the dxpl_ids used on the first write occasioned - * by the destroy (primary_dxpl_id), and on all subsequent - * writes (secondary_dxpl_id). This is useful in the metadata - * cache, but may not be needed elsewhere. If so, just use the - * same dxpl_id for both parameters. + * When enabled, these fields are used to collect statistics as described + * below. The first set are collected only when H5C_COLLECT_CACHE_STATS + * is true. * - * Note that *cache_ptr has been freed upon successful return. + * hits: Array of int64 of length H5C__MAX_NUM_TYPE_IDS. The cells + * are used to record the number of times an entry with type id + * equal to the array index has been in cache when requested in + * the current epoch. * - * Return: Non-negative on success/Negative on failure + * misses: Array of int64 of length H5C__MAX_NUM_TYPE_IDS. The cells + * are used to record the number of times an entry with type id + * equal to the array index has not been in cache when + * requested in the current epoch. * - * Programmer: John Mainzer - * 6/2/04 + * insertions: Array of int64 of length H5C__MAX_NUM_TYPE_IDS. The cells + * are used to record the number of times an entry with type + * id equal to the array index has been inserted into the + * cache in the current epoch. * - * Modifications: + * clears: Array of int64 of length H5C__MAX_NUM_TYPE_IDS. The cells + * are used to record the number of times an entry with type + * id equal to the array index has been cleared in the current + * epoch. * - *------------------------------------------------------------------------- - */ -herr_t -H5C_dest(H5F_t * f, - hid_t primary_dxpl_id, - hid_t secondary_dxpl_id, - H5C_t * cache_ptr) -{ - herr_t ret_value=SUCCEED; /* Return value */ - - FUNC_ENTER_NOAPI(H5C_dest, FAIL) - - HDassert( cache_ptr ); - HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); - HDassert( cache_ptr->skip_file_checks || f ); - - if ( H5C_flush_cache(f, primary_dxpl_id, secondary_dxpl_id, - cache_ptr, H5F_FLUSH_INVALIDATE) < 0 ) { - - HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to flush cache") - } - - if ( cache_ptr->index_tree_ptr != NULL ) { - - /* the index tree should be empty, so we can pass in - * NULL for the fd & fk parameters. - */ - H5TB_dfree(cache_ptr->index_tree_ptr, NULL, NULL); - cache_ptr->index_tree_ptr = NULL; - } - - cache_ptr->magic = 0; - - H5FL_FREE(H5C_t, cache_ptr); - -done: - FUNC_LEAVE_NOAPI(ret_value) - -} /* H5C_dest() */ - - -/*------------------------------------------------------------------------- - * Function: H5C_dest_empty + * flushes: Array of int64 of length H5C__MAX_NUM_TYPE_IDS. The cells + * are used to record the number of times an entry with type id + * equal to the array index has been written to disk in the + * current epoch. * - * Purpose: Destroy an empty cache. + * evictions: Array of int64 of length H5C__MAX_NUM_TYPE_IDS. The cells + * are used to record the number of times an entry with type id + * equal to the array index has been evicted from the cache in + * the current epoch. * - * This function fails if the cache is not empty on entry. + * renames: Array of int64 of length H5C__MAX_NUM_TYPE_IDS. The cells + * are used to record the number of times an entry with type + * id equal to the array index has been renamed in the current + * epoch. * - * Note that *cache_ptr has been freed upon successful return. + * total_ht_insertions: Number of times entries have been inserted into the + * hash table in the current epoch. * - * Return: Non-negative on success/Negative on failure + * total_ht_deletions: Number of times entries have been deleted from the + * hash table in the current epoch. * - * Programmer: John Mainzer - * 6/2/04 + * successful_ht_searches: int64 containing the total number of successful + * searches of the hash table in the current epoch. * - * Modifications: + * total_successful_ht_search_depth: int64 containing the total number of + * entries other than the targets examined in successful + * searches of the hash table in the current epoch. * - *------------------------------------------------------------------------- - */ -herr_t -H5C_dest_empty(H5C_t * cache_ptr) -{ - herr_t ret_value=SUCCEED; /* Return value */ - - FUNC_ENTER_NOAPI(H5C_dest_empty, FAIL) - - /* This would normally be an assert, but we need to use an HGOTO_ERROR - * call to shut up the compiler. - */ - if ( ( ! cache_ptr ) || - ( cache_ptr->magic != H5C__H5C_T_MAGIC ) || - ( cache_ptr->index_len != 0 ) ) { - - HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \ - "Bad cache_ptr or non-empty cache on entry.") - } - - - if ( cache_ptr->index_tree_ptr != NULL ) { - - /* the index tree should be empty, so we can pass in - * NULL for the fd & fk parameters. - */ - H5TB_dfree(cache_ptr->index_tree_ptr, NULL, NULL); - cache_ptr->index_tree_ptr = NULL; - } - - cache_ptr->magic = 0; - - H5FL_FREE(H5C_t, cache_ptr); - -done: - FUNC_LEAVE_NOAPI(ret_value) - -} /* H5C_dest_empty() */ - - -/*------------------------------------------------------------------------- - * Function: H5C_flush_cache + * failed_ht_searches: int64 containing the total number of unsuccessful + * searches of the hash table in the current epoch. * - * Purpose: Flush (and possibly destroy) the entries contained in the - * specified cache. + * total_failed_ht_search_depth: int64 containing the total number of + * entries examined in unsuccessful searches of the hash + * table in the current epoch. * - * If the cache contains protected entries, the function will - * fail, as protected entries cannot be flushed. However - * all unprotected entries should be flushed before the - * function returns failure. + * max_index_len: Largest value attained by the index_len field in the + * current epoch. * - * The primary_dxpl_id and secondary_dxpl_id parameters - * specify the dxpl_ids used on the first write occasioned - * by the flush (primary_dxpl_id), and on all subsequent - * writes (secondary_dxpl_id). This is useful in the metadata - * cache, but may not be needed elsewhere. If so, just use the - * same dxpl_id for both parameters. - * - * Return: Non-negative on success/Negative on failure or if there was - * a request to flush all items and something was protected. + * max_index_size: Largest value attained by the index_size field in the + * current epoch. * - * Programmer: John Mainzer - * 6/2/04 + * max_tree_len: Largest value attained by the tree_len field in the + * current epoch. * - * Modifications: + * max_tree_size: Largest value attained by the tree_size field in the + * current epoch. * - *------------------------------------------------------------------------- - */ -herr_t -H5C_flush_cache(H5F_t * f, - hid_t primary_dxpl_id, - hid_t secondary_dxpl_id, - H5C_t * cache_ptr, - unsigned flags) + * max_pl_len: Largest value attained by the pl_len field in the + * current epoch. + * + * max_pl_size: Largest value attained by the pl_size field in the + * current epoch. + * + * The remaining stats are collected only when both H5C_COLLECT_CACHE_STATS + * and H5C_COLLECT_CACHE_ENTRY_STATS are true. + * + * max_accesses: Array of int32 of length H5C__MAX_NUM_TYPE_IDS. The cells + * are used to record the maximum number of times any single + * entry with type id equal to the array index has been + * accessed in the current epoch. + * + * min_accesses: Array of int32 of length H5C__MAX_NUM_TYPE_IDS. The cells + * are used to record the minimum number of times any single + * entry with type id equal to the array index has been + * accessed in the current epoch. + * + * max_clears: Array of int32 of length H5C__MAX_NUM_TYPE_IDS. The cells + * are used to record the maximum number of times any single + * entry with type id equal to the array index has been cleared + * in the current epoch. + * + * max_flushes: Array of int32 of length H5C__MAX_NUM_TYPE_IDS. The cells + * are used to record the maximum number of times any single + * entry with type id equal to the array index has been + * flushed in the current epoch. + * + * max_size: Array of size_t of length H5C__MAX_NUM_TYPE_IDS. The cells + * are used to record the maximum size of any single entry + * with type id equal to the array index that has resided in + * the cache in the current epoch. + * + * + * Fields supporting testing: + * + * For test purposes, it is useful to turn off some asserts and sanity + * checks. The following flags support this. + * + * skip_file_checks: Boolean flag used to skip sanity checks on file + * parameters passed to the cache. In the test bed, there + * is no reason to have a file open, as the cache proper + * just passes these parameters through without using them. + * + * When this flag is set, all sanity checks on the file + * parameters are skipped. The field defaults to FALSE. + * + * skip_dxpl_id_checks: Boolean flag used to skip sanity checks on the + * dxpl_id parameters passed to the cache. These are not + * used directly by the cache, so skipping the checks + * simplifies the test bed. + * + * When this flag is set, all sanity checks on the dxpl_id + * parameters are skipped. The field defaults to FALSE. + * + ****************************************************************************/ + +#define H5C__H5C_T_MAGIC 0x005CAC0E +#define H5C__MAX_NUM_TYPE_IDS 9 + +struct H5C_t { - herr_t status; - herr_t ret_value = SUCCEED; - hbool_t destroy = ( (flags & H5F_FLUSH_INVALIDATE) != 0 ); - hbool_t first_flush = TRUE; - int32_t protected_entries = 0; - H5TB_NODE * node_ptr; - H5C_cache_entry_t * entry_ptr; -#if H5C_DO_SANITY_CHECKS - int32_t actual_index_len = 0; - size_t actual_index_size = 0; -#endif /* H5C_DO_SANITY_CHECKS */ + uint32_t magic; - FUNC_ENTER_NOAPI(H5C_flush_cache, FAIL) + int32_t max_type_id; + const char * (* type_name_table_ptr); - HDassert( cache_ptr ); - HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); - HDassert( cache_ptr->skip_file_checks || f ); + size_t max_cache_size; + size_t min_clean_size; + H5C_write_permitted_func_t check_write_permitted; - if ( cache_ptr->index_tree_ptr->root == NULL ) { + int32_t index_len; + size_t index_size; + H5C_cache_entry_t * (index[H5C__HASH_TABLE_LEN]); - node_ptr = NULL; - HDassert( cache_ptr->index_len == 0 ); - HDassert( cache_ptr->index_size == 0 ); - } else { + int32_t tree_len; + size_t tree_size; + H5TB_TREE * tree_ptr; - node_ptr = H5TB_first(cache_ptr->index_tree_ptr->root); - } + int32_t pl_len; + size_t pl_size; + H5C_cache_entry_t * pl_head_ptr; + H5C_cache_entry_t * pl_tail_ptr; - while ( node_ptr != NULL ) - { - entry_ptr = (H5C_cache_entry_t *)(node_ptr->data); - HDassert( entry_ptr != NULL ); + int32_t LRU_list_len; + size_t LRU_list_size; + H5C_cache_entry_t * LRU_head_ptr; + H5C_cache_entry_t * LRU_tail_ptr; -#if H5C_DO_SANITY_CHECKS - actual_index_len++; - actual_index_size += entry_ptr->size; -#endif /* H5C_DO_SANITY_CHECKS */ + int32_t cLRU_list_len; + size_t cLRU_list_size; + H5C_cache_entry_t * cLRU_head_ptr; + H5C_cache_entry_t * cLRU_tail_ptr; - if ( entry_ptr->is_protected ) { + int32_t dLRU_list_len; + size_t dLRU_list_size; + H5C_cache_entry_t * dLRU_head_ptr; + H5C_cache_entry_t * dLRU_tail_ptr; - /* we have major problems -- but lets flush everything - * we can before we flag an error. - */ - protected_entries++; +#if H5C_COLLECT_CACHE_STATS - } else { + /* stats fields */ + int64_t hits[H5C__MAX_NUM_TYPE_IDS]; + int64_t misses[H5C__MAX_NUM_TYPE_IDS]; + int64_t insertions[H5C__MAX_NUM_TYPE_IDS]; + int64_t clears[H5C__MAX_NUM_TYPE_IDS]; + int64_t flushes[H5C__MAX_NUM_TYPE_IDS]; + int64_t evictions[H5C__MAX_NUM_TYPE_IDS]; + int64_t renames[H5C__MAX_NUM_TYPE_IDS]; - status = H5C_flush_single_entry(f, - primary_dxpl_id, - secondary_dxpl_id, - cache_ptr, - NULL, - entry_ptr->addr, - flags, - node_ptr, - &first_flush, - FALSE); - if ( status < 0 ) { - - /* This shouldn't happen -- if it does, we are toast so - * just scream and die. - */ - HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \ - "Can't flush entry.") - } - } + int64_t total_ht_insertions; + int64_t total_ht_deletions; + int64_t successful_ht_searches; + int64_t total_successful_ht_search_depth; + int64_t failed_ht_searches; + int64_t total_failed_ht_search_depth; - node_ptr = H5TB_next(node_ptr); + int32_t max_index_len; + size_t max_index_size; - } /* while */ + int32_t max_tree_len; + size_t max_tree_size; - HDassert( protected_entries == cache_ptr->pl_len ); + int32_t max_pl_len; + size_t max_pl_size; -#if H5C_DO_SANITY_CHECKS - HDassert( actual_index_len == cache_ptr->index_len ); - HDassert( actual_index_size == cache_ptr->index_size ); -#endif /* H5C_DO_SANITY_CHECKS */ +#if H5C_COLLECT_CACHE_ENTRY_STATS - if ( destroy ) { + int32_t max_accesses[H5C__MAX_NUM_TYPE_IDS]; + int32_t min_accesses[H5C__MAX_NUM_TYPE_IDS]; + int32_t max_clears[H5C__MAX_NUM_TYPE_IDS]; + int32_t max_flushes[H5C__MAX_NUM_TYPE_IDS]; + size_t max_size[H5C__MAX_NUM_TYPE_IDS]; - /* don't pass in any key or data free functions, as all - * unprotected entries should have already been destroyed. - */ - H5TB_free(&(cache_ptr->index_tree_ptr->root), NULL, NULL); - cache_ptr->index_len = 0; - cache_ptr->index_size = 0; +#endif /* H5C_COLLECT_CACHE_ENTRY_STATS */ - if ( protected_entries > 0 ) - { - /* the caller asked us to flush and destroy a cache that - * contains one or more protected entries. Since we can't - * flush protected entries, we haven't destroyed them either. - * Since they are all on the protected list, just re-insert - * them into the tree before we flag an error. - */ - entry_ptr = cache_ptr->pl_head_ptr; +#endif /* H5C_COLLECT_CACHE_STATS */ - while ( entry_ptr != NULL ) - { - if ( H5C_insert_entry_in_tree(cache_ptr, entry_ptr) < 0 ) { + hbool_t skip_file_checks; + hbool_t skip_dxpl_id_checks; - HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \ - "Can't re-insert protected entry.") - } - entry_ptr = entry_ptr->next; - } - } - } +}; - if ( protected_entries > 0 ) { + +/* + * Private file-scope variables. + */ - HGOTO_ERROR(H5E_CACHE, H5E_PROTECT, FAIL, "cache has protected items") - } +/* Declare a free list to manage the H5C_t struct */ +H5FL_DEFINE_STATIC(H5C_t); -done: +/* + * Private file-scope function declarations: + */ - FUNC_LEAVE_NOAPI(ret_value) +static herr_t H5C_flush_single_entry(H5F_t * f, + hid_t primary_dxpl_id, + hid_t secondary_dxpl_id, + H5C_t * cache_ptr, + const H5C_class_t * type_ptr, + haddr_t addr, + unsigned flags, + H5TB_NODE * tgt_node_ptr, + hbool_t * first_flush_ptr, + hbool_t del_entry_from_tree_on_destroy); -} /* H5C_flush_cache() */ +static void * H5C_load_entry(H5F_t * f, + hid_t dxpl_id, + const H5C_class_t * type, + haddr_t addr, + const void * udata1, + void * udata2, + hbool_t skip_file_checks); + +static herr_t H5C_make_space_in_cache(H5F_t * f, + hid_t primary_dxpl_id, + hid_t secondary_dxpl_id, + H5C_t * cache_ptr, + size_t space_needed, + hbool_t write_permitted); /*------------------------------------------------------------------------- - * Function: H5C_insert_entry + * Function: H5C_create * - * Purpose: Adds the specified thing to the cache. The thing need not - * exist on disk yet, but it must have an address and disk - * space reserved. + * Purpose: Allocate, initialize, and return the address of a new + * instance of H5C_t. * - * The primary_dxpl_id and secondary_dxpl_id parameters - * specify the dxpl_ids used on the first write occasioned - * by the insertion (primary_dxpl_id), and on all subsequent - * writes (secondary_dxpl_id). This is useful in the - * metadata cache, but may not be needed elsewhere. If so, - * just use the same dxpl_id for both parameters. + * In general, the max_cache_size parameter must be positive, + * and the min_clean_size parameter must lie in the closed + * interval [0, max_cache_size]. * - * The primary_dxpl_id is the dxpl_id passed to the - * check_write_permitted function if such a function has been - * provided. + * The check_write_permitted parameter must either be NULL, + * or point to a function of type H5C_write_permitted_func_t. + * If it is NULL, the cache will presume that writes are + * always permitted. * - * Observe that this function cannot occasion a read. + * Return: Success: Pointer to the new instance. * - * Return: Non-negative on success/Negative on failure + * Failure: NULL * * Programmer: John Mainzer - * 6/2/04 + * 6/2/04 * * Modifications: * + * JRM -- 7/20/04 + * Updated for the addition of the hash table. + * *------------------------------------------------------------------------- */ -herr_t -H5C_insert_entry(H5F_t * f, - hid_t primary_dxpl_id, - hid_t secondary_dxpl_id, - H5C_t * cache_ptr, - const H5C_class_t * type, - haddr_t addr, - void * thing) +H5C_t * +H5C_create(size_t max_cache_size, + size_t min_clean_size, + int max_type_id, + const char * (* type_name_table_ptr), + H5C_write_permitted_func_t check_write_permitted) { - herr_t result; - herr_t ret_value = SUCCEED; /* Return value */ - hbool_t write_permitted = TRUE; - H5C_cache_entry_t * entry_ptr; - H5TB_NODE * node_ptr = NULL; - - FUNC_ENTER_NOAPI(H5C_insert_entry, FAIL) + int i; + H5C_t * cache_ptr = NULL; + H5C_t * ret_value = NULL; /* Return value */ - HDassert( cache_ptr ); - HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); - HDassert( cache_ptr->skip_file_checks || f ); - HDassert( type ); - HDassert( type->flush ); - HDassert( type->size ); - HDassert( H5F_addr_defined(addr) ); - HDassert( thing ); + FUNC_ENTER_NOAPI(H5C_create, NULL) - entry_ptr = (H5C_cache_entry_t *)thing; + HDassert( max_cache_size > 0 ); + HDassert( min_clean_size <= max_cache_size ); - entry_ptr->addr = addr; - entry_ptr->type = type; + HDassert( max_type_id >= 0 ); + HDassert( max_type_id < H5C__MAX_NUM_TYPE_IDS ); + HDassert( type_name_table_ptr ); - if ( (type->size)(f, thing, &(entry_ptr->size)) < 0 ) { + for ( i = 0; i <= max_type_id; i++ ) { - HGOTO_ERROR(H5E_RESOURCE, H5E_CANTGETSIZE, FAIL, \ - "Can't get size of thing") + HDassert( (type_name_table_ptr)[i] ); + HDassert( HDstrlen(( type_name_table_ptr)[i]) > 0 ); } - HDassert( entry_ptr->size < H5C_MAX_ENTRY_SIZE ); - - entry_ptr->next = NULL; - entry_ptr->prev = NULL; - entry_ptr->aux_next = NULL; - entry_ptr->aux_prev = NULL; - - H5C__RESET_CACHE_ENTRY_STATS(entry_ptr) - - if ( (cache_ptr->index_size + entry_ptr->size) > cache_ptr->max_cache_size ){ - - size_t space_needed; - - if ( cache_ptr->check_write_permitted != NULL ) { - - result = (cache_ptr->check_write_permitted)(f, - primary_dxpl_id, - &write_permitted); - - if ( result < 0 ) { - - HGOTO_ERROR(H5E_CACHE, H5E_CANTINS, FAIL, \ - "Can't get write_permitted") - } - } - - HDassert( entry_ptr->size <= H5C_MAX_ENTRY_SIZE ); - - space_needed = (cache_ptr->index_size + entry_ptr->size) - - cache_ptr->max_cache_size; - - /* It would be nice to be able to do a tight sanity check on - * space_needed here, but it is hard to assign an upper bound on - * its value other than then value assigned to it. - * - * This fact springs from several features of the cache: - * - * First, it is possible for the cache to grow without - * bound as long as entries are protected and not unprotected. - * - * Second, when writes are not permitted it is also possible - * for the cache to grow without bound. - * - * Finally, we don't check to see if the cache is oversized - * at the end of an unprotect. As a result, it is possible - * to have a vastly oversized cache with no protected entries - * as long as all the protects preceed the unprotects. - * - * Since items 1 and 2 are not changing any time soon, I see - * no point in worrying about the third. - * - * In any case, I hope this explains why there is no sanity - * check on space_needed here. - */ - - result = H5C_make_space_in_cache(f, - primary_dxpl_id, - secondary_dxpl_id, - cache_ptr, - space_needed, - write_permitted); - - if ( result < 0 ) { - HGOTO_ERROR(H5E_CACHE, H5E_CANTINS, FAIL, \ - "H5C_make_space_in_cache failed.") - } + if ( NULL == (cache_ptr = H5FL_CALLOC(H5C_t)) ) { + + HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, \ + "memory allocation failed") } - /* verify the the new entry isn't already in the tree -- scream - * and die if it is. + if ( (cache_ptr->tree_ptr = H5TB_fast_dmake(H5TB_FAST_HADDR_COMPARE)) + == NULL ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTCREATE, NULL, "can't create TBBT.") + } + + /* If we get this far, we should succeed. Go ahead and initialize all + * the fields. */ - node_ptr = H5TB_dfind(cache_ptr->index_tree_ptr, entry_ptr, NULL); - if ( node_ptr != NULL ) { + cache_ptr->magic = H5C__H5C_T_MAGIC; - if ( node_ptr->key == ((void *)(entry_ptr)) ) { + cache_ptr->max_type_id = max_type_id; + cache_ptr->type_name_table_ptr = type_name_table_ptr; - HGOTO_ERROR(H5E_CACHE, H5E_CANTINS, FAIL, \ - "entry already in cache.") + cache_ptr->max_cache_size = max_cache_size; + cache_ptr->min_clean_size = min_clean_size; - } else { + cache_ptr->check_write_permitted = check_write_permitted; - HGOTO_ERROR(H5E_CACHE, H5E_CANTINS, FAIL, \ - "duplicate entry in cache.") - - } + cache_ptr->index_len = 0; + cache_ptr->index_size = (size_t)0; + + cache_ptr->tree_len = 0; + cache_ptr->tree_size = (size_t)0; + + for ( i = 0; i < H5C__HASH_TABLE_LEN; i++ ) + { + (cache_ptr->index)[i] = NULL; } - /* we don't initialize the protected field until here as it is - * possible that the entry is already in the cache, and already - * protected. If it is, we don't want to make things worse by - * marking it unprotected. - */ + cache_ptr->pl_len = 0; + cache_ptr->pl_size = (size_t)0; + cache_ptr->pl_head_ptr = NULL; + cache_ptr->pl_tail_ptr = NULL; - entry_ptr->is_protected = FALSE; + cache_ptr->LRU_list_len = 0; + cache_ptr->LRU_list_size = (size_t)0; + cache_ptr->LRU_head_ptr = NULL; + cache_ptr->LRU_tail_ptr = NULL; - if ( H5C_insert_entry_in_tree(cache_ptr, entry_ptr) < 0 ) { + cache_ptr->cLRU_list_len = 0; + cache_ptr->cLRU_list_size = (size_t)0; + cache_ptr->cLRU_head_ptr = NULL; + cache_ptr->cLRU_tail_ptr = NULL; - HGOTO_ERROR(H5E_CACHE, H5E_CANTINS, FAIL, \ - "Can't insert entry in tree.") - - } + cache_ptr->dLRU_list_len = 0; + cache_ptr->dLRU_list_size = (size_t)0; + cache_ptr->dLRU_head_ptr = NULL; + cache_ptr->dLRU_tail_ptr = NULL; - if ( H5C_update_rp_for_insertion(cache_ptr, entry_ptr) < 0 ) { + H5C_stats__reset(cache_ptr); - HGOTO_ERROR(H5E_CACHE, H5E_CANTINS, FAIL, \ - "Can't update replacement policy for insertion.") - - } + cache_ptr->skip_file_checks = FALSE; + cache_ptr->skip_dxpl_id_checks = FALSE; - H5C__UPDATE_STATS_FOR_INSERTION(cache_ptr, entry_ptr) + /* Set return value */ + ret_value = cache_ptr; done: + if ( ret_value == 0 ) { + + if ( cache_ptr != NULL ) { + + if ( cache_ptr->tree_ptr != NULL ) { + + /* the index tree should be empty, so we can pass in + * NULL for the fd & fk parameters. + */ + H5TB_dfree(cache_ptr->tree_ptr, NULL, NULL); + } + + cache_ptr->magic = 0; + H5FL_FREE(H5C_t, cache_ptr); + cache_ptr = NULL; + + } /* end if */ + + } /* end if */ + FUNC_LEAVE_NOAPI(ret_value) -} /* H5C_insert_entry() */ +} /* H5C_create() */ /*------------------------------------------------------------------------- + * Function: H5C_dest * - * Function: H5C_rename_entry + * Purpose: Flush all data to disk and destroy the cache. * - * Purpose: Use this function to notify the cache that an entry's - * file address changed. + * This function fails if any object are protected since the + * resulting file might not be consistent. + * + * The primary_dxpl_id and secondary_dxpl_id parameters + * specify the dxpl_ids used on the first write occasioned + * by the destroy (primary_dxpl_id), and on all subsequent + * writes (secondary_dxpl_id). This is useful in the metadata + * cache, but may not be needed elsewhere. If so, just use the + * same dxpl_id for both parameters. + * + * Note that *cache_ptr has been freed upon successful return. * * Return: Non-negative on success/Negative on failure * * Programmer: John Mainzer - * 6/2/04 + * 6/2/04 * * Modifications: * *------------------------------------------------------------------------- */ - herr_t -H5C_rename_entry(H5F_t * f, - H5C_t * cache_ptr, - const H5C_class_t * type, - haddr_t old_addr, - haddr_t new_addr) +H5C_dest(H5F_t * f, + hid_t primary_dxpl_id, + hid_t secondary_dxpl_id, + H5C_t * cache_ptr) { - herr_t ret_value = SUCCEED; /* Return value */ - H5TB_NODE * new_node_ptr = NULL; - H5TB_NODE * old_node_ptr = NULL; - H5C_cache_entry_t * entry_ptr; - H5C_cache_entry_t search_target; + herr_t ret_value=SUCCEED; /* Return value */ - FUNC_ENTER_NOAPI(H5C_rename_entry, FAIL) + FUNC_ENTER_NOAPI(H5C_dest, FAIL) HDassert( cache_ptr ); HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); HDassert( cache_ptr->skip_file_checks || f ); - HDassert( type ); - HDassert( H5F_addr_defined(old_addr) ); - HDassert( H5F_addr_defined(new_addr) ); - HDassert( H5F_addr_ne(old_addr, new_addr) ); - - search_target.addr = old_addr; - old_node_ptr = H5TB_dfind(cache_ptr->index_tree_ptr, - (void *)(&search_target), - NULL); - if ( ( old_node_ptr == NULL ) || - ( ((H5C_cache_entry_t *)(old_node_ptr->key))->type != type ) ) { + if ( H5C_flush_cache(f, primary_dxpl_id, secondary_dxpl_id, + cache_ptr, H5F_FLUSH_INVALIDATE) < 0 ) { - /* the old item doesn't exist in the cache, so we are done. */ - HGOTO_DONE(SUCCEED) + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to flush cache") + } - } else { + if ( cache_ptr->tree_ptr != NULL ) { - entry_ptr = old_node_ptr->key; - HDassert( entry_ptr->addr == old_addr ); - HDassert( entry_ptr->type == type ); - HDassert( !(entry_ptr->is_protected) ); + /* the index tree should be empty, so we can pass in + * NULL for the fd & fk parameters. + */ + H5TB_dfree(cache_ptr->tree_ptr, NULL, NULL); + cache_ptr->tree_ptr = NULL; } - search_target.addr = new_addr; - new_node_ptr = H5TB_dfind(cache_ptr->index_tree_ptr, - (void *)&search_target, - NULL); + cache_ptr->magic = 0; - if ( new_node_ptr != NULL ) { /* we are hosed */ - - if ( ((H5C_cache_entry_t *)(new_node_ptr->key))->type == type ) { + H5FL_FREE(H5C_t, cache_ptr); - HGOTO_ERROR(H5E_CACHE, H5E_CANTRENAME, FAIL, \ - "Target already renamed & reinserted???.") +done: + FUNC_LEAVE_NOAPI(ret_value) - } else { +} /* H5C_dest() */ - HGOTO_ERROR(H5E_CACHE, H5E_CANTRENAME, FAIL, \ - "New address already in use?.") + +/*------------------------------------------------------------------------- + * Function: H5C_dest_empty + * + * Purpose: Destroy an empty cache. + * + * This function fails if the cache is not empty on entry. + * + * Note that *cache_ptr has been freed upon successful return. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 6/2/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +herr_t +H5C_dest_empty(H5C_t * cache_ptr) +{ + herr_t ret_value=SUCCEED; /* Return value */ - } - } + FUNC_ENTER_NOAPI(H5C_dest_empty, FAIL) - /* If we get this far, we have work to do. Remove *entry_ptr from - * the tree, change its address to the new address, and then re-insert. - * Update the replacement policy for a hit to avoid an eviction before - * the renamed entry is touched. Update stats for a rename. - * - * Note that we do not check the size of the cache, or evict anything. - * Since this is a simple re-name, cache size should be unaffected. + /* This would normally be an assert, but we need to use an HGOTO_ERROR + * call to shut up the compiler. */ + if ( ( ! cache_ptr ) || + ( cache_ptr->magic != H5C__H5C_T_MAGIC ) || + ( cache_ptr->index_len != 0 ) ) { - if ( H5C_remove_entry_from_tree(cache_ptr, entry_ptr, old_node_ptr) < 0 ) { - - HGOTO_ERROR(H5E_CACHE, H5E_CANTRENAME, FAIL, \ - "Can't remove entry from tree.") + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \ + "Bad cache_ptr or non-empty cache on entry.") } - entry_ptr->addr = new_addr; - if ( H5C_insert_entry_in_tree(cache_ptr, entry_ptr) < 0 ) { + if ( cache_ptr->tree_ptr != NULL ) { - HGOTO_ERROR(H5E_CACHE, H5E_CANTRENAME, FAIL, \ - "Can't re-insert entry from tree.") + /* the tree should be empty, so we can pass in + * NULL for the fd & fk parameters. + */ + H5TB_dfree(cache_ptr->tree_ptr, NULL, NULL); + cache_ptr->tree_ptr = NULL; } - if ( H5C_update_rp_for_rename(cache_ptr, entry_ptr) < 0 ) { - - HGOTO_ERROR(H5E_CACHE, H5E_CANTRENAME, FAIL, \ - "Can't can't update replacement policy for a hit.") - } + cache_ptr->magic = 0; - H5C__UPDATE_STATS_FOR_RENAME(cache_ptr, entry_ptr) + H5FL_FREE(H5C_t, cache_ptr); done: - FUNC_LEAVE_NOAPI(ret_value) -} /* H5C_rename_entry() */ +} /* H5C_dest_empty() */ /*------------------------------------------------------------------------- - * Function: H5C_protect - * - * Purpose: If the target entry is not in the cache, load it. If - * necessary, attempt to evict one or more entries to keep - * the cache within its maximum size. + * Function: H5C_flush_cache * - * Mark the target entry as protected, and return its address - * to the caller. The caller must call H5C_unprotect() when - * finished with the entry. + * Purpose: Flush (and possibly destroy) the entries contained in the + * specified cache. * - * While it is protected, the entry may not be either evicted - * or flushed -- nor may it be accessed by another call to - * H5C_protect. Any attempt to do so will result in a failure. + * If the cache contains protected entries, the function will + * fail, as protected entries cannot be flushed. However + * all unprotected entries should be flushed before the + * function returns failure. * * The primary_dxpl_id and secondary_dxpl_id parameters * specify the dxpl_ids used on the first write occasioned - * by the insertion (primary_dxpl_id), and on all subsequent - * writes (secondary_dxpl_id). This is useful in the - * metadata cache, but may not be needed elsewhere. If so, - * just use the same dxpl_id for both parameters. - * - * All reads are performed with the primary_dxpl_id. - * - * Similarly, the primary_dxpl_id is passed to the - * check_write_permitted function if it is called. - * - * Return: Success: Ptr to the desired entry - * - * Failure: NULL + * by the flush (primary_dxpl_id), and on all subsequent + * writes (secondary_dxpl_id). This is useful in the metadata + * cache, but may not be needed elsewhere. If so, just use the + * same dxpl_id for both parameters. + * + * Return: Non-negative on success/Negative on failure or if there was + * a request to flush all items and something was protected. * - * Programmer: John Mainzer - 6/2/04 + * Programmer: John Mainzer + * 6/2/04 * * Modifications: - * - *------------------------------------------------------------------------- - */ - -void * -H5C_protect(H5F_t * f, - hid_t primary_dxpl_id, - hid_t secondary_dxpl_id, - H5C_t * cache_ptr, - const H5C_class_t * type, - haddr_t addr, - const void * udata1, - void * udata2) + * + * JRM -- 7/20/04 + * Modified the function for the addition of the hash table. + * + *------------------------------------------------------------------------- + */ +herr_t +H5C_flush_cache(H5F_t * f, + hid_t primary_dxpl_id, + hid_t secondary_dxpl_id, + H5C_t * cache_ptr, + unsigned flags) { - hbool_t hit = FALSE; - void * thing = NULL; + herr_t status; + herr_t ret_value = SUCCEED; + hbool_t destroy = ( (flags & H5F_FLUSH_INVALIDATE) != 0 ); + hbool_t first_flush = TRUE; + int32_t protected_entries = 0; + int32_t i; + H5TB_NODE * node_ptr; H5C_cache_entry_t * entry_ptr; - H5TB_NODE * node_ptr = NULL; - void * ret_value; /* Return value */ - H5C_cache_entry_t search_target; +#if H5C_DO_SANITY_CHECKS + int32_t actual_tree_len = 0; + size_t actual_tree_size = 0; +#endif /* H5C_DO_SANITY_CHECKS */ - FUNC_ENTER_NOAPI(H5C_protect, NULL) + FUNC_ENTER_NOAPI(H5C_flush_cache, FAIL) - /* check args */ HDassert( cache_ptr ); HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); HDassert( cache_ptr->skip_file_checks || f ); - HDassert( type ); - HDassert( type->flush ); - HDassert( type->load ); - HDassert( H5F_addr_defined(addr) ); - - /* first check to see if the target is in cache */ - search_target.addr = addr; - node_ptr = H5TB_dfind(cache_ptr->index_tree_ptr, - (void *)(&search_target), - NULL); - - if ( node_ptr != NULL ) { - - hit = TRUE; - thing = node_ptr->key; - entry_ptr = (H5C_cache_entry_t *)thing; + HDassert( cache_ptr->tree_ptr ); - } else { /* must try to load the entry from disk. */ + if ( cache_ptr->tree_ptr->root == NULL ) { - hit = FALSE; - thing = H5C_load_entry(f, primary_dxpl_id, type, addr, udata1, udata2, - cache_ptr->skip_file_checks); + node_ptr = NULL; + HDassert( cache_ptr->tree_len == 0 ); + HDassert( cache_ptr->tree_size == 0 ); - if ( thing == NULL ) { + } else { - HGOTO_ERROR(H5E_CACHE, H5E_CANTLOAD, NULL, "can't load entry") - } + node_ptr = H5TB_first(cache_ptr->tree_ptr->root); + } - entry_ptr = (H5C_cache_entry_t *)thing; + while ( node_ptr != NULL ) + { + entry_ptr = (H5C_cache_entry_t *)(node_ptr->data); - /* try to free up some space if necessay */ - if ( (cache_ptr->index_size + entry_ptr->size) > - cache_ptr->max_cache_size ) { + HDassert( entry_ptr != NULL ); + HDassert( entry_ptr->in_tree ); - hbool_t write_permitted = TRUE; - herr_t result; - size_t space_needed; +#if H5C_DO_SANITY_CHECKS + actual_tree_len++; + actual_tree_size += entry_ptr->size; +#endif /* H5C_DO_SANITY_CHECKS */ - if ( cache_ptr->check_write_permitted != NULL ) { + if ( entry_ptr->is_protected ) { - result = (cache_ptr->check_write_permitted)(f, - primary_dxpl_id, - &write_permitted); + /* we have major problems -- but lets flush everything + * we can before we flag an error. + */ + protected_entries++; - if ( result < 0 ) { + } else { - HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, NULL, \ - "Can't get write_permitted") - } + status = H5C_flush_single_entry(f, + primary_dxpl_id, + secondary_dxpl_id, + cache_ptr, + NULL, + entry_ptr->addr, + flags, + node_ptr, + &first_flush, + FALSE); + if ( status < 0 ) { + + /* This shouldn't happen -- if it does, we are toast so + * just scream and die. + */ + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \ + "Can't flush entry.") } + } - HDassert( entry_ptr->size <= H5C_MAX_ENTRY_SIZE ); - - space_needed = (cache_ptr->index_size + entry_ptr->size) - - cache_ptr->max_cache_size; + node_ptr = H5TB_next(node_ptr); - /* It would be nice to be able to do a tight sanity check on - * space_needed here, but it is hard to assign an upper bound on - * its value other than then value assigned to it. - * - * This fact springs from several features of the cache: - * - * First, it is possible for the cache to grow without - * bound as long as entries are protected and not unprotected. - * - * Second, when writes are not permitted it is also possible - * for the cache to grow without bound. - * - * Finally, we don't check to see if the cache is oversized - * at the end of an unprotect. As a result, it is possible - * to have a vastly oversized cache with no protected entries - * as long as all the protects preceed the unprotects. - * - * Since items 1 and 2 are not changing any time soon, I see - * no point in worrying about the third. - * - * In any case, I hope this explains why there is no sanity - * check on space_needed here. - */ + } /* while */ - result = H5C_make_space_in_cache(f, primary_dxpl_id, - secondary_dxpl_id, cache_ptr, - space_needed, write_permitted); +#if H5C_DO_SANITY_CHECKS + HDassert( actual_tree_len == cache_ptr->tree_len ); + HDassert( actual_tree_size == cache_ptr->tree_size ); +#endif /* H5C_DO_SANITY_CHECKS */ - if ( result < 0 ) { + if ( destroy ) { - HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, NULL, \ - "H5C_make_space_in_cache failed.") + /* don't pass in any key or data free functions, as all + * unprotected entries should have already been destroyed. + */ + H5TB_free(&(cache_ptr->tree_ptr->root), NULL, NULL); + cache_ptr->tree_len = 0; + cache_ptr->tree_size = 0; + + /* Since we are doing a destroy, we must make a pass through + * the hash table and flush all entries that remain. Note that + * all remaining entries entries must be clean, so this will + * not result in any writes to disk. + */ + for ( i = 0; i < H5C__HASH_TABLE_LEN; i++ ) + { + while ( cache_ptr->index[i] ) + { + entry_ptr = cache_ptr->index[i]; + + if ( entry_ptr->is_protected ) { + + /* we have major problems -- but lets flush and destroy + * everything we can before we flag an error. + */ + + H5C__DELETE_FROM_INDEX(cache_ptr, entry_ptr) + + if ( !entry_ptr->in_tree ) { + + protected_entries++; + HDassert( !(entry_ptr->is_dirty) ); + } + } else { + + HDassert( !(entry_ptr->is_dirty) ); + HDassert( !(entry_ptr->in_tree) ); + + status = H5C_flush_single_entry(f, + primary_dxpl_id, + secondary_dxpl_id, + cache_ptr, + NULL, + entry_ptr->addr, + flags, + NULL, + &first_flush, + FALSE); + if ( status < 0 ) { + + /* This shouldn't happen -- if it does, we are toast so + * just scream and die. + */ + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \ + "Can't flush entry.") + } + } } } - /* insert the entry in the tree and in the protected list. */ - if ( H5C_insert_entry_in_tree(cache_ptr, entry_ptr) < 0 ) { + HDassert( protected_entries == cache_ptr->pl_len ); - HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, NULL, \ - "Can't insert newly loaded entry in tree.") - } + if ( protected_entries > 0 ) + { + /* the caller asked us to flush and destroy a cache that + * contains one or more protected entries. Since we can't + * flush protected entries, we haven't destroyed them either. + * Since they are all on the protected list, just re-insert + * them into the cache before we flag an error. + */ + entry_ptr = cache_ptr->pl_head_ptr; - /* insert the entry in the data structures used by the replacement - * policy. We are just going to take it out again when we update - * the replacement policy for a protect, but this simplifies the - * code. If we do this often enough, we may want to optimize this. - */ + while ( entry_ptr != NULL ) + { + entry_ptr->in_tree = FALSE; - if ( H5C_update_rp_for_insertion(cache_ptr, entry_ptr) < 0 ) { + H5C__INSERT_IN_INDEX(cache_ptr, entry_ptr, FAIL) - HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, NULL, \ - "Can't update replacement policy for newly loaded entry.") + if ( entry_ptr->is_dirty ) { + H5C__INSERT_ENTRY_IN_TREE(cache_ptr, entry_ptr) + } + entry_ptr = entry_ptr->next; + } } } - HDassert( entry_ptr->addr == addr ); - HDassert( entry_ptr->type == type ); - - if ( entry_ptr->is_protected ) { - - HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, NULL, \ - "Target already protected?!?.") - } + HDassert( protected_entries <= cache_ptr->pl_len ); - if ( H5C_update_rp_for_protect(cache_ptr, entry_ptr) < 0 ) { + if ( cache_ptr->pl_len > 0 ) { - HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, NULL, \ - "Can't update replacement policy for protect") + HGOTO_ERROR(H5E_CACHE, H5E_PROTECT, FAIL, "cache has protected items") } - entry_ptr->is_protected = TRUE; - - ret_value = thing; - - H5C__UPDATE_STATS_FOR_PROTECT(cache_ptr, entry_ptr, hit) - done: FUNC_LEAVE_NOAPI(ret_value) -} /* H5C_protect() */ +} /* H5C_flush_cache() */ /*------------------------------------------------------------------------- - * Function: H5C_unprotect - * - * Purpose: Undo an H5C_protect() call -- specifically, mark the - * entry as unprotected, remove it from the protected list, - * and give it back to the replacement policy. + * Function: H5C_insert_entry * - * The TYPE and ADDR arguments must be the same as those in - * the corresponding call to H5C_protect() and the THING - * argument must be the value returned by that call to - * H5C_protect(). + * Purpose: Adds the specified thing to the cache. The thing need not + * exist on disk yet, but it must have an address and disk + * space reserved. * * The primary_dxpl_id and secondary_dxpl_id parameters * specify the dxpl_ids used on the first write occasioned - * by the unprotect (primary_dxpl_id), and on all subsequent - * writes (secondary_dxpl_id). Since an uprotect cannot - * occasion a write at present, all this is moot for now. - * However, things change, and in any case, - * H5C_flush_single_entry() needs primary_dxpl_id and - * secondary_dxpl_id in its parameter list. - * - * The function can't cause a read either, so the dxpl_id - * parameters are moot in this case as well. + * by the insertion (primary_dxpl_id), and on all subsequent + * writes (secondary_dxpl_id). This is useful in the + * metadata cache, but may not be needed elsewhere. If so, + * just use the same dxpl_id for both parameters. * - * Return: Non-negative on success/Negative on failure + * The primary_dxpl_id is the dxpl_id passed to the + * check_write_permitted function if such a function has been + * provided. * - * If the deleted flag is TRUE, simply remove the target entry - * from the cache, clear it, and free it without writing it to - * disk. + * Observe that this function cannot occasion a read. * * Return: Non-negative on success/Negative on failure * * Programmer: John Mainzer - * 6/2/04 + * 6/2/04 * * Modifications: * + * JRM -- 7/21/04 + * Updated function for the addition of the hash table. + * *------------------------------------------------------------------------- */ + herr_t -H5C_unprotect(H5F_t * f, - hid_t primary_dxpl_id, - hid_t secondary_dxpl_id, - H5C_t * cache_ptr, - const H5C_class_t * type, - haddr_t addr, - void * thing, - hbool_t deleted) +H5C_insert_entry(H5F_t * f, + hid_t primary_dxpl_id, + hid_t secondary_dxpl_id, + H5C_t * cache_ptr, + const H5C_class_t * type, + haddr_t addr, + void * thing) { - herr_t ret_value = SUCCEED; /* Return value */ + herr_t result; + herr_t ret_value = SUCCEED; /* Return value */ + hbool_t write_permitted = TRUE; H5C_cache_entry_t * entry_ptr; + H5C_cache_entry_t * test_entry_ptr; + + FUNC_ENTER_NOAPI(H5C_insert_entry, FAIL) + + HDassert( cache_ptr ); + HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); + HDassert( cache_ptr->skip_file_checks || f ); + HDassert( type ); + HDassert( type->flush ); + HDassert( type->size ); + HDassert( H5F_addr_defined(addr) ); + HDassert( thing ); + + entry_ptr = (H5C_cache_entry_t *)thing; + + entry_ptr->addr = addr; + entry_ptr->type = type; + + if ( (type->size)(f, thing, &(entry_ptr->size)) < 0 ) { + + HGOTO_ERROR(H5E_RESOURCE, H5E_CANTGETSIZE, FAIL, \ + "Can't get size of thing") + } + + HDassert( entry_ptr->size < H5C_MAX_ENTRY_SIZE ); + + entry_ptr->in_tree = FALSE; - FUNC_ENTER_NOAPI(H5C_unprotect, FAIL) + entry_ptr->ht_next = NULL; + entry_ptr->ht_prev = NULL; - HDassert( cache_ptr ); - HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); - HDassert( cache_ptr->skip_file_checks || f ); - HDassert( type ); - HDassert( type->clear ); - HDassert( type->flush ); - HDassert( H5F_addr_defined(addr) ); - HDassert( thing ); + entry_ptr->next = NULL; + entry_ptr->prev = NULL; - entry_ptr = (H5C_cache_entry_t *)thing; + entry_ptr->aux_next = NULL; + entry_ptr->aux_prev = NULL; - HDassert( entry_ptr->addr == addr ); - HDassert( entry_ptr->type == type ); + H5C__RESET_CACHE_ENTRY_STATS(entry_ptr) - if ( ! (entry_ptr->is_protected) ) { + if ((cache_ptr->index_size + entry_ptr->size) > cache_ptr->max_cache_size) { - HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, \ - "Entry already unprotected??") - } + size_t space_needed; - if ( H5C_update_rp_for_unprotect(cache_ptr, entry_ptr) < 0 ) { + if ( cache_ptr->check_write_permitted != NULL ) { - HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, \ - "Can't update replacement policy for unprotect.") - } + result = (cache_ptr->check_write_permitted)(f, + primary_dxpl_id, + &write_permitted); - entry_ptr->is_protected = FALSE; + if ( result < 0 ) { - /* this implementation of the "deleted" option is a bit inefficient, as - * we re-insert the entry to be deleted into the replacement policy - * data structures, only to remove them again. Depending on how often - * we do this, we may want to optimize a bit. - * - * On the other hand, this implementation is reasonably clean, and - * makes good use of existing code. - * JRM - 5/19/04 - */ - if ( deleted ) { + HGOTO_ERROR(H5E_CACHE, H5E_CANTINS, FAIL, \ + "Can't get write_permitted") + } + } - /* the following first flush flag will never be used as we are - * calling H5C_flush_single_entry with both the H5F_FLUSH_CLEAR_ONLY - * and H5F_FLUSH_INVALIDATE flags. However, it is needed for the - * function call. - */ - hbool_t dummy_first_flush = TRUE; - H5TB_NODE * node_ptr; + HDassert( entry_ptr->size <= H5C_MAX_ENTRY_SIZE ); + + space_needed = (cache_ptr->index_size + entry_ptr->size) - + cache_ptr->max_cache_size; - /* verify that the target entry is in the tree. */ + /* It would be nice to be able to do a tight sanity check on + * space_needed here, but it is hard to assign an upper bound on + * its value other than then value assigned to it. + * + * This fact springs from several features of the cache: + * + * First, it is possible for the cache to grow without + * bound as long as entries are protected and not unprotected. + * + * Second, when writes are not permitted it is also possible + * for the cache to grow without bound. + * + * Finally, we don't check to see if the cache is oversized + * at the end of an unprotect. As a result, it is possible + * to have a vastly oversized cache with no protected entries + * as long as all the protects preceed the unprotects. + * + * Since items 1 and 2 are not changing any time soon, I see + * no point in worrying about the third. + * + * In any case, I hope this explains why there is no sanity + * check on space_needed here. + */ - node_ptr = H5TB_dfind(cache_ptr->index_tree_ptr, entry_ptr, NULL); + result = H5C_make_space_in_cache(f, + primary_dxpl_id, + secondary_dxpl_id, + cache_ptr, + space_needed, + write_permitted); - if ( ( node_ptr == NULL ) || ( node_ptr->key != thing ) ) { + if ( result < 0 ) { - HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, \ - "thing not in tree?!?.") + HGOTO_ERROR(H5E_CACHE, H5E_CANTINS, FAIL, \ + "H5C_make_space_in_cache failed.") } + } - if ( H5C_flush_single_entry(f, - primary_dxpl_id, - secondary_dxpl_id, - cache_ptr, - type, - addr, - (H5F_FLUSH_CLEAR_ONLY|H5F_FLUSH_INVALIDATE), - node_ptr, - &dummy_first_flush, - TRUE) < 0 ) { + /* verify that the new entry isn't already in the hash table -- scream + * and die if it is. + */ - HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, \ - "thing not in tree?!?.") + H5C__SEARCH_INDEX(cache_ptr, addr, test_entry_ptr, FAIL) + + if ( test_entry_ptr != NULL ) { + + if ( test_entry_ptr == entry_ptr ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTINS, FAIL, \ + "entry already in cache.") + + } else { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTINS, FAIL, \ + "duplicate entry in cache.") } } + /* we don't initialize the protected field until here as it is + * possible that the entry is already in the cache, and already + * protected. If it is, we don't want to make things worse by + * marking it unprotected. + */ + + entry_ptr->is_protected = FALSE; + + H5C__INSERT_IN_INDEX(cache_ptr, entry_ptr, FAIL) + + if ( entry_ptr->is_dirty ) { + + H5C__INSERT_ENTRY_IN_TREE(cache_ptr, entry_ptr) + } + + H5C__UPDATE_RP_FOR_INSERTION(cache_ptr, entry_ptr, FAIL) + + H5C__UPDATE_STATS_FOR_INSERTION(cache_ptr, entry_ptr) + done: FUNC_LEAVE_NOAPI(ret_value) -} /* H5C_unprotect() */ +} /* H5C_insert_entry() */ /*------------------------------------------------------------------------- - * Function: H5C_stats * - * Purpose: Prints statistics about the cache. + * Function: H5C_rename_entry + * + * Purpose: Use this function to notify the cache that an entry's + * file address changed. * * Return: Non-negative on success/Negative on failure * @@ -2060,1399 +2799,1385 @@ done: * * Modifications: * + * JRM -- 7/21/04 + * Updated function for the addition of the hash table. + * *------------------------------------------------------------------------- */ herr_t -H5C_stats(H5C_t * cache_ptr, - const char * cache_name, - hbool_t -#ifndef H5C_COLLECT_CACHE_STATS - UNUSED -#endif /* H5C_COLLECT_CACHE_STATS */ - display_detailed_stats) +H5C_rename_entry(H5F_t * f, + H5C_t * cache_ptr, + const H5C_class_t * type, + haddr_t old_addr, + haddr_t new_addr) { - herr_t ret_value = SUCCEED; /* Return value */ -#if H5C_COLLECT_CACHE_STATS - int i; - int64_t total_hits = 0; - int64_t total_misses = 0; - int64_t total_insertions = 0; - int64_t total_clears = 0; - int64_t total_flushes = 0; - int64_t total_evictions = 0; - int64_t total_renames = 0; - int32_t aggregate_max_accesses = 0; - int32_t aggregate_min_accesses = 1000000; - int32_t aggregate_max_clears = 0; - int32_t aggregate_max_flushes = 0; - double hit_rate; -#endif /* H5C_COLLECT_CACHE_STATS */ - - FUNC_ENTER_NOAPI(H5C_stats, FAIL) - - /* This would normally be an assert, but we need to use an HGOTO_ERROR - * call to shut up the compiler. - */ - if ( ( ! cache_ptr ) || - ( cache_ptr->magic != H5C__H5C_T_MAGIC ) || - ( !cache_name ) ) { - - HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Bad cache_ptr or cache_name") - } - -#if H5C_COLLECT_CACHE_STATS - - for ( i = 0; i <= cache_ptr->max_type_id; i++ ) { - - total_hits += cache_ptr->hits[i]; - total_misses += cache_ptr->misses[i]; - total_insertions += cache_ptr->insertions[i]; - total_clears += cache_ptr->clears[i]; - total_flushes += cache_ptr->flushes[i]; - total_evictions += cache_ptr->evictions[i]; - total_renames += cache_ptr->renames[i]; -#if H5C_COLLECT_CACHE_ENTRY_STATS - if ( aggregate_max_accesses < cache_ptr->max_accesses[i] ) - aggregate_max_accesses = cache_ptr->max_accesses[i]; - if ( aggregate_min_accesses > aggregate_max_accesses ) - aggregate_min_accesses = aggregate_max_accesses; - if ( aggregate_min_accesses > cache_ptr->min_accesses[i] ) - aggregate_min_accesses = cache_ptr->min_accesses[i]; - if ( aggregate_max_clears < cache_ptr->max_clears[i] ) - aggregate_max_clears = cache_ptr->max_clears[i]; - if ( aggregate_max_flushes < cache_ptr->max_flushes[i] ) - aggregate_max_flushes = cache_ptr->max_flushes[i]; -#endif /* H5C_COLLECT_CACHE_ENTRY_STATS */ - } - - if ( ( total_hits > 0 ) || ( total_misses > 0 ) ) { - - hit_rate = 100.0 * ((double)(total_hits)) / - ((double)(total_hits + total_misses)); - } else { - hit_rate = 0.0; - } - - HDfprintf(stdout, "\nH5C: cache statistics for %s\n", - cache_name); - - HDfprintf(stdout, "\n"); - - HDfprintf(stdout, - " current (max) index size / length = %ld (%ld) / %ld (%ld)\n", - (long)(cache_ptr->index_size), - (long)(cache_ptr->max_index_size), - (long)(cache_ptr->index_len), - (long)(cache_ptr->max_index_len)); - - HDfprintf(stdout, - " current (max) PL size / length = %ld (%ld) / %ld (%ld)\n", - (long)(cache_ptr->pl_size), - (long)(cache_ptr->max_pl_size), - (long)(cache_ptr->pl_len), - (long)(cache_ptr->max_pl_len)); - - HDfprintf(stdout, - " current LRU list size / length = %ld / %ld\n", - (long)(cache_ptr->LRU_list_size), - (long)(cache_ptr->LRU_list_len)); + herr_t ret_value = SUCCEED; /* Return value */ + H5TB_NODE * old_node_ptr = NULL; + H5C_cache_entry_t * entry_ptr = NULL; + H5C_cache_entry_t * test_entry_ptr = NULL; + H5C_cache_entry_t search_target; - HDfprintf(stdout, - " current clean LRU size / length = %ld / %ld\n", - (long)(cache_ptr->cLRU_list_size), - (long)(cache_ptr->cLRU_list_len)); + FUNC_ENTER_NOAPI(H5C_rename_entry, FAIL) - HDfprintf(stdout, - " current dirty LRU size / length = %ld / %ld\n", - (long)(cache_ptr->dLRU_list_size), - (long)(cache_ptr->dLRU_list_len)); + HDassert( cache_ptr ); + HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); + HDassert( cache_ptr->skip_file_checks || f ); + HDassert( type ); + HDassert( H5F_addr_defined(old_addr) ); + HDassert( H5F_addr_defined(new_addr) ); + HDassert( H5F_addr_ne(old_addr, new_addr) ); - HDfprintf(stdout, - " Total hits / misses / hit_rate = %ld / %ld / %f\n", - (long)total_hits, - (long)total_misses, - hit_rate); + H5C__SEARCH_INDEX(cache_ptr, old_addr, entry_ptr, FAIL) - HDfprintf(stdout, - " Total clears / flushes / evictions = %ld / %ld / %ld\n", - (long)total_clears, - (long)total_flushes, - (long)total_evictions); + if ( ( entry_ptr == NULL ) || ( entry_ptr->type != type ) ) { - HDfprintf(stdout, " Total insertions / renames = %ld / %ld\n", - (long)total_insertions, - (long)total_renames); + /* the old item doesn't exist in the cache, so we are done. */ + HGOTO_DONE(SUCCEED) -#if H5C_COLLECT_CACHE_ENTRY_STATS + } else { - HDfprintf(stdout, " aggregate max / min accesses = %d / %d\n", - (int)aggregate_max_accesses, - (int)aggregate_min_accesses); + HDassert( entry_ptr->addr == old_addr ); + HDassert( entry_ptr->type == type ); + HDassert( !(entry_ptr->is_protected) ); - HDfprintf(stdout, " aggregate max_clears / max_flushes = %d / %d\n", - (int)aggregate_max_clears, - (int)aggregate_max_flushes); + if ( entry_ptr->in_tree ) { -#endif /* H5C_COLLECT_CACHE_ENTRY_STATS */ + HDassert( cache_ptr->tree_ptr ); + search_target.addr = old_addr; - if ( display_detailed_stats ) - { + old_node_ptr = H5TB_dfind(cache_ptr->tree_ptr, + (void *)(&search_target), + NULL); - for ( i = 0; i <= cache_ptr->max_type_id; i++ ) { + HDassert( old_node_ptr != NULL ); + HDassert( old_node_ptr->key == (void *)entry_ptr ); + } + } - HDfprintf(stdout, "\n"); + H5C__SEARCH_INDEX(cache_ptr, new_addr, test_entry_ptr, FAIL) - HDfprintf(stdout, " Stats on %s:\n", - ((cache_ptr->type_name_table_ptr))[i]); + if ( test_entry_ptr != NULL ) { /* we are hosed */ + + if ( test_entry_ptr->type == type ) { - if ( ( cache_ptr->hits[i] > 0 ) || ( cache_ptr->misses[i] > 0 ) ) { + HGOTO_ERROR(H5E_CACHE, H5E_CANTRENAME, FAIL, \ + "Target already renamed & reinserted???.") - hit_rate = 100.0 * ((double)(cache_ptr->hits[i])) / - ((double)(cache_ptr->hits[i] + cache_ptr->misses[i])); - } else { - hit_rate = 0.0; - } + } else { - HDfprintf(stdout, - " hits / misses / hit_rate = %ld / %ld / %f\n", - (long)(cache_ptr->hits[i]), - (long)(cache_ptr->misses[i]), - hit_rate); + HGOTO_ERROR(H5E_CACHE, H5E_CANTRENAME, FAIL, \ + "New address already in use?.") + } + } - HDfprintf(stdout, - " clears / flushes / evictions = %ld / %ld / %ld\n", - (long)(cache_ptr->clears[i]), - (long)(cache_ptr->flushes[i]), - (long)(cache_ptr->evictions[i])); + /* If we get this far, we have work to do. Remove *entry_ptr from + * the hash table (and tree if necessary), change its address to the + * new address, and then re-insert. + * + * Update the replacement policy for a hit to avoid an eviction before + * the renamed entry is touched. Update stats for a rename. + * + * Note that we do not check the size of the cache, or evict anything. + * Since this is a simple re-name, cache size should be unaffected. + */ + H5C__DELETE_FROM_INDEX(cache_ptr, entry_ptr) - HDfprintf(stdout, - " insertions / renames = %ld / %ld\n", - (long)(cache_ptr->insertions[i]), - (long)(cache_ptr->renames[i])); + if ( old_node_ptr ) { -#if H5C_COLLECT_CACHE_ENTRY_STATS + H5C__REMOVE_ENTRY_FROM_TREE(cache_ptr, entry_ptr, old_node_ptr) + } - HDfprintf(stdout, - " entry max / min accesses = %d / %d\n", - cache_ptr->max_accesses[i], - cache_ptr->min_accesses[i]); + entry_ptr->addr = new_addr; - HDfprintf(stdout, - " entry max_clears / max_flushes = %d / %d\n", - cache_ptr->max_clears[i], - cache_ptr->max_flushes[i]); + H5C__INSERT_IN_INDEX(cache_ptr, entry_ptr, FAIL) -#endif /* H5C_COLLECT_CACHE_ENTRY_STATS */ + if ( entry_ptr->is_dirty ) { - } + H5C__INSERT_ENTRY_IN_TREE(cache_ptr, entry_ptr) } - HDfprintf(stdout, "\n"); + H5C__UPDATE_RP_FOR_RENAME(cache_ptr, entry_ptr, FAIL) -#endif /* H5C_COLLECT_CACHE_STATS */ + H5C__UPDATE_STATS_FOR_RENAME(cache_ptr, entry_ptr) done: + FUNC_LEAVE_NOAPI(ret_value) -} /* H5C_stats() */ +} /* H5C_rename_entry() */ /*------------------------------------------------------------------------- + * Function: H5C_protect * - * Function: H5C_stats__reset + * Purpose: If the target entry is not in the cache, load it. If + * necessary, attempt to evict one or more entries to keep + * the cache within its maximum size. * - * Purpose: Reset the stats fields to their initial values. + * Mark the target entry as protected, and return its address + * to the caller. The caller must call H5C_unprotect() when + * finished with the entry. * - * Return: void + * While it is protected, the entry may not be either evicted + * or flushed -- nor may it be accessed by another call to + * H5C_protect. Any attempt to do so will result in a failure. * - * Programmer: John Mainzer, 4/28/04 + * The primary_dxpl_id and secondary_dxpl_id parameters + * specify the dxpl_ids used on the first write occasioned + * by the insertion (primary_dxpl_id), and on all subsequent + * writes (secondary_dxpl_id). This is useful in the + * metadata cache, but may not be needed elsewhere. If so, + * just use the same dxpl_id for both parameters. + * + * All reads are performed with the primary_dxpl_id. + * + * Similarly, the primary_dxpl_id is passed to the + * check_write_permitted function if it is called. + * + * Return: Success: Ptr to the desired entry + * + * Failure: NULL + * + * Programmer: John Mainzer - 6/2/04 * * Modifications: * + * JRM - 7/21/04 + * Updated for the addition of the hash table. + * *------------------------------------------------------------------------- */ -void -H5C_stats__reset(H5C_t * cache_ptr) +void * +H5C_protect(H5F_t * f, + hid_t primary_dxpl_id, + hid_t secondary_dxpl_id, + H5C_t * cache_ptr, + const H5C_class_t * type, + haddr_t addr, + const void * udata1, + void * udata2) { -#if H5C_COLLECT_CACHE_STATS - int i; -#endif /* H5C_COLLECT_CACHE_STATS */ + hbool_t hit = FALSE; + void * thing = NULL; + H5C_cache_entry_t * entry_ptr; + void * ret_value; /* Return value */ + FUNC_ENTER_NOAPI(H5C_protect, NULL) + + /* check args */ HDassert( cache_ptr ); HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); + HDassert( cache_ptr->skip_file_checks || f ); + HDassert( type ); + HDassert( type->flush ); + HDassert( type->load ); + HDassert( H5F_addr_defined(addr) ); -#if H5C_COLLECT_CACHE_STATS - for ( i = 0; i <= cache_ptr->max_type_id; i++ ) - { - cache_ptr->hits[i] = 0; - cache_ptr->misses[i] = 0; - cache_ptr->insertions[i] = 0; - cache_ptr->clears[i] = 0; - cache_ptr->flushes[i] = 0; - cache_ptr->evictions[i] = 0; - cache_ptr->renames[i] = 0; - } + /* first check to see if the target is in cache */ + H5C__SEARCH_INDEX(cache_ptr, addr, entry_ptr, NULL) - cache_ptr->max_index_len = 0; - cache_ptr->max_index_size = (size_t)0; + if ( entry_ptr != NULL ) { - cache_ptr->max_pl_len = 0; - cache_ptr->max_pl_size = (size_t)0; + hit = TRUE; + thing = (void *)entry_ptr; -#if H5C_COLLECT_CACHE_ENTRY_STATS + } else { /* must try to load the entry from disk. */ - for ( i = 0; i <= cache_ptr->max_type_id; i++ ) - { - cache_ptr->max_accesses[i] = 0; - cache_ptr->min_accesses[i] = 1000000; - cache_ptr->max_clears[i] = 0; - cache_ptr->max_flushes[i] = 0; - } + hit = FALSE; + thing = H5C_load_entry(f, primary_dxpl_id, type, addr, udata1, udata2, + cache_ptr->skip_file_checks); -#endif /* H5C_COLLECT_CACHE_ENTRY_STATS */ -#endif /* H5C_COLLECT_CACHE_STATS */ + if ( thing == NULL ) { - return; + HGOTO_ERROR(H5E_CACHE, H5E_CANTLOAD, NULL, "can't load entry") + } -} /* H5C_stats__reset() */ + entry_ptr = (H5C_cache_entry_t *)thing; - -/*------------------------------------------------------------------------- - * Function: H5C_set_skip_flags - * - * Purpose: Set the values of the skip sanity check flags. - * - * This function and the skip sanity check flags were created - * for the convenience of the test bed. However it is - * possible that there may be other uses for the flags. - * - * Return: Non-negative on success/Negative on failure - * - * Programmer: John Mainzer - * 6/11/04 - * - * Modifications: - * - *------------------------------------------------------------------------- - */ + /* try to free up some space if necessay */ + if ( (cache_ptr->index_size + entry_ptr->size) > + cache_ptr->max_cache_size ) { -herr_t -H5C_set_skip_flags(H5C_t * cache_ptr, - hbool_t skip_file_checks, - hbool_t skip_dxpl_id_checks) -{ - herr_t ret_value = SUCCEED; /* Return value */ + hbool_t write_permitted = TRUE; + herr_t result; + size_t space_needed; + + if ( cache_ptr->check_write_permitted != NULL ) { + + result = (cache_ptr->check_write_permitted)(f, + primary_dxpl_id, + &write_permitted); + + if ( result < 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, NULL, \ + "Can't get write_permitted") + } + } + + HDassert( entry_ptr->size <= H5C_MAX_ENTRY_SIZE ); + + space_needed = (cache_ptr->index_size + entry_ptr->size) - + cache_ptr->max_cache_size; + + /* It would be nice to be able to do a tight sanity check on + * space_needed here, but it is hard to assign an upper bound on + * its value other than then value assigned to it. + * + * This fact springs from several features of the cache: + * + * First, it is possible for the cache to grow without + * bound as long as entries are protected and not unprotected. + * + * Second, when writes are not permitted it is also possible + * for the cache to grow without bound. + * + * Finally, we don't check to see if the cache is oversized + * at the end of an unprotect. As a result, it is possible + * to have a vastly oversized cache with no protected entries + * as long as all the protects preceed the unprotects. + * + * Since items 1 and 2 are not changing any time soon, I see + * no point in worrying about the third. + * + * In any case, I hope this explains why there is no sanity + * check on space_needed here. + */ + + result = H5C_make_space_in_cache(f, primary_dxpl_id, + secondary_dxpl_id, cache_ptr, + space_needed, write_permitted); + + if ( result < 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, NULL, \ + "H5C_make_space_in_cache failed.") + } + } + + /* Insert the entry in the hash table. It can't be dirty yet, so + * we don't even check to see if it should go in the tree. + */ + H5C__INSERT_IN_INDEX(cache_ptr, entry_ptr, NULL) - FUNC_ENTER_NOAPI(H5C_set_skip_flags, FAIL) + /* insert the entry in the data structures used by the replacement + * policy. We are just going to take it out again when we update + * the replacement policy for a protect, but this simplifies the + * code. If we do this often enough, we may want to optimize this. + */ + H5C__UPDATE_RP_FOR_INSERTION(cache_ptr, entry_ptr, NULL) + } - /* This would normally be an assert, but we need to use an HGOTO_ERROR - * call to shut up the compiler. - */ - if ( ( ! cache_ptr ) || ( cache_ptr->magic != H5C__H5C_T_MAGIC ) ) { + HDassert( entry_ptr->addr == addr ); + HDassert( entry_ptr->type == type ); - HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Bad cache_ptr") + if ( entry_ptr->is_protected ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, NULL, \ + "Target already protected?!?.") } - cache_ptr->skip_file_checks = skip_file_checks; - cache_ptr->skip_dxpl_id_checks = skip_dxpl_id_checks; + H5C__UPDATE_RP_FOR_PROTECT(cache_ptr, entry_ptr, NULL) + + entry_ptr->is_protected = TRUE; + + ret_value = thing; + + H5C__UPDATE_STATS_FOR_PROTECT(cache_ptr, entry_ptr, hit) done: + FUNC_LEAVE_NOAPI(ret_value) -} /* H5C_set_skip_flags() */ +} /* H5C_protect() */ -/*************************************************************************/ -/**************************** Private Functions: *************************/ -/*************************************************************************/ - /*------------------------------------------------------------------------- + * Function: H5C_unprotect * - * Function: H5C_flush_single_entry - * - * Purpose: Flush or clear (and evict if requested) the cache entry - * with the specified address and type. If the type is NULL, - * any unprotected entry at the specified address will be - * flushed (and possibly evicted). - * - * Attempts to flush a protected entry will result in an - * error. + * Purpose: Undo an H5C_protect() call -- specifically, mark the + * entry as unprotected, remove it from the protected list, + * and give it back to the replacement policy. * - * *first_flush_ptr should be true if only one - * flush is contemplated before the next load, or if this - * is the first of a sequence of flushes that will be - * completed before the next load. *first_flush_ptr is set - * to false if a flush actually takes place, and should be - * left false until the end of the sequence. + * The TYPE and ADDR arguments must be the same as those in + * the corresponding call to H5C_protect() and the THING + * argument must be the value returned by that call to + * H5C_protect(). * - * The primary_dxpl_id is used if *first_flush_ptr is TRUE - * on entry, and a flush actually takes place. The - * secondary_dxpl_id is used in any subsequent flush where - * *first_flush_ptr is FALSE on entry. + * The primary_dxpl_id and secondary_dxpl_id parameters + * specify the dxpl_ids used on the first write occasioned + * by the unprotect (primary_dxpl_id), and on all subsequent + * writes (secondary_dxpl_id). Since an uprotect cannot + * occasion a write at present, all this is moot for now. + * However, things change, and in any case, + * H5C_flush_single_entry() needs primary_dxpl_id and + * secondary_dxpl_id in its parameter list. * - * If the H5F_FLUSH_CLEAR_ONLY flag is set, the entry will - * be cleared and not flushed -- in the case *first_flush_ptr, - * primary_dxpl_id, and secondary_dxpl_id are all irrelevent, - * and the call can't be part of a sequence of flushes. + * The function can't cause a read either, so the dxpl_id + * parameters are moot in this case as well. * - * If the caller knows the address of the TBBT node at - * which the target entry resides, it can avoid a lookup - * by supplying that address in the tgt_node_ptr parameter. - * If this parameter is NULL, the function will do a TBBT - * search for the entry instead. + * Return: Non-negative on success/Negative on failure * - * The function does nothing silently if there is no entry - * at the supplied address, or if the entry found has the - * wrong type. + * If the deleted flag is TRUE, simply remove the target entry + * from the cache, clear it, and free it without writing it to + * disk. * - * Return: Non-negative on success/Negative on failure or if there was - * an attempt to flush a protected item. + * Return: Non-negative on success/Negative on failure * - * Programmer: John Mainzer, 5/5/04 + * Programmer: John Mainzer + * 6/2/04 * * Modifications: * + * JRM - 7/21/04 + * Updated the function for the addition of the hash table. + * In particular, we now add dirty entries to the tree if + * they aren't in the tree already. + * *------------------------------------------------------------------------- */ -static herr_t -H5C_flush_single_entry(H5F_t * f, - hid_t primary_dxpl_id, - hid_t secondary_dxpl_id, - H5C_t * cache_ptr, - const H5C_class_t * type_ptr, - haddr_t addr, - unsigned flags, - H5TB_NODE * tgt_node_ptr, - hbool_t * first_flush_ptr, - hbool_t remove_entry_from_tree_on_destroy) +herr_t +H5C_unprotect(H5F_t * f, + hid_t primary_dxpl_id, + hid_t secondary_dxpl_id, + H5C_t * cache_ptr, + const H5C_class_t * type, + haddr_t addr, + void * thing, + hbool_t deleted) { - hbool_t destroy = ( (flags & H5F_FLUSH_INVALIDATE) != 0 ); - hbool_t clear_only = ( (flags & H5F_FLUSH_CLEAR_ONLY) != 0); - herr_t ret_value = SUCCEED; /* Return value */ - herr_t status; - H5TB_NODE * node_ptr; - H5C_cache_entry_t * entry_ptr = NULL; - H5C_cache_entry_t search_target; + herr_t ret_value = SUCCEED; /* Return value */ + H5C_cache_entry_t * entry_ptr; + H5C_cache_entry_t * test_entry_ptr; - FUNC_ENTER_NOAPI_NOINIT(H5C_flush_single_entry) + FUNC_ENTER_NOAPI(H5C_unprotect, FAIL) HDassert( cache_ptr ); HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); HDassert( cache_ptr->skip_file_checks || f ); + HDassert( type ); + HDassert( type->clear ); + HDassert( type->flush ); HDassert( H5F_addr_defined(addr) ); - HDassert( first_flush_ptr ); - - /* If tgt_node_ptr is NULL, look up the target entry in the tree. - * If it doesn't exist, we are done. - */ - - if ( tgt_node_ptr == NULL ) { - - search_target.addr = addr; - node_ptr = H5TB_dfind(cache_ptr->index_tree_ptr, - (void *)&search_target, - NULL); - - } else { + HDassert( thing ); - node_ptr = tgt_node_ptr; - } + entry_ptr = (H5C_cache_entry_t *)thing; - if ( node_ptr != NULL ) { + HDassert( entry_ptr->addr == addr ); + HDassert( entry_ptr->type == type ); - entry_ptr = (H5C_cache_entry_t *)(node_ptr->data); - HDassert( entry_ptr != NULL ); - HDassert( entry_ptr->addr == addr ); - HDassert( node_ptr->data == node_ptr->key ); - } + if ( ! (entry_ptr->is_protected) ) { - if ( ( entry_ptr != NULL ) && ( 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.") + HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, \ + "Entry already unprotected??") } - if ( ( entry_ptr != NULL ) && - ( ( type_ptr == NULL ) || ( type_ptr->id == entry_ptr->type->id ) ) ) - { - /* we have work to do */ - -#ifdef H5_HAVE_PARALLEL -#ifndef NDEBUG - - /* If MPI based VFD is used, do special parallel I/O sanity checks. - * Note that we only do these sanity checks when the clear_only flag - * is not set, and the entry to be flushed is dirty. Don't bother - * otherwise as no file I/O can result. - * - * There are also cases (testing for instance) where it is convenient - * to pass in dummy dxpl_ids. Since we don't use the dxpl_ids directly, - * this isn't a problem -- but we do have to turn off sanity checks - * involving them. We use cache_ptr->skip_dxpl_id_checks to do this. - */ - if ( ( ! cache_ptr->skip_dxpl_id_checks ) && - ( ! clear_only ) && - ( entry_ptr->is_dirty ) && - ( IS_H5FD_MPI(f) ) ) { - - H5P_genplist_t *dxpl; /* Dataset transfer property list */ - H5FD_mpio_xfer_t xfer_mode; /* I/O xfer mode property value */ - - /* Get the dataset transfer property list */ - if ( NULL == (dxpl = H5I_object(primary_dxpl_id)) ) { - - HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, \ - "not a dataset creation property list") - } - - /* Get the transfer mode property */ - if( H5P_get(dxpl, H5D_XFER_IO_XFER_MODE_NAME, &xfer_mode) < 0 ) { - - HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL, \ - "can't retrieve xfer mode") - } - - /* Sanity check transfer mode */ - HDassert( xfer_mode == H5FD_MPIO_COLLECTIVE || IS_H5FD_FPHDF5(f) ); - } - -#endif /* NDEBUG */ -#endif /* H5_HAVE_PARALLEL */ - if ( clear_only ) { - H5C__UPDATE_STATS_FOR_CLEAR(cache_ptr, entry_ptr) - } else { - H5C__UPDATE_STATS_FOR_FLUSH(cache_ptr, entry_ptr) - } - - if ( destroy ) { - H5C__UPDATE_STATS_FOR_EVICTION(cache_ptr, entry_ptr) - } - - /* remove entry from tree if asked -- must do this now as the - * callback routines will free the entry if destroy is true. - */ - if ( ( destroy ) && ( remove_entry_from_tree_on_destroy ) ) { - if ( H5C_remove_entry_from_tree(cache_ptr, entry_ptr, node_ptr) - < 0 ) { - HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \ - "Can't delete entry from tree.") - } - } + H5C__UPDATE_RP_FOR_UNPROTECT(cache_ptr, entry_ptr, FAIL) - /* Update the replacement policy for the flush or eviction. - * Again, do this now so we don't have to reference freed - * memory in the destroy case. - */ + entry_ptr->is_protected = FALSE; - if ( destroy ) { /* AKA eviction */ - status = H5C_update_rp_for_eviction(cache_ptr, entry_ptr); - } else { - status = H5C_update_rp_for_flush(cache_ptr, entry_ptr); - } + /* add the entry to the tree if it is dirty, and it isn't already in + * the tree. + */ - if ( status < 0 ) { - HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \ - "Can't update replacement policy.") - } + if ( ( entry_ptr->is_dirty ) && ( ! (entry_ptr->in_tree) ) ) { - /* Clear the dirty flag only, if requested */ - if ( clear_only ) { - /* Call the callback routine to clear all dirty flags for object */ - if ( (entry_ptr->type->clear)(f, entry_ptr, destroy) < 0 ) { - HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "can't clear entry") - } - } else { + H5C__INSERT_ENTRY_IN_TREE(cache_ptr, entry_ptr) + } - /* Only block for all the processes on the first piece of metadata - */ + /* this implementation of the "deleted" option is a bit inefficient, as + * we re-insert the entry to be deleted into the replacement policy + * data structures, only to remove them again. Depending on how often + * we do this, we may want to optimize a bit. + * + * On the other hand, this implementation is reasonably clean, and + * makes good use of existing code. + * JRM - 5/19/04 + */ + if ( deleted ) { - if ( *first_flush_ptr && entry_ptr->is_dirty ) { - status = (entry_ptr->type->flush)(f, primary_dxpl_id, destroy, - entry_ptr->addr, entry_ptr); - *first_flush_ptr = FALSE; - } else { - status = (entry_ptr->type->flush)(f, secondary_dxpl_id, - destroy, entry_ptr->addr, - entry_ptr); - } + /* the following first flush flag will never be used as we are + * calling H5C_flush_single_entry with both the H5F_FLUSH_CLEAR_ONLY + * and H5F_FLUSH_INVALIDATE flags. However, it is needed for the + * function call. + */ + hbool_t dummy_first_flush = TRUE; - if ( status < 0 ) { - HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \ - "unable to flush entry") - } + /* verify that the target entry is in the cache. */ + + H5C__SEARCH_INDEX(cache_ptr, addr, test_entry_ptr, FAIL) + + if ( test_entry_ptr == NULL ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, \ + "entry not in hash table?!?.") } + else if ( test_entry_ptr != entry_ptr ) { - if ( ! destroy ) { - - HDassert( !(entry_ptr->is_dirty) ); + HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, \ + "hash table contains multiple entries for addr?!?.") + } + + if ( H5C_flush_single_entry(f, + primary_dxpl_id, + secondary_dxpl_id, + cache_ptr, + type, + addr, + (H5F_FLUSH_CLEAR_ONLY|H5F_FLUSH_INVALIDATE), + NULL, + &dummy_first_flush, + TRUE) < 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, "Can't flush.") } } + H5C__UPDATE_STATS_FOR_UNPROTECT(cache_ptr) + done: FUNC_LEAVE_NOAPI(ret_value) -} /* H5C_flush_single_entry() */ +} /* H5C_unprotect() */ /*------------------------------------------------------------------------- + * Function: H5C_stats * - * Function: H5C_insert_entry_in_tree - * - * Purpose: Insert the specified instance of H5C_cache_entry_t from the - * index tree in the specified instance of H5C_t. Update - * the associated length and size fields. + * Purpose: Prints statistics about the cache. * - * Return: Non-negative on success/Negative on failure. + * Return: Non-negative on success/Negative on failure * - * Programmer: John Mainzer, 5/10/04 + * Programmer: John Mainzer + * 6/2/04 * * Modifications: * + * JRM -- 7/21/04 + * Updated function for the addition of the hash table. + * *------------------------------------------------------------------------- */ -static herr_t -H5C_insert_entry_in_tree(H5C_t * cache_ptr, - H5C_cache_entry_t * entry_ptr) +herr_t +H5C_stats(H5C_t * cache_ptr, + const char * cache_name, + hbool_t +#if !H5C_COLLECT_CACHE_STATS + UNUSED +#endif /* H5C_COLLECT_CACHE_STATS */ + display_detailed_stats) { - herr_t ret_value = SUCCEED; /* Return value */ - H5TB_NODE * node_ptr = NULL; + herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_NOAPI_NOINIT(H5C_insert_entry_in_tree) +#if H5C_COLLECT_CACHE_STATS + int i; + int64_t total_hits = 0; + int64_t total_misses = 0; + int64_t total_insertions = 0; + int64_t total_clears = 0; + int64_t total_flushes = 0; + int64_t total_evictions = 0; + int64_t total_renames = 0; + int32_t aggregate_max_accesses = 0; + int32_t aggregate_min_accesses = 1000000; + int32_t aggregate_max_clears = 0; + int32_t aggregate_max_flushes = 0; + size_t aggregate_max_size = 0; + double hit_rate; + double average_successful_search_depth = 0.0; + double average_failed_search_depth = 0.0; +#endif /* H5C_COLLECT_CACHE_STATS */ - HDassert( cache_ptr ); - HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); - HDassert( entry_ptr ); - HDassert( entry_ptr->size > 0 ); - HDassert( H5F_addr_defined(entry_ptr->addr) ); + FUNC_ENTER_NOAPI(H5C_stats, FAIL) - /* Don't bother to check if the entry is already in the tree -- if it - * is, H5TB_dins() will fail. + /* This would normally be an assert, but we need to use an HGOTO_ERROR + * call to shut up the compiler. */ - node_ptr = H5TB_dins(cache_ptr->index_tree_ptr, (void *)entry_ptr, NULL); + if ( ( ! cache_ptr ) || + ( cache_ptr->magic != H5C__H5C_T_MAGIC ) || + ( !cache_name ) ) { + + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Bad cache_ptr or cache_name") + } - if ( node_ptr == NULL ) { +#if H5C_COLLECT_CACHE_STATS - HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Can't insert entry in tree") + for ( i = 0; i <= cache_ptr->max_type_id; i++ ) { + total_hits += cache_ptr->hits[i]; + total_misses += cache_ptr->misses[i]; + total_insertions += cache_ptr->insertions[i]; + total_clears += cache_ptr->clears[i]; + total_flushes += cache_ptr->flushes[i]; + total_evictions += cache_ptr->evictions[i]; + total_renames += cache_ptr->renames[i]; +#if H5C_COLLECT_CACHE_ENTRY_STATS + if ( aggregate_max_accesses < cache_ptr->max_accesses[i] ) + aggregate_max_accesses = cache_ptr->max_accesses[i]; + if ( aggregate_min_accesses > aggregate_max_accesses ) + aggregate_min_accesses = aggregate_max_accesses; + if ( aggregate_min_accesses > cache_ptr->min_accesses[i] ) + aggregate_min_accesses = cache_ptr->min_accesses[i]; + if ( aggregate_max_clears < cache_ptr->max_clears[i] ) + aggregate_max_clears = cache_ptr->max_clears[i]; + if ( aggregate_max_flushes < cache_ptr->max_flushes[i] ) + aggregate_max_flushes = cache_ptr->max_flushes[i]; + if ( aggregate_max_size < cache_ptr->max_size[i] ) + aggregate_max_size = cache_ptr->max_size[i]; +#endif /* H5C_COLLECT_CACHE_ENTRY_STATS */ } - cache_ptr->index_len++; - cache_ptr->index_size += entry_ptr->size; - HDassert( cache_ptr->index_len > 0 ); - HDassert( cache_ptr->index_size > 0 ); + if ( ( total_hits > 0 ) || ( total_misses > 0 ) ) { -done: + hit_rate = 100.0 * ((double)(total_hits)) / + ((double)(total_hits + total_misses)); + } else { + hit_rate = 0.0; + } - FUNC_LEAVE_NOAPI(ret_value) + if ( cache_ptr->successful_ht_searches > 0 ) { -} /* H5C_insert_entry_in_tree */ + average_successful_search_depth = + ((double)(cache_ptr->total_successful_ht_search_depth)) / + ((double)(cache_ptr->successful_ht_searches)); + } - -/*------------------------------------------------------------------------- - * - * Function: H5C_load_entry - * - * Purpose: Attempt to load the entry at the specified disk address - * and with the specified type into memory. If successful. - * return the in memory address of the entry. Return NULL - * on failure. - * - * Note that this function simply loads the entry into - * core. It does not insert it into the cache. - * - * Return: Non-NULL on success / NULL on failure. - * - * Programmer: John Mainzer, 5/18/04 - * - * Modifications: - * - *------------------------------------------------------------------------- - */ + if ( cache_ptr->failed_ht_searches > 0 ) { -static void * -H5C_load_entry(H5F_t * f, - hid_t dxpl_id, - const H5C_class_t * type, - haddr_t addr, - const void * udata1, - void * udata2, - hbool_t skip_file_checks) -{ - void * thing = NULL; - void * ret_value = NULL; - H5C_cache_entry_t * entry_ptr = NULL; + average_failed_search_depth = + ((double)(cache_ptr->total_failed_ht_search_depth)) / + ((double)(cache_ptr->failed_ht_searches)); + } + - FUNC_ENTER_NOAPI_NOINIT(H5C_load_entry) + HDfprintf(stdout, "\nH5C: cache statistics for %s\n", + cache_name); - HDassert( skip_file_checks || f ); - HDassert( type ); - HDassert( type->load ); - HDassert( type->size ); - HDassert( H5F_addr_defined(addr) ); + HDfprintf(stdout, "\n"); - if ( NULL == (thing = (type->load)(f, dxpl_id, addr, udata1, udata2)) ) { + HDfprintf(stdout, + " hash table insertion / deletions = %ld / %ld\n", + (long)(cache_ptr->total_ht_insertions), + (long)(cache_ptr->total_ht_deletions)); - HGOTO_ERROR(H5E_CACHE, H5E_CANTLOAD, NULL, "unable to load entry") + HDfprintf(stdout, + " HT successful / failed searches = %ld / %ld\n", + (long)(cache_ptr->successful_ht_searches), + (long)(cache_ptr->failed_ht_searches)); - } + HDfprintf(stdout, + " Av. HT suc / failed search depth = %f / %f\n", + average_successful_search_depth, + average_failed_search_depth); - entry_ptr = (H5C_cache_entry_t *)thing; + HDfprintf(stdout, + " current (max) index size / length = %ld (%ld) / %ld (%ld)\n", + (long)(cache_ptr->index_size), + (long)(cache_ptr->max_index_size), + (long)(cache_ptr->index_len), + (long)(cache_ptr->max_index_len)); + + HDfprintf(stdout, + " current (max) tree size / length = %ld (%ld) / %ld (%ld)\n", + (long)(cache_ptr->tree_size), + (long)(cache_ptr->max_tree_size), + (long)(cache_ptr->tree_len), + (long)(cache_ptr->max_tree_len)); + + HDfprintf(stdout, + " current (max) PL size / length = %ld (%ld) / %ld (%ld)\n", + (long)(cache_ptr->pl_size), + (long)(cache_ptr->max_pl_size), + (long)(cache_ptr->pl_len), + (long)(cache_ptr->max_pl_len)); + + HDfprintf(stdout, + " current LRU list size / length = %ld / %ld\n", + (long)(cache_ptr->LRU_list_size), + (long)(cache_ptr->LRU_list_len)); + + HDfprintf(stdout, + " current clean LRU size / length = %ld / %ld\n", + (long)(cache_ptr->cLRU_list_size), + (long)(cache_ptr->cLRU_list_len)); + + HDfprintf(stdout, + " current dirty LRU size / length = %ld / %ld\n", + (long)(cache_ptr->dLRU_list_size), + (long)(cache_ptr->dLRU_list_len)); + + HDfprintf(stdout, + " Total hits / misses / hit_rate = %ld / %ld / %f\n", + (long)total_hits, + (long)total_misses, + hit_rate); + + HDfprintf(stdout, + " Total clears / flushes / evictions = %ld / %ld / %ld\n", + (long)total_clears, + (long)total_flushes, + (long)total_evictions); + + HDfprintf(stdout, " Total insertions / renames = %ld / %ld\n", + (long)total_insertions, + (long)total_renames); + +#if H5C_COLLECT_CACHE_ENTRY_STATS + + HDfprintf(stdout, " aggregate max / min accesses = %d / %d\n", + (int)aggregate_max_accesses, + (int)aggregate_min_accesses); + + HDfprintf(stdout, " aggregate max_clears / max_flushes = %d / %d\n", + (int)aggregate_max_clears, + (int)aggregate_max_flushes); + + HDfprintf(stdout, " aggregate max_size = %d\n", + (int)aggregate_max_size); + + +#endif /* H5C_COLLECT_CACHE_ENTRY_STATS */ + + if ( display_detailed_stats ) + { + + for ( i = 0; i <= cache_ptr->max_type_id; i++ ) { + + HDfprintf(stdout, "\n"); + + HDfprintf(stdout, " Stats on %s:\n", + ((cache_ptr->type_name_table_ptr))[i]); + + if ( ( cache_ptr->hits[i] > 0 ) || ( cache_ptr->misses[i] > 0 ) ) { + + hit_rate = 100.0 * ((double)(cache_ptr->hits[i])) / + ((double)(cache_ptr->hits[i] + cache_ptr->misses[i])); + } else { + hit_rate = 0.0; + } + + HDfprintf(stdout, + " hits / misses / hit_rate = %ld / %ld / %f\n", + (long)(cache_ptr->hits[i]), + (long)(cache_ptr->misses[i]), + hit_rate); + + HDfprintf(stdout, + " clears / flushes / evictions = %ld / %ld / %ld\n", + (long)(cache_ptr->clears[i]), + (long)(cache_ptr->flushes[i]), + (long)(cache_ptr->evictions[i])); + + HDfprintf(stdout, + " insertions / renames = %ld / %ld\n", + (long)(cache_ptr->insertions[i]), + (long)(cache_ptr->renames[i])); + +#if H5C_COLLECT_CACHE_ENTRY_STATS - HDassert( entry_ptr->is_dirty == FALSE ); + HDfprintf(stdout, + " entry max / min accesses = %d / %d\n", + cache_ptr->max_accesses[i], + cache_ptr->min_accesses[i]); - entry_ptr->addr = addr; - entry_ptr->type = type; - entry_ptr->is_protected = FALSE; + HDfprintf(stdout, + " entry max_clears / max_flushes = %d / %d\n", + cache_ptr->max_clears[i], + cache_ptr->max_flushes[i]); - if ( (type->size)(f, thing, &(entry_ptr->size)) < 0 ) { + HDfprintf(stdout, + " entry max_size = %d\n", + (int)(cache_ptr->max_size[i])); - HGOTO_ERROR(H5E_RESOURCE, H5E_CANTGETSIZE, NULL, \ - "Can't get size of thing") - } - HDassert( entry_ptr->size < H5C_MAX_ENTRY_SIZE ); +#endif /* H5C_COLLECT_CACHE_ENTRY_STATS */ - entry_ptr->next = NULL; - entry_ptr->prev = NULL; - entry_ptr->aux_next = NULL; - entry_ptr->aux_prev = NULL; + } + } - H5C__RESET_CACHE_ENTRY_STATS(entry_ptr); + HDfprintf(stdout, "\n"); - ret_value = thing; +#endif /* H5C_COLLECT_CACHE_STATS */ done: - FUNC_LEAVE_NOAPI(ret_value) -} /* H5C_load_entry() */ +} /* H5C_stats() */ /*------------------------------------------------------------------------- * - * Function: H5C_make_space_in_cache - * - * Purpose: Attempt to evict cache entries until the index_size - * is at least needed_space below max_cache_size. - * - * In passing, also attempt to bring cLRU_list_size to a - * value greater than min_clean_size. - * - * Depending on circumstances, both of these goals may - * be impossible, as in parallel mode, we must avoid generating - * a write as part of a read (to avoid deadlock in collective - * I/O), and in all cases, it is possible (though hopefully - * highly unlikely) that the protected list may exceed the - * maximum size of the cache. - * - * Thus the function simply does its best, returning success - * unless an error is encountered. - * - * The primary_dxpl_id and secondary_dxpl_id parameters - * specify the dxpl_ids used on the first write occasioned - * by the call (primary_dxpl_id), and on all subsequent - * writes (secondary_dxpl_id). This is useful in the metadata - * cache, but may not be needed elsewhere. If so, just use the - * same dxpl_id for both parameters. + * Function: H5C_stats__reset * - * Observe that this function cannot occasion a read. + * Purpose: Reset the stats fields to their initial values. * - * Return: Non-negative on success/Negative on failure. + * Return: void * - * Programmer: John Mainzer, 5/14/04 + * Programmer: John Mainzer, 4/28/04 * * Modifications: * + * JRM - 7/21/04 + * Updated for hash table related statistics. + * *------------------------------------------------------------------------- */ -static herr_t -H5C_make_space_in_cache(H5F_t * f, - hid_t primary_dxpl_id, - hid_t secondary_dxpl_id, - H5C_t * cache_ptr, - size_t space_needed, - hbool_t write_permitted) +void +H5C_stats__reset(H5C_t * cache_ptr) { - hbool_t first_flush = TRUE; - herr_t ret_value = SUCCEED; /* Return value */ - herr_t result; - int32_t entries_examined = 0; - int32_t initial_list_len; - H5C_cache_entry_t * entry_ptr; - H5C_cache_entry_t * prev_ptr; - - FUNC_ENTER_NOAPI_NOINIT(H5C_make_space_in_cache) +#if H5C_COLLECT_CACHE_STATS + int i; +#endif /* H5C_COLLECT_CACHE_STATS */ HDassert( cache_ptr ); HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); - if ( write_permitted ) { - - initial_list_len = cache_ptr->LRU_list_len; - entry_ptr = cache_ptr->LRU_tail_ptr; - - while ( ( (cache_ptr->index_size + space_needed) - > - cache_ptr->max_cache_size - ) - && - ( entries_examined <= (2 * initial_list_len) ) - && - ( entry_ptr != NULL ) - ) - { - HDassert( ! (entry_ptr->is_protected) ); - - prev_ptr = entry_ptr->prev; - - if ( entry_ptr->is_dirty ) { - - result = H5C_flush_single_entry(f, - primary_dxpl_id, - secondary_dxpl_id, - cache_ptr, - entry_ptr->type, - entry_ptr->addr, - (unsigned)0, - NULL, - &first_flush, - FALSE); - } else { - - result = H5C_flush_single_entry(f, - primary_dxpl_id, - secondary_dxpl_id, - cache_ptr, - entry_ptr->type, - entry_ptr->addr, - H5F_FLUSH_INVALIDATE, - NULL, - &first_flush, - TRUE); - } - - if ( result < 0 ) { - - HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \ - "unable to flush entry") - } - - entry_ptr = prev_ptr; - } - - initial_list_len = cache_ptr->dLRU_list_len; - entry_ptr = cache_ptr->dLRU_tail_ptr; - - while ( ( cache_ptr->cLRU_list_size < cache_ptr->min_clean_size ) && - ( entries_examined <= initial_list_len ) && - ( entry_ptr != NULL ) - ) - { - HDassert( ! (entry_ptr->is_protected) ); - HDassert( entry_ptr->is_dirty ); - - prev_ptr = entry_ptr->aux_prev; - - result = H5C_flush_single_entry(f, - primary_dxpl_id, - secondary_dxpl_id, - cache_ptr, - entry_ptr->type, - entry_ptr->addr, - (unsigned)0, - NULL, - &first_flush, - FALSE); - - if ( result < 0 ) { - - HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \ - "unable to flush entry") - } - - entry_ptr = prev_ptr; - } - } else { - - initial_list_len = cache_ptr->cLRU_list_len; - entry_ptr = cache_ptr->cLRU_tail_ptr; +#if H5C_COLLECT_CACHE_STATS + for ( i = 0; i <= cache_ptr->max_type_id; i++ ) + { + cache_ptr->hits[i] = 0; + cache_ptr->misses[i] = 0; + cache_ptr->insertions[i] = 0; + cache_ptr->clears[i] = 0; + cache_ptr->flushes[i] = 0; + cache_ptr->evictions[i] = 0; + cache_ptr->renames[i] = 0; + } - while ( ( (cache_ptr->index_size + space_needed) - > - cache_ptr->max_cache_size - ) - && - ( entries_examined <= initial_list_len ) - && - ( entry_ptr != NULL ) - ) - { - HDassert( ! (entry_ptr->is_protected) ); - HDassert( ! (entry_ptr->is_dirty) ); + cache_ptr->total_ht_insertions = 0; + cache_ptr->total_ht_deletions = 0; + cache_ptr->successful_ht_searches = 0; + cache_ptr->total_successful_ht_search_depth = 0; + cache_ptr->failed_ht_searches = 0; + cache_ptr->total_failed_ht_search_depth = 0; - prev_ptr = entry_ptr->aux_prev; + cache_ptr->max_index_len = 0; + cache_ptr->max_index_size = (size_t)0; - result = H5C_flush_single_entry(f, - primary_dxpl_id, - secondary_dxpl_id, - cache_ptr, - entry_ptr->type, - entry_ptr->addr, - H5F_FLUSH_INVALIDATE, - NULL, - &first_flush, - TRUE); + cache_ptr->max_tree_len = 0; + cache_ptr->max_tree_size = (size_t)0; - if ( result < 0 ) { + cache_ptr->max_pl_len = 0; + cache_ptr->max_pl_size = (size_t)0; - HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \ - "unable to flush entry") - } +#if H5C_COLLECT_CACHE_ENTRY_STATS - entry_ptr = prev_ptr; - } + for ( i = 0; i <= cache_ptr->max_type_id; i++ ) + { + cache_ptr->max_accesses[i] = 0; + cache_ptr->min_accesses[i] = 1000000; + cache_ptr->max_clears[i] = 0; + cache_ptr->max_flushes[i] = 0; + cache_ptr->max_size[i] = (size_t)0; } -done: +#endif /* H5C_COLLECT_CACHE_ENTRY_STATS */ +#endif /* H5C_COLLECT_CACHE_STATS */ - FUNC_LEAVE_NOAPI(ret_value) + return; -} /* H5C_make_space_in_cache() */ +} /* H5C_stats__reset() */ /*------------------------------------------------------------------------- + * Function: H5C_set_skip_flags * - * Function: H5C_remove_entry_from_tree - * - * Purpose: Remove the specified instance of H5C_cache_entry_t from the - * index tree in the specified instance of H5C_t. Update - * the associated length and size fields. + * Purpose: Set the values of the skip sanity check flags. + * + * This function and the skip sanity check flags were created + * for the convenience of the test bed. However it is + * possible that there may be other uses for the flags. * - * Return: Non-negative on success/Negative on failure. + * Return: Non-negative on success/Negative on failure * - * Programmer: John Mainzer, 5/10/04 + * Programmer: John Mainzer + * 6/11/04 * * Modifications: * *------------------------------------------------------------------------- */ -static herr_t -H5C_remove_entry_from_tree(H5C_t * cache_ptr, - H5C_cache_entry_t * entry_ptr, - H5TB_NODE * node_ptr) +herr_t +H5C_set_skip_flags(H5C_t * cache_ptr, + hbool_t skip_file_checks, + hbool_t skip_dxpl_id_checks) { - herr_t ret_value = SUCCEED; /* Return value */ - - FUNC_ENTER_NOAPI_NOINIT(H5C_remove_entry_from_tree) - - HDassert( cache_ptr ); - HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); - HDassert( entry_ptr ); - HDassert( !entry_ptr->is_protected); - HDassert( node_ptr ); - HDassert( node_ptr->data == entry_ptr ); - HDassert( entry_ptr->size > 0 ); - - if ( H5TB_rem(&(cache_ptr->index_tree_ptr->root), node_ptr, NULL) - != entry_ptr ) { - HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \ - "Can't delete entry from tree.") - } else { - HDassert( cache_ptr->index_len > 0 ); + herr_t ret_value = SUCCEED; /* Return value */ - cache_ptr->index_len--; + FUNC_ENTER_NOAPI(H5C_set_skip_flags, FAIL) - HDassert( cache_ptr->index_size >= entry_ptr->size ); + /* This would normally be an assert, but we need to use an HGOTO_ERROR + * call to shut up the compiler. + */ + if ( ( ! cache_ptr ) || ( cache_ptr->magic != H5C__H5C_T_MAGIC ) ) { - cache_ptr->index_size -= entry_ptr->size; + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Bad cache_ptr") } + cache_ptr->skip_file_checks = skip_file_checks; + cache_ptr->skip_dxpl_id_checks = skip_dxpl_id_checks; + done: - FUNC_LEAVE_NOAPI(ret_value) -} /* H5C_remove_entry_from_tree */ +} /* H5C_set_skip_flags() */ +/*************************************************************************/ +/**************************** Private Functions: *************************/ +/*************************************************************************/ + /*------------------------------------------------------------------------- * - * Function: H5C_update_rp_for_eviction + * Function: H5C_flush_single_entry * - * Purpose: Update the replacement policy data structures for an - * eviction of the specified cache entry. + * Purpose: Flush or clear (and evict if requested) the cache entry + * with the specified address and type. If the type is NULL, + * any unprotected entry at the specified address will be + * flushed (and possibly evicted). * - * At present, we only support the modified LRU policy, so - * this function deals with that case unconditionally. If - * we ever support other replacement policies, the function - * should switch on the current policy and act accordingly. + * Attempts to flush a protected entry will result in an + * error. * - * Return: Non-negative on success/Negative on failure. + * *first_flush_ptr should be true if only one + * flush is contemplated before the next load, or if this + * is the first of a sequence of flushes that will be + * completed before the next load. *first_flush_ptr is set + * to false if a flush actually takes place, and should be + * left false until the end of the sequence. * - * Programmer: John Mainzer, 5/10/04 + * The primary_dxpl_id is used if *first_flush_ptr is TRUE + * on entry, and a flush actually takes place. The + * secondary_dxpl_id is used in any subsequent flush where + * *first_flush_ptr is FALSE on entry. + * + * If the H5F_FLUSH_CLEAR_ONLY flag is set, the entry will + * be cleared and not flushed -- in the case *first_flush_ptr, + * primary_dxpl_id, and secondary_dxpl_id are all irrelevent, + * and the call can't be part of a sequence of flushes. + * + * If the caller knows the address of the TBBT node at + * which the target entry resides, it can avoid a lookup + * by supplying that address in the tgt_node_ptr parameter. + * If this parameter is NULL, the function will do a TBBT + * search for the entry instead. + * + * The function does nothing silently if there is no entry + * at the supplied address, or if the entry found has the + * wrong type. + * + * Return: Non-negative on success/Negative on failure or if there was + * an attempt to flush a protected item. + * + * Programmer: John Mainzer, 5/5/04 * * Modifications: * + * JRM -- 7/21/04 + * Updated function for the addition of the hash table. + * *------------------------------------------------------------------------- */ - static herr_t -H5C_update_rp_for_eviction(H5C_t * cache_ptr, - H5C_cache_entry_t * entry_ptr) +H5C_flush_single_entry(H5F_t * f, + hid_t primary_dxpl_id, + hid_t secondary_dxpl_id, + H5C_t * cache_ptr, + const H5C_class_t * type_ptr, + haddr_t addr, + unsigned flags, + H5TB_NODE * tgt_node_ptr, + hbool_t * first_flush_ptr, + hbool_t del_entry_from_tree_on_destroy) { + hbool_t destroy = ( (flags & H5F_FLUSH_INVALIDATE) != 0 ); + hbool_t clear_only = ( (flags & H5F_FLUSH_CLEAR_ONLY) != 0); herr_t ret_value = SUCCEED; /* Return value */ + herr_t status; + H5TB_NODE * node_ptr; + H5C_cache_entry_t * entry_ptr = NULL; + H5C_cache_entry_t search_target; - FUNC_ENTER_NOAPI_NOINIT(H5C_update_rp_for_eviction) + FUNC_ENTER_NOAPI_NOINIT(H5C_flush_single_entry) HDassert( cache_ptr ); HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); - HDassert( entry_ptr ); - HDassert( !entry_ptr->is_protected); - HDassert( entry_ptr->size > 0 ); + HDassert( cache_ptr->skip_file_checks || f ); + HDassert( H5F_addr_defined(addr) ); + HDassert( first_flush_ptr ); - /* modified LRU specific code */ + /* attempt to find the target entry in the hash table */ + H5C__SEARCH_INDEX(cache_ptr, addr, entry_ptr, FAIL) - /* remove the entry from the LRU list. */ + /* If tgt_node_ptr is NULL, look up the target entry in the tree. */ - H5C__DLL_REMOVE(entry_ptr, cache_ptr->LRU_head_ptr, \ - cache_ptr->LRU_tail_ptr, cache_ptr->LRU_list_len, \ - cache_ptr->LRU_list_size) + if ( tgt_node_ptr == NULL ) { - /* If the entry is clean when it is evicted, it should be on the - * clean LRU list, if it was dirty, it should be on the dirty LRU list. - * Remove it from the appropriate list according to the value of the - * dirty flag. - */ + HDassert( cache_ptr->tree_ptr ); + search_target.addr = addr; - if ( entry_ptr->is_dirty ) { + node_ptr = H5TB_dfind(cache_ptr->tree_ptr, + (void *)&search_target, + NULL); - H5C__AUX_DLL_REMOVE(entry_ptr, cache_ptr->dLRU_head_ptr, \ - cache_ptr->dLRU_tail_ptr, \ - cache_ptr->dLRU_list_len, \ - cache_ptr->dLRU_list_size) } else { - H5C__AUX_DLL_REMOVE(entry_ptr, cache_ptr->cLRU_head_ptr, \ - cache_ptr->cLRU_tail_ptr, \ - cache_ptr->cLRU_list_len, \ - cache_ptr->cLRU_list_size) + + node_ptr = tgt_node_ptr; } #if H5C_DO_SANITY_CHECKS -done: + if ( node_ptr != NULL ) { + + if ( ( entry_ptr == NULL ) || + ( entry_ptr != (H5C_cache_entry_t *)(node_ptr->data) ) || + ( ! (entry_ptr->in_tree) ) || + ( entry_ptr->addr != addr ) || + ( node_ptr->data != node_ptr->key ) ) { + + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \ + "Hash table and tree out of sync.") + } + } else if ( entry_ptr != NULL ) { + + if ( ( entry_ptr->in_tree ) || + ( entry_ptr->is_dirty ) || + ( entry_ptr->addr != addr ) ) { + + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \ + "entry failed sanity checks.") + } + } #endif /* H5C_DO_SANITY_CHECKS */ - FUNC_LEAVE_NOAPI(ret_value) + if ( ( entry_ptr != NULL ) && ( 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.") + } -} /* H5C_update_rp_for_eviction() */ + if ( ( entry_ptr != NULL ) && + ( ( type_ptr == NULL ) || ( type_ptr->id == entry_ptr->type->id ) ) ) + { + /* we have work to do */ - -/*------------------------------------------------------------------------- - * - * Function: H5C_update_rp_for_flush - * - * Purpose: Update the replacement policy data structures for a flush - * of the specified cache entry. - * - * At present, we only support the modified LRU policy, so - * this function deals with that case unconditionally. If - * we ever support other replacement policies, the function - * should switch on the current policy and act accordingly. - * - * Return: Non-negative on success/Negative on failure. - * - * Programmer: John Mainzer, 5/6/04 - * - * Modifications: - * - *------------------------------------------------------------------------- - */ +#ifdef H5_HAVE_PARALLEL +#ifndef NDEBUG -static herr_t -H5C_update_rp_for_flush(H5C_t * cache_ptr, - H5C_cache_entry_t * entry_ptr) -{ - herr_t ret_value = SUCCEED; /* Return value */ + /* If MPI based VFD is used, do special parallel I/O sanity checks. + * Note that we only do these sanity checks when the clear_only flag + * is not set, and the entry to be flushed is dirty. Don't bother + * otherwise as no file I/O can result. + * + * There are also cases (testing for instance) where it is convenient + * to pass in dummy dxpl_ids. Since we don't use the dxpl_ids directly, + * this isn't a problem -- but we do have to turn off sanity checks + * involving them. We use cache_ptr->skip_dxpl_id_checks to do this. + */ + if ( ( ! cache_ptr->skip_dxpl_id_checks ) && + ( ! clear_only ) && + ( entry_ptr->is_dirty ) && + ( IS_H5FD_MPI(f) ) ) { - FUNC_ENTER_NOAPI_NOINIT(H5C_update_rp_for_flush) + H5P_genplist_t *dxpl; /* Dataset transfer property list */ + H5FD_mpio_xfer_t xfer_mode; /* I/O xfer mode property value */ - HDassert( cache_ptr ); - HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); - HDassert( entry_ptr ); - HDassert( !entry_ptr->is_protected); - HDassert( entry_ptr->size > 0 ); + /* Get the dataset transfer property list */ + if ( NULL == (dxpl = H5I_object(primary_dxpl_id)) ) { - /* modified LRU specific code */ + HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, \ + "not a dataset creation property list") + } - /* remove the entry from the LRU list, and re-insert it at the head. */ + /* Get the transfer mode property */ + if( H5P_get(dxpl, H5D_XFER_IO_XFER_MODE_NAME, &xfer_mode) < 0 ) { - H5C__DLL_REMOVE(entry_ptr, cache_ptr->LRU_head_ptr, \ - cache_ptr->LRU_tail_ptr, cache_ptr->LRU_list_len, \ - cache_ptr->LRU_list_size) + HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL, \ + "can't retrieve xfer mode") + } - H5C__DLL_PREPEND(entry_ptr, cache_ptr->LRU_head_ptr, \ - cache_ptr->LRU_tail_ptr, cache_ptr->LRU_list_len, \ - cache_ptr->LRU_list_size) + /* Sanity check transfer mode */ + HDassert( xfer_mode == H5FD_MPIO_COLLECTIVE || IS_H5FD_FPHDF5(f) ); + } - /* since the entry is being flushed or cleared, one would think that it - * must be dirty -- but that need not be the case. Use the dirty flag - * to infer whether the entry is on the clean or dirty LRU list, and - * remove it. Then insert it at the head of the clean LRU list. - * - * The function presumes that a dirty entry will be either cleared or - * flushed shortly, so it is OK if we put a dirty entry on the clean - * LRU list. - */ +#endif /* NDEBUG */ +#endif /* H5_HAVE_PARALLEL */ - if ( entry_ptr->is_dirty ) { - H5C__AUX_DLL_REMOVE(entry_ptr, cache_ptr->dLRU_head_ptr, \ - cache_ptr->dLRU_tail_ptr, \ - cache_ptr->dLRU_list_len, \ - cache_ptr->dLRU_list_size) - } else { - H5C__AUX_DLL_REMOVE(entry_ptr, cache_ptr->cLRU_head_ptr, \ - cache_ptr->cLRU_tail_ptr, \ - cache_ptr->cLRU_list_len, \ - cache_ptr->cLRU_list_size) - } + if ( clear_only ) { + H5C__UPDATE_STATS_FOR_CLEAR(cache_ptr, entry_ptr) + } else { + H5C__UPDATE_STATS_FOR_FLUSH(cache_ptr, entry_ptr) + } - H5C__AUX_DLL_PREPEND(entry_ptr, cache_ptr->cLRU_head_ptr, \ - cache_ptr->cLRU_tail_ptr, cache_ptr->cLRU_list_len, \ - cache_ptr->cLRU_list_size) + if ( destroy ) { + H5C__UPDATE_STATS_FOR_EVICTION(cache_ptr, entry_ptr) + } -#if H5C_DO_SANITY_CHECKS -done: -#endif /* H5C_DO_SANITY_CHECKS */ + /* Always remove the entry from the hash table on a destroy. On a + * flush with destroy, it is cheaper to discard the tree all at once + * rather than remove the entries one by one, so we only delete from + * the tree if requested. + * + * We must do deletions now as the callback routines will free the + * entry if destroy is true. + */ + if ( destroy ) { - FUNC_LEAVE_NOAPI(ret_value) + H5C__DELETE_FROM_INDEX(cache_ptr, entry_ptr) -} /* H5C_update_rp_for_flush() */ + if ( ( node_ptr ) && ( del_entry_from_tree_on_destroy ) ) { - -/*------------------------------------------------------------------------- - * - * Function: H5C_update_rp_for_insertion - * - * Purpose: Update the replacement policy data structures for an - * insertion of the specified cache entry. - * - * At present, we only support the modified LRU policy, so - * this function deals with that case unconditionally. If - * we ever support other replacement policies, the function - * should switch on the current policy and act accordingly. - * - * Return: Non-negative on success/Negative on failure. - * - * Programmer: John Mainzer, 5/17/04 - * - * Modifications: - * - *------------------------------------------------------------------------- - */ + H5C__REMOVE_ENTRY_FROM_TREE(cache_ptr, entry_ptr, node_ptr) + } + } -static herr_t -H5C_update_rp_for_insertion(H5C_t * cache_ptr, - H5C_cache_entry_t * entry_ptr) -{ - herr_t ret_value = SUCCEED; /* Return value */ + /* Update the replacement policy for the flush or eviction. + * Again, do this now so we don't have to reference freed + * memory in the destroy case. + */ + if ( destroy ) { /* AKA eviction */ - FUNC_ENTER_NOAPI_NOINIT(H5C_update_rp_for_insertion) + H5C__UPDATE_RP_FOR_EVICTION(cache_ptr, entry_ptr, FAIL) - HDassert( cache_ptr ); - HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); - HDassert( entry_ptr ); - HDassert( !entry_ptr->is_protected); - HDassert( entry_ptr->size > 0 ); + } else { - /* modified LRU specific code */ + H5C__UPDATE_RP_FOR_FLUSH(cache_ptr, entry_ptr, FAIL) + } - /* insert the entry at the head of the LRU list. */ + /* Clear the dirty flag only, if requested */ + if ( clear_only ) { + /* Call the callback routine to clear all dirty flags for object */ + if ( (entry_ptr->type->clear)(f, entry_ptr, destroy) < 0 ) { + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "can't clear entry") + } + } else { - H5C__DLL_PREPEND(entry_ptr, cache_ptr->LRU_head_ptr, \ - cache_ptr->LRU_tail_ptr, cache_ptr->LRU_list_len, \ - cache_ptr->LRU_list_size) + /* Only block for all the processes on the first piece of metadata + */ - /* insert the entry at the head of the clean or dirty LRU list as - * appropriate. - */ + if ( *first_flush_ptr && entry_ptr->is_dirty ) { + status = (entry_ptr->type->flush)(f, primary_dxpl_id, destroy, + entry_ptr->addr, entry_ptr); + *first_flush_ptr = FALSE; + } else { + status = (entry_ptr->type->flush)(f, secondary_dxpl_id, + destroy, entry_ptr->addr, + entry_ptr); + } - if ( entry_ptr->is_dirty ) { - H5C__AUX_DLL_PREPEND(entry_ptr, cache_ptr->dLRU_head_ptr, \ - cache_ptr->dLRU_tail_ptr, \ - cache_ptr->dLRU_list_len, \ - cache_ptr->dLRU_list_size) - } else { - H5C__AUX_DLL_PREPEND(entry_ptr, cache_ptr->cLRU_head_ptr, \ - cache_ptr->cLRU_tail_ptr, \ - cache_ptr->cLRU_list_len, \ - cache_ptr->cLRU_list_size) + if ( status < 0 ) { + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \ + "unable to flush entry") + } + } + + if ( ! destroy ) { + + HDassert( !(entry_ptr->is_dirty) ); + } } -#if H5C_DO_SANITY_CHECKS done: -#endif /* H5C_DO_SANITY_CHECKS */ FUNC_LEAVE_NOAPI(ret_value) -} /* H5C_update_rp_for_insertion() */ +} /* H5C_flush_single_entry() */ /*------------------------------------------------------------------------- * - * Function: H5C_update_rp_for_protect - * - * Purpose: Update the replacement policy data structures for a - * protect of the specified cache entry. + * Function: H5C_load_entry * - * To do this, unlink the specified entry from any data - * structures used by the replacement policy, and add the - * entry to the protected list. + * Purpose: Attempt to load the entry at the specified disk address + * and with the specified type into memory. If successful. + * return the in memory address of the entry. Return NULL + * on failure. * - * At present, we only support the modified LRU policy, so - * this function deals with that case unconditionally. If - * we ever support other replacement policies, the function - * should switch on the current policy and act accordingly. + * Note that this function simply loads the entry into + * core. It does not insert it into the cache. * - * Return: Non-negative on success/Negative on failure. + * Return: Non-NULL on success / NULL on failure. * - * Programmer: John Mainzer, 5/17/04 + * Programmer: John Mainzer, 5/18/04 * * Modifications: * + * JRM - 7/21/04 + * Updated function for the addition of the hash table. + * *------------------------------------------------------------------------- */ -static herr_t -H5C_update_rp_for_protect(H5C_t * cache_ptr, - H5C_cache_entry_t * entry_ptr) +static void * +H5C_load_entry(H5F_t * f, + hid_t dxpl_id, + const H5C_class_t * type, + haddr_t addr, + const void * udata1, + void * udata2, + hbool_t skip_file_checks) { - herr_t ret_value = SUCCEED; /* Return value */ + void * thing = NULL; + void * ret_value = NULL; + H5C_cache_entry_t * entry_ptr = NULL; - FUNC_ENTER_NOAPI_NOINIT(H5C_update_rp_for_protect) + FUNC_ENTER_NOAPI_NOINIT(H5C_load_entry) - HDassert( cache_ptr ); - HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); - HDassert( entry_ptr ); - HDassert( !entry_ptr->is_protected); - HDassert( entry_ptr->size > 0 ); + HDassert( skip_file_checks || f ); + HDassert( type ); + HDassert( type->load ); + HDassert( type->size ); + HDassert( H5F_addr_defined(addr) ); - /* modified LRU specific code */ + if ( NULL == (thing = (type->load)(f, dxpl_id, addr, udata1, udata2)) ) { - /* remove the entry from the LRU list. */ + HGOTO_ERROR(H5E_CACHE, H5E_CANTLOAD, NULL, "unable to load entry") - H5C__DLL_REMOVE(entry_ptr, cache_ptr->LRU_head_ptr, \ - cache_ptr->LRU_tail_ptr, cache_ptr->LRU_list_len, \ - cache_ptr->LRU_list_size) + } - /* Similarly, remove the entry from the clean or dirty LRU list - * as appropriate. - */ + entry_ptr = (H5C_cache_entry_t *)thing; - if ( entry_ptr->is_dirty ) { + HDassert( entry_ptr->is_dirty == FALSE ); - H5C__AUX_DLL_REMOVE(entry_ptr, cache_ptr->dLRU_head_ptr, \ - cache_ptr->dLRU_tail_ptr, \ - cache_ptr->dLRU_list_len, \ - cache_ptr->dLRU_list_size) + entry_ptr->addr = addr; + entry_ptr->type = type; + entry_ptr->is_protected = FALSE; + entry_ptr->in_tree = FALSE; - } else { + if ( (type->size)(f, thing, &(entry_ptr->size)) < 0 ) { - H5C__AUX_DLL_REMOVE(entry_ptr, cache_ptr->cLRU_head_ptr, \ - cache_ptr->cLRU_tail_ptr, \ - cache_ptr->cLRU_list_len, \ - cache_ptr->cLRU_list_size) + HGOTO_ERROR(H5E_RESOURCE, H5E_CANTGETSIZE, NULL, \ + "Can't get size of thing") } - /* End modified LRU specific code. */ + HDassert( entry_ptr->size < H5C_MAX_ENTRY_SIZE ); + entry_ptr->ht_next = NULL; + entry_ptr->ht_prev = NULL; - /* Regardless of the replacement policy, now add the entry to the - * protected list. - */ + entry_ptr->next = NULL; + entry_ptr->prev = NULL; - H5C__DLL_APPEND(entry_ptr, cache_ptr->pl_head_ptr, cache_ptr->pl_tail_ptr, \ - cache_ptr->pl_len, cache_ptr->pl_size) + entry_ptr->aux_next = NULL; + entry_ptr->aux_prev = NULL; + + H5C__RESET_CACHE_ENTRY_STATS(entry_ptr); + + ret_value = thing; -#if H5C_DO_SANITY_CHECKS done: -#endif /* H5C_DO_SANITY_CHECKS */ FUNC_LEAVE_NOAPI(ret_value) -} /* H5C_update_rp_for_protect() */ +} /* H5C_load_entry() */ /*------------------------------------------------------------------------- * - * Function: H5C_update_rp_for_rename + * Function: H5C_make_space_in_cache * - * Purpose: Update the replacement policy data structures for a - * rename of the specified cache entry. + * Purpose: Attempt to evict cache entries until the index_size + * is at least needed_space below max_cache_size. * - * At present, we only support the modified LRU policy, so - * this function deals with that case unconditionally. If - * we ever support other replacement policies, the function - * should switch on the current policy and act accordingly. + * In passing, also attempt to bring cLRU_list_size to a + * value greater than min_clean_size. + * + * Depending on circumstances, both of these goals may + * be impossible, as in parallel mode, we must avoid generating + * a write as part of a read (to avoid deadlock in collective + * I/O), and in all cases, it is possible (though hopefully + * highly unlikely) that the protected list may exceed the + * maximum size of the cache. + * + * Thus the function simply does its best, returning success + * unless an error is encountered. + * + * The primary_dxpl_id and secondary_dxpl_id parameters + * specify the dxpl_ids used on the first write occasioned + * by the call (primary_dxpl_id), and on all subsequent + * writes (secondary_dxpl_id). This is useful in the metadata + * cache, but may not be needed elsewhere. If so, just use the + * same dxpl_id for both parameters. + * + * Observe that this function cannot occasion a read. * * Return: Non-negative on success/Negative on failure. * - * Programmer: John Mainzer, 5/17/04 + * Programmer: John Mainzer, 5/14/04 * * Modifications: * + * JRM --7/21/04 + * Minor modifications in support of the addition of a hash + * table to facilitate lookups. + * *------------------------------------------------------------------------- */ static herr_t -H5C_update_rp_for_rename(H5C_t * cache_ptr, - H5C_cache_entry_t * entry_ptr) +H5C_make_space_in_cache(H5F_t * f, + hid_t primary_dxpl_id, + hid_t secondary_dxpl_id, + H5C_t * cache_ptr, + size_t space_needed, + hbool_t write_permitted) { + hbool_t first_flush = TRUE; herr_t ret_value = SUCCEED; /* Return value */ + herr_t result; + int32_t entries_examined = 0; + int32_t initial_list_len; + H5C_cache_entry_t * entry_ptr; + H5C_cache_entry_t * prev_ptr; - FUNC_ENTER_NOAPI_NOINIT(H5C_update_rp_for_rename) + FUNC_ENTER_NOAPI_NOINIT(H5C_make_space_in_cache) HDassert( cache_ptr ); HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); - HDassert( entry_ptr ); - HDassert( !entry_ptr->is_protected); - HDassert( entry_ptr->size > 0 ); - /* modified LRU specific code */ + if ( write_permitted ) { - /* remove the entry from the LRU list, and re-insert it at the head. */ + initial_list_len = cache_ptr->LRU_list_len; + entry_ptr = cache_ptr->LRU_tail_ptr; - H5C__DLL_REMOVE(entry_ptr, cache_ptr->LRU_head_ptr, \ - cache_ptr->LRU_tail_ptr, cache_ptr->LRU_list_len, \ - cache_ptr->LRU_list_size) + while ( ( (cache_ptr->index_size + space_needed) + > + cache_ptr->max_cache_size + ) + && + ( entries_examined <= (2 * initial_list_len) ) + && + ( entry_ptr != NULL ) + ) + { + HDassert( ! (entry_ptr->is_protected) ); - H5C__DLL_PREPEND(entry_ptr, cache_ptr->LRU_head_ptr, \ - cache_ptr->LRU_tail_ptr, cache_ptr->LRU_list_len, \ - cache_ptr->LRU_list_size) + prev_ptr = entry_ptr->prev; - /* move the entry to the head of either the clean or dirty LRU list - * as appropriate. - */ + if ( entry_ptr->is_dirty ) { - if ( entry_ptr->is_dirty ) { + result = H5C_flush_single_entry(f, + primary_dxpl_id, + secondary_dxpl_id, + cache_ptr, + entry_ptr->type, + entry_ptr->addr, + (unsigned)0, + NULL, + &first_flush, + FALSE); + } else { - H5C__AUX_DLL_REMOVE(entry_ptr, cache_ptr->dLRU_head_ptr, \ - cache_ptr->dLRU_tail_ptr, \ - cache_ptr->dLRU_list_len, \ - cache_ptr->dLRU_list_size) + result = H5C_flush_single_entry(f, + primary_dxpl_id, + secondary_dxpl_id, + cache_ptr, + entry_ptr->type, + entry_ptr->addr, + H5F_FLUSH_INVALIDATE, + NULL, + &first_flush, + TRUE); + } - H5C__AUX_DLL_PREPEND(entry_ptr, cache_ptr->dLRU_head_ptr, \ - cache_ptr->dLRU_tail_ptr, \ - cache_ptr->dLRU_list_len, \ - cache_ptr->dLRU_list_size) + if ( result < 0 ) { - } else { + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \ + "unable to flush entry") + } - H5C__AUX_DLL_REMOVE(entry_ptr, cache_ptr->cLRU_head_ptr, \ - cache_ptr->cLRU_tail_ptr, \ - cache_ptr->cLRU_list_len, \ - cache_ptr->cLRU_list_size) + entry_ptr = prev_ptr; + } - H5C__AUX_DLL_PREPEND(entry_ptr, cache_ptr->cLRU_head_ptr, \ - cache_ptr->cLRU_tail_ptr, \ - cache_ptr->cLRU_list_len, \ - cache_ptr->cLRU_list_size) - } +#if H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS -#if H5C_DO_SANITY_CHECKS -done: -#endif /* H5C_DO_SANITY_CHECKS */ + initial_list_len = cache_ptr->dLRU_list_len; + entry_ptr = cache_ptr->dLRU_tail_ptr; - FUNC_LEAVE_NOAPI(ret_value) + while ( ( cache_ptr->cLRU_list_size < cache_ptr->min_clean_size ) && + ( entries_examined <= initial_list_len ) && + ( entry_ptr != NULL ) + ) + { + HDassert( ! (entry_ptr->is_protected) ); + HDassert( entry_ptr->is_dirty ); + HDassert( entry_ptr->in_tree ); -} /* H5C_update_rp_for_rename() */ + prev_ptr = entry_ptr->aux_prev; - -/*------------------------------------------------------------------------- - * - * Function: H5C_update_rp_for_unprotect - * - * Purpose: Update the replacement policy data structures for an - * unprotect of the specified cache entry. - * - * To do this, unlink the specified entry from the protected - * list, and re-insert it in the data structures used by the - * current replacement policy. - * - * At present, we only support the modified LRU policy, so - * this function deals with that case unconditionally. If - * we ever support other replacement policies, the function - * should switch on the current policy and act accordingly. - * - * Return: Non-negative on success/Negative on failure. - * - * Programmer: John Mainzer, 5/19/04 - * - * Modifications: - * - *------------------------------------------------------------------------- - */ + result = H5C_flush_single_entry(f, + primary_dxpl_id, + secondary_dxpl_id, + cache_ptr, + entry_ptr->type, + entry_ptr->addr, + (unsigned)0, + NULL, + &first_flush, + FALSE); -static herr_t -H5C_update_rp_for_unprotect(H5C_t * cache_ptr, - H5C_cache_entry_t * entry_ptr) -{ - herr_t ret_value = SUCCEED; /* Return value */ + if ( result < 0 ) { - FUNC_ENTER_NOAPI_NOINIT(H5C_update_rp_for_unprotect) + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \ + "unable to flush entry") + } - HDassert( cache_ptr ); - HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); - HDassert( entry_ptr ); - HDassert( entry_ptr->is_protected); - HDassert( entry_ptr->size > 0 ); + entry_ptr = prev_ptr; + } - /* Regardless of the replacement policy, remove the entry from the - * protected list. - */ - H5C__DLL_REMOVE(entry_ptr, cache_ptr->pl_head_ptr, \ - cache_ptr->pl_tail_ptr, cache_ptr->pl_len, \ - cache_ptr->pl_size) +#endif /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */ + } else { - /* modified LRU specific code */ + HDassert( H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS ); - /* insert the entry at the head of the LRU list. */ + initial_list_len = cache_ptr->cLRU_list_len; + entry_ptr = cache_ptr->cLRU_tail_ptr; - H5C__DLL_PREPEND(entry_ptr, cache_ptr->LRU_head_ptr, \ - cache_ptr->LRU_tail_ptr, cache_ptr->LRU_list_len, \ - cache_ptr->LRU_list_size) + while ( ( (cache_ptr->index_size + space_needed) + > + cache_ptr->max_cache_size + ) + && + ( entries_examined <= initial_list_len ) + && + ( entry_ptr != NULL ) + ) + { + HDassert( ! (entry_ptr->is_protected) ); + HDassert( ! (entry_ptr->is_dirty) ); - /* Similarly, insert the entry at the head of either the clean or - * dirty LRU list as appropriate. - */ + prev_ptr = entry_ptr->aux_prev; - if ( entry_ptr->is_dirty ) { + result = H5C_flush_single_entry(f, + primary_dxpl_id, + secondary_dxpl_id, + cache_ptr, + entry_ptr->type, + entry_ptr->addr, + H5F_FLUSH_INVALIDATE, + NULL, + &first_flush, + TRUE); - H5C__AUX_DLL_PREPEND(entry_ptr, cache_ptr->dLRU_head_ptr, \ - cache_ptr->dLRU_tail_ptr, \ - cache_ptr->dLRU_list_len, \ - cache_ptr->dLRU_list_size) + if ( result < 0 ) { - } else { + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \ + "unable to flush entry") + } - H5C__AUX_DLL_PREPEND(entry_ptr, cache_ptr->cLRU_head_ptr, \ - cache_ptr->cLRU_tail_ptr, \ - cache_ptr->cLRU_list_len, \ - cache_ptr->cLRU_list_size) + entry_ptr = prev_ptr; + } } - /* End modified LRU specific code. */ - -#if H5C_DO_SANITY_CHECKS done: -#endif /* H5C_DO_SANITY_CHECKS */ FUNC_LEAVE_NOAPI(ret_value) -} /* H5C_update_rp_for_unprotect() */ - +} /* H5C_make_space_in_cache() */ diff --git a/src/H5Cprivate.h b/src/H5Cprivate.h index aad57f7..4c14c4c 100644 --- a/src/H5Cprivate.h +++ b/src/H5Cprivate.h @@ -43,7 +43,7 @@ * * JRM - 5/17/04 */ -#define H5C_MAX_ENTRY_SIZE ((size_t)(100 * 1024)) +#define H5C_MAX_ENTRY_SIZE ((size_t)(10 * 1024 * 1024)) /* H5C_COLLECT_CACHE_STATS controls overall collection of statistics * on cache activity. In general, this #define should be set to 0. @@ -66,6 +66,25 @@ #endif /* H5C_COLLECT_CACHE_STATS */ + +#ifdef H5_HAVE_PARALLEL + +/* we must maintain the clean and dirty LRU lists when we are compiled + * with parallel support. + */ +#define H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS 1 + +#else /* H5_HAVE_PARALLEL */ + +/* The clean and dirty LRU lists don't buy us anything here -- we may + * want them on for testing on occasion, but in general they should be + * off. + */ +#define H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS 0 + +#endif /* H5_HAVE_PARALLEL */ + + /* * Class methods pertaining to caching. Each type of cached object will * have a constant variable with permanent life-span that describes how @@ -130,28 +149,28 @@ typedef herr_t (*H5C_write_permitted_func_t)(H5F_t *f, * them generally accessable. */ -#define H5C__DEFAULT_MAX_CACHE_SIZE ((size_t)(2 * 1024 * 1024)) -#define H5C__DEFAULT_MIN_CLEAN_SIZE ((size_t)(1 * 1024 * 1024)) +#define H5C__DEFAULT_MAX_CACHE_SIZE ((size_t)(4 * 1024 * 1024)) +#define H5C__DEFAULT_MIN_CLEAN_SIZE ((size_t)(2 * 1024 * 1024)) /**************************************************************************** * * structure H5C_cache_entry_t * - * Instances of the H5C_cache_entry_t structure are used to store meta data - * cache entries in an a threaded binary B-tree. See H5TB.c for the - * particulars of the B-tree. + * Instances of the H5C_cache_entry_t structure are used to store cache + * entries in a hash table and sometimes in a threaded balanced binary tree. + * See H5TB.c for the particulars of the tree. * * In typical application, this structure is the first field in a * structure to be cached. For historical reasons, the external module - * is responsible for managing the dirty field. All other fields are + * is responsible for managing the is_dirty field. All other fields are * managed by the cache. * - * Note that our current implementation of a threaded binary B-tree will - * occasionaly change the node a particular datum is associated with. Thus - * this structure does not have a back pointer to its B-tree node. If we - * ever modify the threaded binary B-tree code to fix this, a back pointer - * would save us a few tree traversals. + * Note that our current implementation of a threaded balanced binary tree + * will occasionaly change the node a particular datum is associated with. + * Thus this structure does not have a back pointer to its tree node. If we + * ever modify the threaded balanced binary tree code to fix this, a back + * pointer would save us a few tree traversals. * * The fields of this structure are discussed individually below: * @@ -195,6 +214,28 @@ typedef herr_t (*H5C_write_permitted_func_t)(H5F_t *f, * Note that protected entries are removed from the LRU lists * and inserted on the protected list. * + * in_tree: Boolean flag indicating whether the entry is in the threaded + * balanced binary tree. As a general rule, entries are placed + * in the tree when they are marked dirty. However they may + * remain in the tree after being flushed. + * + * + * Fields supporting the hash table: + * + * Fields in the cache are indexed by a more or less conventional hash table. + * If there are multiple entries in any hash bin, they are stored in a doubly + * linked list. + * + * ht_next: Next pointer used by the hash table to store multiple + * entries in a single hash bin. This field points to the + * next entry in the doubly linked list of entries in the + * hash bin, or NULL if there is no next entry. + * + * ht_prev: Prev pointer used by the hash table to store multiple + * entries in a single hash bin. This field points to the + * previous entry in the doubly linked list of entries in + * the hash bin, or NULL if there is no previuos entry. + * * * Fields supporting replacement policies: * @@ -281,6 +322,12 @@ typedef struct H5C_cache_entry_t const H5C_class_t * type; hbool_t is_dirty; hbool_t is_protected; + hbool_t in_tree; + + /* fields supporting the hash table: */ + + struct H5C_cache_entry_t * ht_next; + struct H5C_cache_entry_t * ht_prev; /* fields supporting replacement policies: */ @@ -366,7 +413,6 @@ H5_DLL herr_t H5C_stats(H5C_t * cache_ptr, H5_DLL void H5C_stats__reset(H5C_t * cache_ptr); - H5_DLL herr_t H5C_set_skip_flags(H5C_t * cache_ptr, hbool_t skip_file_checks, hbool_t skip_dxpl_id_checks); diff --git a/src/H5F.c b/src/H5F.c index 6aac8d1..b181357 100644 --- a/src/H5F.c +++ b/src/H5F.c @@ -3277,9 +3277,9 @@ H5F_close(H5F_t *f) /* Only flush at this point if the file will be closed */ assert(closing); /* Dump debugging info */ -#if H5C_COLLECT_CACHE_STATS +#if H5AC_DUMP_STATS_ON_CLOSE H5AC_stats(f); -#endif /* H5AC_COLLECT_CACHE_STATS */ +#endif /* H5AC_DUMP_STATS_ON_CLOSE */ /* Only try to flush the file if it was opened with write access */ if(f->intent&H5F_ACC_RDWR) { -- cgit v0.12