@@ -40,15 +40,22 @@ typedef struct FIFO_Reinsertion_params {
4040 cache_obj_t * q_tail ;
4141
4242 // points to the eviction position
43- cache_obj_t * next_to_merge ;
44- // the number of object to examine at each eviction
45- int n_exam_obj ;
46- // of the n_exam_obj , we keep n_keep_obj and evict the rest
47- int n_keep_obj ;
48- // used to sort the n_exam_obj objects
43+ cache_obj_t * next_to_exam ;
44+ // the number of bytes to examine at each eviction round
45+ int64_t n_exam_byte ;
46+ // of the n_exam_byte examined , we keep n_keep_byte and evict the rest
47+ int64_t n_keep_byte ;
48+ // used to sort the objects collected in the current batch
4949 struct sort_list_node * metric_list ;
50- // the policy to determine the n_keep_obj objects
50+ // current allocated capacity (in entries) of metric_list
51+ int metric_list_capacity ;
52+ // the policy to determine which objects to keep
5153 retain_policy_t retain_policy ;
54+ int pos_in_metric_list ;
55+ // number of objects in the eviction portion of the current sorted batch
56+ int n_collected_in_batch ;
57+ // number of bytes already evicted from the current sorted batch
58+ int64_t bytes_evicted_in_batch ;
5259
5360 int64_t n_obj_rewritten ;
5461 int64_t n_byte_rewritten ;
@@ -118,24 +125,30 @@ cache_t *FIFO_Reinsertion_init(const common_cache_params_t ccache_params,
118125 memset (params , 0 , sizeof (FIFO_Reinsertion_params_t ));
119126 cache -> eviction_params = params ;
120127
121- params -> n_exam_obj = 100 ;
122- params -> n_keep_obj = params -> n_exam_obj / 5 ;
128+ params -> n_exam_byte = 100LL * 1024 * 1024 ; // 100 MB
129+ params -> n_keep_byte = 25LL * 1024 * 1024 ; // 25 MB
123130 params -> retain_policy = RETAIN_POLICY_RECENCY ;
124- params -> next_to_merge = NULL ;
131+ params -> next_to_exam = NULL ;
125132 params -> q_head = NULL ;
126133 params -> q_tail = NULL ;
134+ params -> pos_in_metric_list = INT32_MAX ;
135+ params -> n_collected_in_batch = 0 ;
136+ params -> bytes_evicted_in_batch = 0 ;
127137
128138 if (cache_specific_params != NULL ) {
129139 FIFO_Reinsertion_parse_params (cache , cache_specific_params );
130140 }
131141
132- assert (params -> n_exam_obj > 0 && params -> n_keep_obj >= 0 );
133- assert (params -> n_keep_obj <= params -> n_exam_obj );
142+ assert (params -> n_exam_byte > 0 && params -> n_keep_byte >= 0 );
143+ assert (params -> n_keep_byte <= params -> n_exam_byte );
134144
135145 snprintf (cache -> cache_name , CACHE_NAME_ARRAY_LEN , "FIFO_Reinsertion_%s-%.4lf" ,
136146 retain_policy_names [params -> retain_policy ],
137- (double )params -> n_keep_obj / params -> n_exam_obj );
138- params -> metric_list = my_malloc_n (struct sort_list_node , params -> n_exam_obj );
147+ (double )params -> n_keep_byte / (double )params -> n_exam_byte );
148+ // initial capacity guess; grows on demand in the eviction routine
149+ params -> metric_list_capacity = 1024 ;
150+ params -> metric_list =
151+ my_malloc_n (struct sort_list_node , params -> metric_list_capacity );
139152
140153 return cache ;
141154}
@@ -148,7 +161,7 @@ cache_t *FIFO_Reinsertion_init(const common_cache_params_t ccache_params,
148161static void FIFO_Reinsertion_free (cache_t * cache ) {
149162 FIFO_Reinsertion_params_t * params =
150163 (FIFO_Reinsertion_params_t * )cache -> eviction_params ;
151- my_free (sizeof (struct sort_list_node ) * params -> n_exam_obj ,
164+ my_free (sizeof (struct sort_list_node ) * params -> metric_list_capacity ,
152165 params -> metric_list );
153166 my_free (sizeof (FIFO_Reinsertion_params_t ), params );
154167 cache_struct_free (cache );
@@ -259,60 +272,95 @@ static cache_obj_t *FIFO_Reinsertion_to_evict(cache_t *cache,
259272static void FIFO_Reinsertion_evict (cache_t * cache , const request_t * req ) {
260273 FIFO_Reinsertion_params_t * params =
261274 (FIFO_Reinsertion_params_t * )cache -> eviction_params ;
262-
263- // collect metric for n_exam obj, we will keep objects with larger metric
264- int n_loop = 0 ;
265- cache_obj_t * cache_obj = params -> next_to_merge ;
266- if (cache_obj == NULL ) {
267- params -> next_to_merge = params -> q_tail ;
268- cache_obj = params -> q_tail ;
269- n_loop = 1 ;
275+ cache_obj_t * cache_obj = NULL ;
276+ // Collect ~n_exam_byte objects, sort them, then:
277+ // - reinsert the keep-set to head immediately (no cache-size change)
278+ // - store the evict-set and drain it one object per call
279+ const int64_t bytes_to_evict_per_batch =
280+ params -> n_exam_byte - params -> n_keep_byte ;
281+
282+ if (params -> pos_in_metric_list < params -> n_collected_in_batch &&
283+ params -> bytes_evicted_in_batch < bytes_to_evict_per_batch ) {
284+ cache_obj = params -> metric_list [params -> pos_in_metric_list ++ ].cache_obj ;
285+ params -> bytes_evicted_in_batch += cache_obj -> obj_size ;
286+ remove_obj_from_list (& params -> q_head , & params -> q_tail , cache_obj );
287+ cache_evict_base (cache , cache_obj , true);
288+ return ;
270289 }
271290
272- if (cache -> n_obj <= params -> n_exam_obj ) {
273- // just evict one object
274- cache_obj = params -> next_to_merge -> queue . prev ;
275- FIFO_Reinsertion_remove_obj ( cache , params -> next_to_merge ) ;
276- params -> next_to_merge = cache_obj ;
277-
291+ if (cache -> get_occupied_byte ( cache ) <= params -> n_exam_byte ) {
292+ // cache is too small for a useful merge - fall back to plain FIFO
293+ cache_obj = params -> q_tail ;
294+ params -> next_to_exam = NULL ;
295+ remove_obj_from_list ( & params -> q_head , & params -> q_tail , cache_obj ) ;
296+ cache_evict_base ( cache , cache_obj , true);
278297 return ;
279298 }
280299
281- for (int i = 0 ; i < params -> n_exam_obj ; i ++ ) {
282- assert (cache_obj != NULL );
283- params -> metric_list [i ].metric = retain_metric (cache , cache_obj );
284- params -> metric_list [i ].cache_obj = cache_obj ;
285- cache_obj = cache_obj -> queue .prev ;
286-
287- // TODO: wrap back to the head of the list early before reaching the end of
288- // the list
300+ // collect ~n_exam_byte worth of objects from the merge cursor
301+ int n_loop = 0 ;
302+ int64_t bytes_collected = 0 ;
303+ int n_collected = 0 ;
304+ cache_obj = params -> next_to_exam ;
305+ while (bytes_collected < params -> n_exam_byte ) {
289306 if (cache_obj == NULL ) {
290307 cache_obj = params -> q_tail ;
291- DEBUG_ASSERT (n_loop ++ <= 2 );
308+ n_loop += 1 ;
309+ DEBUG_ASSERT (n_loop <= 2 );
292310 }
311+
312+ // grow metric_list if needed
313+ if (n_collected >= params -> metric_list_capacity ) {
314+ int new_capacity = params -> metric_list_capacity * 2 ;
315+ struct sort_list_node * new_list =
316+ my_malloc_n (struct sort_list_node , new_capacity );
317+ memcpy (new_list , params -> metric_list ,
318+ sizeof (struct sort_list_node ) * params -> metric_list_capacity );
319+ my_free (sizeof (struct sort_list_node ) * params -> metric_list_capacity ,
320+ params -> metric_list );
321+ params -> metric_list = new_list ;
322+ params -> metric_list_capacity = new_capacity ;
323+ }
324+
325+ params -> metric_list [n_collected ].metric = retain_metric (cache , cache_obj );
326+ params -> metric_list [n_collected ].cache_obj = cache_obj ;
327+ bytes_collected += cache_obj -> obj_size ;
328+ n_collected += 1 ;
329+ cache_obj = cache_obj -> queue .prev ;
293330 }
294- params -> next_to_merge = cache_obj ;
331+ params -> next_to_exam = cache_obj ;
295332
296- // sort metrics
297- qsort (params -> metric_list , params -> n_exam_obj , sizeof (struct sort_list_node ),
333+ // sort by metric ascending - low metric (least worth keeping) first
334+ qsort (params -> metric_list , n_collected , sizeof (struct sort_list_node ),
298335 cmp_list_node );
299336
300- // remove objects
301- int n_evict = params -> n_exam_obj - params -> n_keep_obj ;
302- for (int i = 0 ; i < n_evict ; i ++ ) {
303- cache_obj = params -> metric_list [i ].cache_obj ;
304- FIFO_Reinsertion_remove_obj (cache , cache_obj );
337+ // find the split point: evict [0..n_evict), reinsert [n_evict..n_collected)
338+ int n_evict = 0 ;
339+ int64_t bytes_evict_counted = 0 ;
340+ while (n_evict < n_collected && bytes_evict_counted < bytes_to_evict_per_batch ) {
341+ bytes_evict_counted += params -> metric_list [n_evict ].cache_obj -> obj_size ;
342+ n_evict ++ ;
305343 }
306344
307- for (int i = n_evict ; i < params -> n_exam_obj ; i ++ ) {
345+ // reinsert the keep-set to head now (size unchanged, so safe to do eagerly)
346+ for (int i = n_evict ; i < n_collected ; i ++ ) {
308347 cache_obj = params -> metric_list [i ].cache_obj ;
309348 move_obj_to_head (& params -> q_head , & params -> q_tail , cache_obj );
310349 cache_obj -> FIFO_Reinsertion .freq =
311350 (cache_obj -> FIFO_Reinsertion .freq + 1 ) / 2 ;
312-
313351 params -> n_obj_rewritten += 1 ;
314352 params -> n_byte_rewritten += cache_obj -> obj_size ;
315353 }
354+
355+ // begin draining the evict-set one object per call
356+ params -> n_collected_in_batch = n_evict ;
357+ params -> bytes_evicted_in_batch = 0 ;
358+ params -> pos_in_metric_list = 1 ;
359+
360+ cache_obj = params -> metric_list [0 ].cache_obj ;
361+ params -> bytes_evicted_in_batch += cache_obj -> obj_size ;
362+ remove_obj_from_list (& params -> q_head , & params -> q_tail , cache_obj );
363+ cache_evict_base (cache , cache_obj , true);
316364}
317365
318366static void FIFO_Reinsertion_remove_obj (cache_t * cache , cache_obj_t * obj ) {
@@ -356,20 +404,51 @@ static bool FIFO_Reinsertion_remove(cache_t *cache, obj_id_t obj_id) {
356404static const char * FIFO_Reinsertion_current_params (
357405 FIFO_Reinsertion_params_t * params ) {
358406 static __thread char params_str [128 ];
359- snprintf (params_str , 128 , "n-exam=%d, n-keep=%d, retain-policy=%s" ,
360- params -> n_exam_obj , params -> n_keep_obj ,
407+ snprintf (params_str , 128 ,
408+ "n-exam-byte=%lld, n-keep-byte=%lld, retain-policy=%s" ,
409+ (long long )params -> n_exam_byte , (long long )params -> n_keep_byte ,
361410 retain_policy_names [params -> retain_policy ]);
362411 return params_str ;
363412}
364413
414+ /* Parse a human-readable byte size like "100", "100KB", "100MB", "2GB".
415+ * Suffix is case-insensitive; binary (1024) units are used.
416+ * Returns the parsed value, or aborts on parse error. */
417+ static int64_t FIFO_Reinsertion_parse_byte_size (const char * value ) {
418+ char * end = NULL ;
419+ int64_t n = (int64_t )strtoll (value , & end , 0 );
420+ if (end == value ) {
421+ ERROR ("param parsing error: expected number, got \"%s\"\n" , value );
422+ exit (1 );
423+ }
424+ while (* end == ' ' ) end ++ ;
425+ if (* end == '\0' ) return n ;
426+
427+ int64_t mult = 1 ;
428+ if (strcasecmp (end , "B" ) == 0 ) {
429+ mult = 1 ;
430+ } else if (strcasecmp (end , "K" ) == 0 || strcasecmp (end , "KB" ) == 0 ) {
431+ mult = 1024LL ;
432+ } else if (strcasecmp (end , "M" ) == 0 || strcasecmp (end , "MB" ) == 0 ) {
433+ mult = 1024LL * 1024 ;
434+ } else if (strcasecmp (end , "G" ) == 0 || strcasecmp (end , "GB" ) == 0 ) {
435+ mult = 1024LL * 1024 * 1024 ;
436+ } else if (strcasecmp (end , "T" ) == 0 || strcasecmp (end , "TB" ) == 0 ) {
437+ mult = 1024LL * 1024 * 1024 * 1024 ;
438+ } else {
439+ ERROR ("param parsing error: unknown size suffix \"%s\"\n" , end );
440+ exit (1 );
441+ }
442+ return n * mult ;
443+ }
444+
365445static void FIFO_Reinsertion_parse_params (cache_t * cache ,
366446 const char * cache_specific_params ) {
367447 FIFO_Reinsertion_params_t * params =
368448 (FIFO_Reinsertion_params_t * )cache -> eviction_params ;
369449
370450 char * params_str = strdup (cache_specific_params );
371451 char * old_params_str = params_str ;
372- char * end ;
373452
374453 while (params_str != NULL && params_str [0 ] != '\0' ) {
375454 /* different parameters are separated by comma,
@@ -391,21 +470,15 @@ static void FIFO_Reinsertion_parse_params(cache_t *cache,
391470 params -> retain_policy = RETAIN_POLICY_BELADY ;
392471 else if (strcasecmp (value , "none" ) == 0 ) {
393472 params -> retain_policy = RETAIN_NONE ;
394- params -> n_keep_obj = 0 ;
473+ params -> n_keep_byte = 0 ;
395474 } else {
396475 ERROR ("unknown retain-policy %s\n" , value );
397476 exit (1 );
398477 }
399- } else if (strcasecmp (key , "n-exam" ) == 0 ) {
400- params -> n_exam_obj = (int )strtol (value , & end , 0 );
401- if (strlen (end ) > 2 ) {
402- ERROR ("param parsing error, find string \"%s\" after number\n" , end );
403- }
404- } else if (strcasecmp (key , "n-keep" ) == 0 ) {
405- params -> n_keep_obj = (int )strtol (value , & end , 0 );
406- if (strlen (end ) > 2 ) {
407- ERROR ("param parsing error, find string \"%s\" after number\n" , end );
408- }
478+ } else if (strcasecmp (key , "n-exam-byte" ) == 0 ) {
479+ params -> n_exam_byte = FIFO_Reinsertion_parse_byte_size (value );
480+ } else if (strcasecmp (key , "n-keep-byte" ) == 0 ) {
481+ params -> n_keep_byte = FIFO_Reinsertion_parse_byte_size (value );
409482 } else if (strcasecmp (key , "print" ) == 0 ) {
410483 printf ("%s parameters: %s\n" , cache -> cache_name ,
411484 FIFO_Reinsertion_current_params (params ));
0 commit comments