123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158 |
- #include <stdio.h>
- #include <time.h>
- #include <stdint.h>
- #include <stdlib.h>
- int decr_every = 1;
- int keyspace_size = 1000000;
- time_t switch_after = 30; /* Switch access pattern after N seconds. */
- struct entry {
- /* Field that the LFU Redis implementation will have (we have
- * 24 bits of total space in the object->lru field). */
- uint8_t counter; /* Logarithmic counter. */
- uint16_t decrtime; /* (Reduced precision) time of last decrement. */
- /* Fields only useful for visualization. */
- uint64_t hits; /* Number of real accesses. */
- time_t ctime; /* Key creation time. */
- };
- #define to_16bit_minutes(x) ((x/60) & 65535)
- #define COUNTER_INIT_VAL 5
- /* Compute the difference in minutes between two 16 bit minutes times
- * obtained with to_16bit_minutes(). Since they can wrap around if
- * we detect the overflow we account for it as if the counter wrapped
- * a single time. */
- uint16_t minutes_diff(uint16_t now, uint16_t prev) {
- if (now >= prev) return now-prev;
- return 65535-prev+now;
- }
- /* Increment a couter logaritmically: the greatest is its value, the
- * less likely is that the counter is really incremented.
- * The maximum value of the counter is saturated at 255. */
- uint8_t log_incr(uint8_t counter) {
- if (counter == 255) return counter;
- double r = (double)rand()/RAND_MAX;
- double baseval = counter-COUNTER_INIT_VAL;
- if (baseval < 0) baseval = 0;
- double limit = 1.0/(baseval*10+1);
- if (r < limit) counter++;
- return counter;
- }
- /* Simulate an access to an entry. */
- void access_entry(struct entry *e) {
- e->counter = log_incr(e->counter);
- e->hits++;
- }
- /* Return the entry LFU value and as a side effect decrement the
- * entry value if the decrement time was reached. */
- uint8_t scan_entry(struct entry *e) {
- if (minutes_diff(to_16bit_minutes(time(NULL)),e->decrtime)
- >= decr_every)
- {
- if (e->counter) {
- if (e->counter > COUNTER_INIT_VAL*2) {
- e->counter /= 2;
- } else {
- e->counter--;
- }
- }
- e->decrtime = to_16bit_minutes(time(NULL));
- }
- return e->counter;
- }
- /* Print the entry info. */
- void show_entry(long pos, struct entry *e) {
- char *tag = "normal ";
- if (pos >= 10 && pos <= 14) tag = "new no access";
- if (pos >= 15 && pos <= 19) tag = "new accessed ";
- if (pos >= keyspace_size -5) tag= "old no access";
- printf("%ld] <%s> frequency:%d decrtime:%d [%lu hits | age:%ld sec]\n",
- pos, tag, e->counter, e->decrtime, (unsigned long)e->hits,
- time(NULL) - e->ctime);
- }
- int main(void) {
- time_t start = time(NULL);
- time_t new_entry_time = start;
- time_t display_time = start;
- struct entry *entries = malloc(sizeof(*entries)*keyspace_size);
- long j;
- /* Initialize. */
- for (j = 0; j < keyspace_size; j++) {
- entries[j].counter = COUNTER_INIT_VAL;
- entries[j].decrtime = to_16bit_minutes(start);
- entries[j].hits = 0;
- entries[j].ctime = time(NULL);
- }
- while(1) {
- time_t now = time(NULL);
- long idx;
- /* Scan N random entries (simulates the eviction under maxmemory). */
- for (j = 0; j < 3; j++) {
- scan_entry(entries+(rand()%keyspace_size));
- }
- /* Access a random entry: use a power-law access pattern up to
- * 'switch_after' seconds. Then revert to flat access pattern. */
- if (now-start < switch_after) {
- /* Power law. */
- idx = 1;
- while((rand() % 21) != 0 && idx < keyspace_size) idx *= 2;
- if (idx > keyspace_size) idx = keyspace_size;
- idx = rand() % idx;
- } else {
- /* Flat. */
- idx = rand() % keyspace_size;
- }
- /* Never access entries between position 10 and 14, so that
- * we simulate what happens to new entries that are never
- * accessed VS new entries which are accessed in positions
- * 15-19.
- *
- * Also never access last 5 entry, so that we have keys which
- * are never recreated (old), and never accessed. */
- if ((idx < 10 || idx > 14) && (idx < keyspace_size-5))
- access_entry(entries+idx);
- /* Simulate the addition of new entries at positions between
- * 10 and 19, a random one every 10 seconds. */
- if (new_entry_time <= now) {
- idx = 10+(rand()%10);
- entries[idx].counter = COUNTER_INIT_VAL;
- entries[idx].decrtime = to_16bit_minutes(time(NULL));
- entries[idx].hits = 0;
- entries[idx].ctime = time(NULL);
- new_entry_time = now+10;
- }
- /* Show the first 20 entries and the last 20 entries. */
- if (display_time != now) {
- printf("=============================\n");
- printf("Current minutes time: %d\n", (int)to_16bit_minutes(now));
- printf("Access method: %s\n",
- (now-start < switch_after) ? "power-law" : "flat");
- for (j = 0; j < 20; j++)
- show_entry(j,entries+j);
- for (j = keyspace_size-20; j < keyspace_size; j++)
- show_entry(j,entries+j);
- display_time = now;
- }
- }
- return 0;
- }
|