1- use std:: time:: { Duration , Instant } ;
1+ use std:: time:: Duration ;
2+ #[ cfg( not( test) ) ]
3+ use std:: time:: Instant ;
4+
5+ #[ cfg( test) ]
6+ use mock_instant:: Instant ;
27
38#[ cfg( not( test) ) ]
49pub const PAGE_SIZE : usize = 1 << 20 ;
@@ -60,7 +65,7 @@ impl Default for ArenaStats {
6065 fn default ( ) -> ArenaStats {
6166 ArenaStats {
6267 // We arbitrarily initialize num used pages former to 100.
63- max_num_used_pages_former : 100 ,
68+ max_num_used_pages_former : 0 ,
6469 max_num_used_pages_current : 0 ,
6570 call_counter : 0u8 ,
6671 next_window_start : Instant :: now ( ) ,
@@ -76,35 +81,27 @@ impl ArenaStats {
7681 self . next_window_start = now + WINDOW ;
7782 }
7883
84+ /// Records the number of used pages, and returns an estimation of the maximum number of pages
85+ /// in the last 5 minutes.
7986 pub fn record_num_used_page ( & mut self , num_used_pages : usize ) -> usize {
8087 // The only function of the call counter is to avoid calling `Instant::now()`
8188 // at every single call.
82- self . call_counter = self . call_counter . wrapping_add ( 1 ) ;
89+ self . call_counter = ( self . call_counter + 1 ) % 64 ;
8390 if self . call_counter == 0u8 {
8491 let now = Instant :: now ( ) ;
8592 if now > self . next_window_start {
8693 self . roll ( now) ;
8794 }
8895 }
8996 self . max_num_used_pages_current = self . max_num_used_pages_current . max ( num_used_pages) ;
90- self . target_num_pages ( )
91- }
92-
93- // This method returns a target number of pages.
94- //
95- // If we currently have a number of allocated pages higher than this, we need to free
96- // pages until we reach this number.
97- fn target_num_pages ( & self ) -> usize {
98- let max_over_both_windows = self
99- . max_num_used_pages_former
100- . max ( self . max_num_used_pages_current ) ;
101- ( max_over_both_windows + 10 ) . max ( max_over_both_windows * 105 / 100 )
97+ self . max_num_used_pages_former
98+ . max ( self . max_num_used_pages_current )
10299 }
103100}
104101
105102impl Arena {
106103 /// Returns an allocated page id.
107- pub fn get_page_id ( & mut self ) -> PageId {
104+ pub fn acquire_page ( & mut self ) -> PageId {
108105 if let Some ( page_id) = self . free_page_ids . pop ( ) {
109106 assert ! ( self . pages[ page_id. 0 ] . is_some( ) ) ;
110107 return page_id;
@@ -141,7 +138,11 @@ impl Arena {
141138 /// `gc` releases memory by deallocating ALL of the free pages.
142139 pub fn gc ( & mut self ) {
143140 let num_used_pages = self . num_used_pages ( ) ;
144- let target_num_pages = self . stats . record_num_used_page ( num_used_pages) ;
141+ let max_used_num_pages_in_last_5_min = self . stats . record_num_used_page ( num_used_pages) ;
142+ // We pick a target slightly higher than the maximum number of pages used in the last 5
143+ // minutes to avoid needless allocations when we are experience a general increase
144+ // in memory usage.
145+ let target_num_pages = ( max_used_num_pages_in_last_5_min * 105 / 100 ) . max ( 10 ) ;
145146 let num_pages_to_free = self . num_allocated_pages ( ) . saturating_sub ( target_num_pages) ;
146147 assert ! ( num_pages_to_free <= self . free_page_ids. len( ) ) ;
147148 for _ in 0 ..num_pages_to_free {
@@ -162,40 +163,63 @@ impl Arena {
162163 self . pages . len ( ) - self . free_slots . len ( ) - self . free_page_ids . len ( )
163164 }
164165
165- pub fn capacity ( & self ) -> usize {
166- self . num_allocated_pages ( ) * PAGE_SIZE
167- }
168-
169166 pub fn unused_capacity ( & self ) -> usize {
170167 self . free_page_ids . len ( ) * PAGE_SIZE
171168 }
172169}
173170
174171#[ cfg( test) ]
175172mod tests {
173+ use mock_instant:: MockClock ;
174+
176175 use super :: * ;
177176
178177 #[ test]
179178 fn test_arena_simple ( ) {
180179 let mut arena = Arena :: default ( ) ;
181- assert_eq ! ( arena. capacity ( ) , 0 ) ;
182- assert_eq ! ( arena. get_page_id ( ) , PageId ( 0 ) ) ;
183- assert_eq ! ( arena. get_page_id ( ) , PageId ( 1 ) ) ;
180+ assert_eq ! ( arena. num_allocated_pages ( ) , 0 ) ;
181+ assert_eq ! ( arena. acquire_page ( ) , PageId ( 0 ) ) ;
182+ assert_eq ! ( arena. acquire_page ( ) , PageId ( 1 ) ) ;
184183 arena. release_page ( PageId ( 0 ) ) ;
185- assert_eq ! ( arena. get_page_id ( ) , PageId ( 0 ) ) ;
184+ assert_eq ! ( arena. acquire_page ( ) , PageId ( 0 ) ) ;
186185 }
187186
188187 #[ test]
189188 fn test_arena_gc ( ) {
190189 let mut arena = Arena :: default ( ) ;
191- assert_eq ! ( arena. capacity ( ) , 0 ) ;
192- assert_eq ! ( arena. get_page_id ( ) , PageId ( 0 ) ) ;
193- assert_eq ! ( arena. get_page_id ( ) , PageId ( 1 ) ) ;
190+ assert_eq ! ( arena. num_allocated_pages ( ) , 0 ) ;
191+ assert_eq ! ( arena. acquire_page ( ) , PageId ( 0 ) ) ;
192+ assert_eq ! ( arena. acquire_page ( ) , PageId ( 1 ) ) ;
194193 arena. release_page ( PageId ( 1 ) ) ;
195194 assert_eq ! ( arena. num_allocated_pages( ) , 2 ) ;
196195 arena. gc ( ) ;
197196 assert_eq ! ( arena. num_allocated_pages( ) , 2 ) ;
198- assert_eq ! ( arena. get_page_id ( ) , PageId ( 1 ) ) ;
197+ assert_eq ! ( arena. acquire_page ( ) , PageId ( 1 ) ) ;
199198 assert_eq ! ( arena. num_allocated_pages( ) , 2 ) ;
200199 }
200+
201+ #[ test]
202+ fn test_arena_stats ( ) {
203+ let mut arena_stats = ArenaStats :: default ( ) ;
204+ for _ in 0 ..256 {
205+ assert_eq ! ( arena_stats. record_num_used_page( 10 ) , 10 ) ;
206+ }
207+ MockClock :: advance ( WINDOW . mul_f32 ( 1.1f32 ) ) ;
208+ for _ in 0 ..256 {
209+ assert_eq ! ( arena_stats. record_num_used_page( 1 ) , 10 ) ;
210+ }
211+ MockClock :: advance ( WINDOW . mul_f32 ( 1.1f32 ) ) ;
212+ for _ in 0 ..256 {
213+ arena_stats. record_num_used_page ( 1 ) ;
214+ }
215+ assert_eq ! ( arena_stats. record_num_used_page( 1 ) , 1 ) ;
216+ assert_eq ! ( arena_stats. record_num_used_page( 2 ) , 2 ) ;
217+ for _ in 0 ..256 {
218+ assert_eq ! ( arena_stats. record_num_used_page( 1 ) , 2 ) ;
219+ }
220+ MockClock :: advance ( WINDOW ) ;
221+ for _ in 0 ..256 {
222+ assert_eq ! ( arena_stats. record_num_used_page( 1 ) , 2 ) ;
223+ }
224+ }
201225}
0 commit comments