From bd20a26e85e0de2174848897576f214484d69fc4 Mon Sep 17 00:00:00 2001 From: xmas79 Date: Tue, 7 Jul 2015 19:03:36 +0200 Subject: [PATCH] Cache statistics I implemented some stats for the upper filesystem block usage. For the entire filesystem (Yes! it will consume a lot of memory, 36 bytes per block in this implementation) we now track how many times each block has been read, and how many time it has been written. We also track the "cumulative" time between reads and writes, so in the output we can average them and gather some usage patterns. I added also "collisions" and "replaces" in the hashtable implmementations. TODO: Dump the usage stats (only used blocks just to shorten the output) and the hash collisions stats (better in another FUSE file?) --- block_cache.c | 34 ++++++++++++++++++++++++++++++++++ block_cache.h | 13 +++++++++++++ hash.c | 5 +++++ 3 files changed, 52 insertions(+) diff --git a/block_cache.c b/block_cache.c index 03b05726..78015a3e 100644 --- a/block_cache.c +++ b/block_cache.c @@ -158,6 +158,7 @@ struct block_cache_private { pthread_cond_t worker_work; // there is new work for worker thread(s) pthread_cond_t worker_exit; // a worker thread has exited pthread_cond_t write_complete; // a write has completed + struct block_cache_usage_stats *usage_stats; /* Pointer to array of usage stats (for each block) */ }; /* Callback info */ @@ -198,6 +199,8 @@ static uint64_t block_cache_get_time_millis(void); static int block_cache_read_data(struct block_cache_private *priv, struct cache_entry *entry, void *dest, u_int off, u_int len); static int block_cache_write_data(struct block_cache_private *priv, struct cache_entry *entry, const void *src, u_int off, u_int len); +static void block_cache_update_read_stats(struct block_cache_private *priv, s3b_block_t block_num); +static void block_cache_update_write_stats(struct block_cache_private *priv, s3b_block_t block_num); /* Invariants checking */ #ifndef NDEBUG @@ -551,6 +554,9 @@ block_cache_do_read(struct block_cache_private *const priv, s3b_block_t block_nu assert(len <= priv->config->block_size); assert(off + len <= priv->config->block_size); + /* update read usage stats here, so we eventually count read hits too */ + block_cache_update_read_stats(priv, block_num); + again: /* Check to see if a cache entry already exists */ if ((entry = s3b_hash_get(priv->hashtable, block_num)) != NULL) { @@ -746,6 +752,9 @@ block_cache_write(struct block_cache_private *const priv, s3b_block_t block_num, /* Grab lock */ pthread_mutex_lock(&priv->mutex); + /* update write usage stats here, so we eventually count write hits too */ + block_cache_update_write_stats(priv, block_num); + again: /* Sanity check */ S3BCACHE_CHECK_INVARIANTS(priv); @@ -1338,6 +1347,31 @@ block_cache_dirty_callback(void *arg, void *value) } } + +static void block_cache_update_read_stats(struct block_cache_private *priv, s3b_block_t block_num) +{ + uint64_t timestamp = block_cache_get_time_millis(); + if (priv->stats.usage_stats[block_num].num_reads > 0) { + uint64_t old_timestamp = priv->stats.usage_stats[block_num].last_read_timestamp; + priv->stats.usage_stats[block_num].cumulative_reads_time += (timestamp - old_timestamp); + } + priv->stats.usage_stats[block_num].last_read_timestamp = timestamp; + priv->stats.usage_stats[block_num].num_reads++; +} + +static void block_cache_update_write_stats(struct block_cache_private *priv, s3b_block_t block_num) +{ + uint64_t timestamp = block_cache_get_time_millis(); + if (priv->stats.usage_stats[block_num].num_writes > 0) { + uint64_t old_timestamp = priv->stats.usage_stats[block_num].last_write_timestamp; + priv->stats.usage_stats[block_num].cumulative_writes_time += (timestamp - old_timestamp); + } + priv->stats.usage_stats[block_num].last_write_timestamp = timestamp; + priv->stats.usage_stats[block_num].num_writes++; +} + + + #ifndef NDEBUG /* Accounting structure */ diff --git a/block_cache.h b/block_cache.h index 1c329a0c..3eabf8ad 100644 --- a/block_cache.h +++ b/block_cache.h @@ -36,6 +36,18 @@ struct block_cache_conf { log_func_t *log; }; +/* Usage statistics for block_cache */ +/* 36 bytes for each block */ +struct block_cache_usage_stats { + uint16_t num_reads; /* Number of read operations */ + uint64_t cumulative_reads_time; /* Sum of all the differences between read opeation times */ + uint64_t last_read_timestamp; /* Last time we read this block */ + + uint16_t num_writes; /* Number of write operations */ + uint64_t cumulative_writes_time; /* Sum of all the differences between writes opeation times */ + uint64_t last_write_timestamp; /* Last time we wrote this block */ +}; + /* Statistics structure for block_cache */ struct block_cache_stats { u_int initial_size; @@ -48,6 +60,7 @@ struct block_cache_stats { u_int verified; u_int mismatch; u_int out_of_memory_errors; + struct block_cache_usage_stats *usage_stats; /* Pointer to array of usage stats (for each block) */ }; /* block_cache.c */ diff --git a/hash.c b/hash.c index 68cf106a..fd3abdbd 100644 --- a/hash.c +++ b/hash.c @@ -41,6 +41,8 @@ struct s3b_hash { u_int maxkeys; /* max capacity */ u_int numkeys; /* number of keys in table */ u_int alen; /* hash array length */ + u_int collisions; /* Hash collisions */ + u_int replaces; /* Correct hash replaces */ void *array[0]; /* hash array */ }; @@ -106,7 +108,10 @@ s3b_hash_put(struct s3b_hash *hash, void *value) break; if (KEY(value2) == key) { VALUE(hash, i) = value; /* replace existing value having the same key with new value */ + hash->replaces++; return value2; + } else { + hash->collisions++; /* We have a collision */ } } assert(hash->numkeys < hash->maxkeys);