1+ use std:: sync:: atomic:: AtomicUsize ;
2+ use std:: sync:: atomic:: Ordering ;
13use std:: time:: Duration ;
2- #[ cfg( not( test) ) ]
3- use std:: time:: Instant ;
4-
5- #[ cfg( test) ]
6- use mock_instant:: Instant ;
74
85/// 256 KiB.
96#[ cfg( not( test) ) ]
@@ -54,49 +51,34 @@ pub struct Arena {
5451// Pitfall: If pages are requests way less often than 256 times per minutes,
5552// this arena may take way too much time to release its memory.
5653struct ArenaStats {
57- max_num_used_pages_former : usize ,
58- max_num_used_pages_current : usize ,
59- call_counter : u8 ,
60- next_window_start : Instant ,
54+ max_num_used_pages_former : AtomicUsize ,
55+ max_num_used_pages_current : AtomicUsize ,
6156}
6257
63- const WINDOW : Duration = Duration :: from_secs ( 60 ) ;
64-
6558impl Default for ArenaStats {
6659 fn default ( ) -> ArenaStats {
6760 ArenaStats {
6861 // We arbitrarily initialize num used pages former to 100.
69- max_num_used_pages_former : 0 ,
70- max_num_used_pages_current : 0 ,
71- call_counter : 0u8 ,
72- next_window_start : Instant :: now ( ) ,
62+ max_num_used_pages_former : AtomicUsize :: new ( 0 ) ,
63+ max_num_used_pages_current : AtomicUsize :: new ( 0 ) ,
7364 }
7465 }
7566}
7667
7768impl ArenaStats {
7869 /// This method happens when we are changing time window.
79- fn roll ( & mut self , now : Instant ) {
80- self . max_num_used_pages_former = self . max_num_used_pages_current ;
81- self . max_num_used_pages_current = 0 ;
82- self . next_window_start = now + WINDOW ;
70+ fn roll ( & mut self , ) {
71+ let max_num_used_page_former = self . max_num_used_pages_current . load ( Ordering :: Relaxed ) ;
72+ self . max_num_used_pages_former . store ( max_num_used_page_former , Ordering :: Relaxed ) ;
73+ self . max_num_used_pages_current . store ( 0 , Ordering :: Relaxed ) ;
8374 }
8475
8576 /// Records the number of used pages, and returns an estimation of the maximum number of pages
8677 /// in the last 5 minutes.
8778 pub fn record_num_used_page ( & mut self , num_used_pages : usize ) -> usize {
88- // The only function of the call counter is to avoid calling `Instant::now()`
89- // at every single call.
90- self . call_counter = ( self . call_counter + 1 ) % 64 ;
91- if self . call_counter == 0u8 {
92- let now = Instant :: now ( ) ;
93- if now > self . next_window_start {
94- self . roll ( now) ;
95- }
96- }
97- self . max_num_used_pages_current = self . max_num_used_pages_current . max ( num_used_pages) ;
98- self . max_num_used_pages_former
99- . max ( self . max_num_used_pages_current )
79+ let max_num_used_pages = self . max_num_used_pages_current . load ( Ordering :: Relaxed ) . max ( num_used_pages) ;
80+ self . max_num_used_pages_current . store ( max_num_used_pages, Ordering :: Relaxed ) ;
81+ self . max_num_used_pages_former . load ( Ordering :: Relaxed ) . max ( max_num_used_pages)
10082 }
10183}
10284
@@ -105,20 +87,17 @@ impl Arena {
10587 pub fn acquire_page ( & mut self ) -> PageId {
10688 if let Some ( page_id) = self . free_page_ids . pop ( ) {
10789 assert ! ( self . pages[ page_id. 0 ] . is_some( ) ) ;
108- self . gc ( ) ;
10990 return page_id;
11091 }
11192 let page: Page = vec ! [ 0u8 ; PAGE_SIZE ] . into_boxed_slice ( ) ;
11293 if let Some ( free_slot) = self . free_slots . pop ( ) {
11394 let slot = & mut self . pages [ free_slot. 0 ] ;
11495 assert ! ( slot. is_none( ) ) ;
11596 * slot = Some ( page) ;
116- self . gc ( ) ;
11797 free_slot
11898 } else {
11999 let new_page_id = self . pages . len ( ) ;
120100 self . pages . push ( Some ( page) ) ;
121- self . gc ( ) ;
122101 PageId ( new_page_id)
123102 }
124103 }
@@ -139,7 +118,13 @@ impl Arena {
139118 self . gc ( ) ;
140119 }
141120
142- /// `gc` releases memory by deallocating ALL of the free pages.
121+ /// Clients are expected roll the stats regularly.
122+ pub fn roll ( & mut self , ) {
123+ self . stats . roll ( ) ;
124+ self . gc ( ) ;
125+ }
126+
127+ /// `gc` releases memory by some of the free pages.
143128 pub fn gc ( & mut self ) {
144129 let num_used_pages = self . num_used_pages ( ) ;
145130 let max_used_num_pages_in_last_5_min = self . stats . record_num_used_page ( num_used_pages) ;
0 commit comments