lfu-simulation.c 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. #include <stdio.h>
  2. #include <time.h>
  3. #include <stdint.h>
  4. #include <stdlib.h>
  5. int decr_every = 1;
  6. int keyspace_size = 1000000;
  7. time_t switch_after = 30; /* Switch access pattern after N seconds. */
  8. struct entry {
  9. /* Field that the LFU Redis implementation will have (we have
  10. * 24 bits of total space in the object->lru field). */
  11. uint8_t counter; /* Logarithmic counter. */
  12. uint16_t decrtime; /* (Reduced precision) time of last decrement. */
  13. /* Fields only useful for visualization. */
  14. uint64_t hits; /* Number of real accesses. */
  15. time_t ctime; /* Key creation time. */
  16. };
  17. #define to_16bit_minutes(x) ((x/60) & 65535)
  18. #define COUNTER_INIT_VAL 5
  19. /* Compute the difference in minutes between two 16 bit minutes times
  20. * obtained with to_16bit_minutes(). Since they can wrap around if
  21. * we detect the overflow we account for it as if the counter wrapped
  22. * a single time. */
  23. uint16_t minutes_diff(uint16_t now, uint16_t prev) {
  24. if (now >= prev) return now-prev;
  25. return 65535-prev+now;
  26. }
  27. /* Increment a counter logarithmically: the greatest is its value, the
  28. * less likely is that the counter is really incremented.
  29. * The maximum value of the counter is saturated at 255. */
  30. uint8_t log_incr(uint8_t counter) {
  31. if (counter == 255) return counter;
  32. double r = (double)rand()/RAND_MAX;
  33. double baseval = counter-COUNTER_INIT_VAL;
  34. if (baseval < 0) baseval = 0;
  35. double limit = 1.0/(baseval*10+1);
  36. if (r < limit) counter++;
  37. return counter;
  38. }
  39. /* Simulate an access to an entry. */
  40. void access_entry(struct entry *e) {
  41. e->counter = log_incr(e->counter);
  42. e->hits++;
  43. }
  44. /* Return the entry LFU value and as a side effect decrement the
  45. * entry value if the decrement time was reached. */
  46. uint8_t scan_entry(struct entry *e) {
  47. if (minutes_diff(to_16bit_minutes(time(NULL)),e->decrtime)
  48. >= decr_every)
  49. {
  50. if (e->counter) {
  51. if (e->counter > COUNTER_INIT_VAL*2) {
  52. e->counter /= 2;
  53. } else {
  54. e->counter--;
  55. }
  56. }
  57. e->decrtime = to_16bit_minutes(time(NULL));
  58. }
  59. return e->counter;
  60. }
  61. /* Print the entry info. */
  62. void show_entry(long pos, struct entry *e) {
  63. char *tag = "normal ";
  64. if (pos >= 10 && pos <= 14) tag = "new no access";
  65. if (pos >= 15 && pos <= 19) tag = "new accessed ";
  66. if (pos >= keyspace_size -5) tag= "old no access";
  67. printf("%ld] <%s> frequency:%d decrtime:%d [%lu hits | age:%ld sec]\n",
  68. pos, tag, e->counter, e->decrtime, (unsigned long)e->hits,
  69. time(NULL) - e->ctime);
  70. }
  71. int main(void) {
  72. time_t start = time(NULL);
  73. time_t new_entry_time = start;
  74. time_t display_time = start;
  75. struct entry *entries = malloc(sizeof(*entries)*keyspace_size);
  76. long j;
  77. /* Initialize. */
  78. for (j = 0; j < keyspace_size; j++) {
  79. entries[j].counter = COUNTER_INIT_VAL;
  80. entries[j].decrtime = to_16bit_minutes(start);
  81. entries[j].hits = 0;
  82. entries[j].ctime = time(NULL);
  83. }
  84. while(1) {
  85. time_t now = time(NULL);
  86. long idx;
  87. /* Scan N random entries (simulates the eviction under maxmemory). */
  88. for (j = 0; j < 3; j++) {
  89. scan_entry(entries+(rand()%keyspace_size));
  90. }
  91. /* Access a random entry: use a power-law access pattern up to
  92. * 'switch_after' seconds. Then revert to flat access pattern. */
  93. if (now-start < switch_after) {
  94. /* Power law. */
  95. idx = 1;
  96. while((rand() % 21) != 0 && idx < keyspace_size) idx *= 2;
  97. if (idx > keyspace_size) idx = keyspace_size;
  98. idx = rand() % idx;
  99. } else {
  100. /* Flat. */
  101. idx = rand() % keyspace_size;
  102. }
  103. /* Never access entries between position 10 and 14, so that
  104. * we simulate what happens to new entries that are never
  105. * accessed VS new entries which are accessed in positions
  106. * 15-19.
  107. *
  108. * Also never access last 5 entry, so that we have keys which
  109. * are never recreated (old), and never accessed. */
  110. if ((idx < 10 || idx > 14) && (idx < keyspace_size-5))
  111. access_entry(entries+idx);
  112. /* Simulate the addition of new entries at positions between
  113. * 10 and 19, a random one every 10 seconds. */
  114. if (new_entry_time <= now) {
  115. idx = 10+(rand()%10);
  116. entries[idx].counter = COUNTER_INIT_VAL;
  117. entries[idx].decrtime = to_16bit_minutes(time(NULL));
  118. entries[idx].hits = 0;
  119. entries[idx].ctime = time(NULL);
  120. new_entry_time = now+10;
  121. }
  122. /* Show the first 20 entries and the last 20 entries. */
  123. if (display_time != now) {
  124. printf("=============================\n");
  125. printf("Current minutes time: %d\n", (int)to_16bit_minutes(now));
  126. printf("Access method: %s\n",
  127. (now-start < switch_after) ? "power-law" : "flat");
  128. for (j = 0; j < 20; j++)
  129. show_entry(j,entries+j);
  130. for (j = keyspace_size-20; j < keyspace_size; j++)
  131. show_entry(j,entries+j);
  132. display_time = now;
  133. }
  134. }
  135. return 0;
  136. }