1
+ /*
2
+ * rv32emu is freely redistributable under the MIT License. See the file
3
+ * "LICENSE" for information on usage and redistribution of this file.
4
+ */
5
+
6
+ #include "cache.h"
7
+
8
+ #define MIN (a , b ) ((a < b) ? a : b)
9
+ #define BITS 10
10
+ #define SIZE 1024
11
+ #define GOLDEN_RATIO_32 0x61C88647
12
+ #define HASH (val ) (((val) *GOLDEN_RATIO_32) >> (32 - BITS)) % SIZE
13
+
14
+ typedef struct arc_entry {
15
+ void * value ;
16
+ uint32_t key ;
17
+ list_type_t arc_type ;
18
+ struct list_head list ;
19
+ struct list_head ht_list ;
20
+ } arc_entry_t ;
21
+
22
+ typedef struct hashtable {
23
+ struct list_head * ht_list_head ;
24
+ } hashtable_t ;
25
+
26
+ cache_t * cache_create ()
27
+ {
28
+ cache_t * cache = (cache_t * ) malloc (sizeof (cache_t ));
29
+ for (int i = 0 ; i < 4 ; i ++ ) {
30
+ cache -> list_table [i ] =
31
+ (struct list_head * ) malloc (sizeof (struct list_head ));
32
+ INIT_LIST_HEAD (cache -> list_table [i ]);
33
+ cache -> list_size [i ] = 0 ;
34
+ }
35
+ cache -> map = (hashtable_t * ) malloc (sizeof (hashtable_t ));
36
+ cache -> map -> ht_list_head =
37
+ (struct list_head * ) malloc (SIZE * sizeof (struct list_head ));
38
+
39
+ for (int i = 0 ; i < SIZE ; i ++ ) {
40
+ INIT_LIST_HEAD (& cache -> map -> ht_list_head [i ]);
41
+ }
42
+
43
+ cache -> capacity = SIZE ;
44
+ cache -> lru_capacity = SIZE / 2 ;
45
+ #if CACHE_INFO
46
+ cache -> get_time = 0 ;
47
+ cache -> hit_time = 0 ;
48
+ #endif
49
+ return cache ;
50
+ }
51
+
52
+ /* Rule of ARC
53
+ * 1. size of LRU_list + size of LFU_list <= c
54
+ * 2. size of LRU_list + size of LRU_ghost_list <= c
55
+ * 3. size of LFU_list + size of LFU_ghost_list <= 2c
56
+ * 4. size of LRU_list + size of LFU_list + size of LRU_ghost_list + size of
57
+ * LFU_ghost_list <= 2c
58
+ */
59
+ void assert_cache (cache_t * cache )
60
+ {
61
+ assert (cache -> list_size [LRU_list ] + cache -> list_size [LFU_list ] <=
62
+ cache -> capacity );
63
+ assert (cache -> list_size [LRU_list ] + cache -> list_size [LRU_ghost_list ] <=
64
+ cache -> capacity );
65
+ assert (cache -> list_size [LFU_list ] + cache -> list_size [LFU_ghost_list ] <=
66
+ 2 * cache -> capacity );
67
+ assert (cache -> list_size [LRU_list ] + cache -> list_size [LRU_ghost_list ] +
68
+ cache -> list_size [LFU_list ] + cache -> list_size [LFU_ghost_list ] <=
69
+ 2 * cache -> capacity );
70
+ }
71
+
72
+ void move_to_mru (cache_t * cache , arc_entry_t * entry , const list_type_t arc_type )
73
+ {
74
+ cache -> list_size [entry -> arc_type ]-- ;
75
+ cache -> list_size [arc_type ]++ ;
76
+ entry -> arc_type = arc_type ;
77
+ list_move (& entry -> list , cache -> list_table [arc_type ]);
78
+ }
79
+
80
+ void replace_LRU_list (cache_t * cache )
81
+ {
82
+ if (cache -> list_size [LRU_list ] >= cache -> lru_capacity )
83
+ move_to_mru (
84
+ cache ,
85
+ list_last_entry (cache -> list_table [LRU_list ], arc_entry_t , list ),
86
+ LRU_ghost_list );
87
+ }
88
+ void replace_LFU_list (cache_t * cache )
89
+ {
90
+ if (cache -> list_size [LFU_list ] >= (cache -> capacity - cache -> lru_capacity ))
91
+ move_to_mru (
92
+ cache ,
93
+ list_last_entry (cache -> list_table [LFU_list ], arc_entry_t , list ),
94
+ LFU_ghost_list );
95
+ }
96
+
97
+ void * cache_get (cache_t * cache , uint32_t key )
98
+ {
99
+ if (cache -> capacity <= 0 ||
100
+ list_empty (& cache -> map -> ht_list_head [HASH (key )]))
101
+ return NULL ;
102
+
103
+ arc_entry_t * entry = NULL ;
104
+ list_for_each_entry (entry , & cache -> map -> ht_list_head [HASH (key )], ht_list )
105
+ {
106
+ if (entry -> key == key )
107
+ break ;
108
+ }
109
+ #if CACHE_INFO
110
+ cache -> get_time ++ ;
111
+ #endif
112
+ if (!entry || entry -> key != key )
113
+ return NULL ;
114
+ /* cache hit in LRU_list */
115
+ if (entry -> arc_type == LRU_list ) {
116
+ #if CACHE_INFO
117
+ cache -> hit_time ++ ;
118
+ #endif
119
+ replace_LFU_list (cache );
120
+ move_to_mru (cache , entry , LFU_list );
121
+ }
122
+
123
+ /* cache hit in LFU_list */
124
+ if (entry -> arc_type == LFU_list ) {
125
+ #if CACHE_INFO
126
+ cache -> hit_time ++ ;
127
+ #endif
128
+ move_to_mru (cache , entry , LFU_list );
129
+ }
130
+
131
+ /* cache hit in LRU_ghost_list */
132
+ if (entry -> arc_type == LRU_ghost_list ) {
133
+ cache -> lru_capacity = MIN (cache -> lru_capacity + 1 , cache -> capacity );
134
+ replace_LFU_list (cache );
135
+ move_to_mru (cache , entry , LFU_list );
136
+ }
137
+
138
+ /* cache hit in LFU_ghost_list */
139
+ if (entry -> arc_type == LFU_ghost_list ) {
140
+ cache -> lru_capacity = cache -> lru_capacity ? cache -> lru_capacity - 1 : 0 ;
141
+ replace_LRU_list (cache );
142
+ move_to_mru (cache , entry , LFU_list );
143
+ }
144
+ #if CACHE_INFO
145
+ assert_cache (cache );
146
+ #endif
147
+ /* return NULL if cache miss */
148
+ return entry -> value ;
149
+ }
150
+
151
+ void * cache_put (cache_t * cache , uint32_t key , void * value )
152
+ {
153
+ #if CACHE_INFO
154
+ cache -> get_time ++ ;
155
+ #endif
156
+ void * delete_value = NULL ;
157
+ assert (cache -> list_size [LRU_list ] + cache -> list_size [LRU_ghost_list ] <=
158
+ cache -> capacity );
159
+ /* Before adding new element to cach, we should check the status
160
+ * of cache.
161
+ */
162
+ if ((cache -> list_size [LRU_list ] + cache -> list_size [LRU_ghost_list ]) ==
163
+ cache -> capacity ) {
164
+ if (cache -> list_size [LRU_list ] < cache -> capacity ) {
165
+ arc_entry_t * delete_target = list_last_entry (
166
+ cache -> list_table [LRU_ghost_list ], arc_entry_t , list );
167
+ list_del_init (& delete_target -> list );
168
+ list_del_init (& delete_target -> ht_list );
169
+ delete_value = delete_target -> value ;
170
+ free (delete_target );
171
+ cache -> list_size [LRU_ghost_list ]-- ;
172
+ replace_LRU_list (cache );
173
+ } else {
174
+ arc_entry_t * delete_target =
175
+ list_last_entry (cache -> list_table [LRU_list ], arc_entry_t , list );
176
+ list_del_init (& delete_target -> list );
177
+ list_del_init (& delete_target -> ht_list );
178
+ delete_value = delete_target -> value ;
179
+ free (delete_target );
180
+ cache -> list_size [LRU_list ]-- ;
181
+ }
182
+ } else {
183
+ assert (cache -> list_size [LRU_list ] + cache -> list_size [LRU_ghost_list ] <
184
+ cache -> capacity );
185
+ uint32_t size =
186
+ cache -> list_size [LRU_list ] + cache -> list_size [LRU_ghost_list ] +
187
+ cache -> list_size [LFU_list ] + cache -> list_size [LFU_ghost_list ];
188
+ if (size == cache -> capacity * 2 ) {
189
+ arc_entry_t * delete_target = list_last_entry (
190
+ cache -> list_table [LFU_ghost_list ], arc_entry_t , list );
191
+ list_del_init (& delete_target -> list );
192
+ list_del_init (& delete_target -> ht_list );
193
+ delete_value = delete_target -> value ;
194
+ free (delete_target );
195
+ cache -> list_size [LFU_ghost_list ]-- ;
196
+ }
197
+ if (cache -> list_size [LRU_list ] + cache -> list_size [LFU_list ] >=
198
+ cache -> capacity &&
199
+ cache -> list_size [LRU_list ] < cache -> lru_capacity )
200
+ replace_LFU_list (cache );
201
+ else
202
+ replace_LRU_list (cache );
203
+ }
204
+ arc_entry_t * new_entry = (arc_entry_t * ) malloc (sizeof (arc_entry_t ));
205
+ new_entry -> key = key ;
206
+ new_entry -> value = value ;
207
+ new_entry -> arc_type = LRU_list ;
208
+ list_add (& new_entry -> list , cache -> list_table [LRU_list ]);
209
+ list_add (& new_entry -> ht_list , & cache -> map -> ht_list_head [HASH (key )]);
210
+ cache -> list_size [LRU_list ]++ ;
211
+ assert_cache (cache );
212
+ return delete_value ;
213
+ }
214
+
215
+ #if CACHE_INFO
216
+ void cache_print_stats (cache_t * cache )
217
+ {
218
+ printf (
219
+ "requests: %12lu \n"
220
+ "hits: %12lu \n"
221
+ "ratio: %lf%%\n" ,
222
+ cache -> get_time , cache -> hit_time ,
223
+ cache -> hit_time * 100 / (double ) cache -> get_time );
224
+ }
225
+ #endif
226
+
227
+ void cache_free (cache_t * cache , void (* release_entry )(void * ))
228
+ {
229
+ #if CACHE_INFO
230
+ cache_print_stats (cache );
231
+ #endif
232
+ for (int i = 0 ; i < 4 ; i ++ ) {
233
+ arc_entry_t * entry , * safe ;
234
+ list_for_each_entry_safe (entry , safe , cache -> list_table [i ], list )
235
+ release_entry (entry -> value );
236
+ free (cache -> list_table [i ]);
237
+ }
238
+ free (cache -> map -> ht_list_head );
239
+ free (cache -> map );
240
+ free (cache );
241
+ }
0 commit comments