worst case efficient dynamic arrays in practice
play

Worst-case-efficient dynamic arrays in practice Jyrki Katajainen - PowerPoint PPT Presentation

4 April, 2016 ARCO Meeting, Odense Last revision: 17 October, 2018 Worst-case-efficient dynamic arrays in practice Jyrki Katajainen Department of Computer Science University of Copenhagen paper code goto my research information system at


  1. 4 April, 2016 ARCO Meeting, Odense Last revision: 17 October, 2018 Worst-case-efficient dynamic arrays in practice Jyrki Katajainen Department of Computer Science University of Copenhagen paper code goto my research information system at http://www.diku.dk/~jyrki/ slides

  2. C array N 0 1 i A : p • contiguous memory segment • fast random access : • capacity fixed (denoted N ) A [ i ] ≡ ∗ ( A + i ) • uninitialized • iteration : • no bounds checking p = A ; ++ p ; ∗ p ;

  3. C ++ array n N 0 1 i A : p • contiguous memory segment • fast random access : • size varies ( ++ , −− , denoted n ) A [ i ] ≡ ∗ ( A . begin ( ) + i ) • capacity varies (denoted N ) • iteration : • initialized (up to n ) p = A . begin ( ); • bounds checking if desired ++ p ; ∗ p ;

  4. Array interfaces V , A = std::allocator < V > V std::vector leda::array + V : value type + V : value type + A : allocator type + I : iterator type + I : iterator type + item: I + N : size type + N : size type . . . . . . + default constructor + default constructor + destructor + copy constructor + get allocator() const → A + copy assignment + begin() const → I + destructor + end() const → I + first item() → I + last item() → I + size() const → N + next item( I ) → I + resize( N ) → void + prev item( I ) → I + capacity() const → N + begin() → I + reserve( N ) → void + end() → I + shrink to fit() → void + operator [ ]( N ) const → V const & + low() const → N + operator [ ]( N ) → V & + high() const → N LEDA user manual + push back( V const &) → void + size() const → N + push back( V &&) → void + resize( N ) → void C ++ standard + get( N ) → V & + pop back() → void + insert( I , V const &) → I + set( N , V ) → void + erase( I ) → I + operator [ ]( N ) → V & . . . . . . 62 member functions 36 member functions 7 free functions

  5. Bridge design pattern V , A = std::allocator < V > , K = std::vector < V , A > V , A = std::allocator < V > cphstl::vector array kernel + V : value type + V : value type + A : allocator type + A : allocator type + K : array kernel < V , A > + K : kernel type + I : rank iterator < K > . . . + J : rank iterator < K const > . . . + N : size type – A rgs : any argument-pack type . . . – index to address( N ) const → V * V , K = std::vector < V , std::allocator < V >> + default constructor + destructor cphleda::array + get allocator() const → A + V : value type + swap( K &) → void + K : kernel type + begin() const → I + end() const → I . . . + size() const → N . . . + capacity() const → N + operator [ ]( N ) const → V const & + operator [ ]( N ) → V & + emplace back( A rgs &&...) → void minimal + push back( V const &) → void + push back( V &&) → void + pop back() → void

  6. Usage example # include < algorithm > // std : : sort # include < cassert > // assert # include < iostream > // standard streams # include < memory > // std : : allocator # include ”sliced array . h++” // cphstl : : sliced array # include ” s t l − vector . h++” // cphstl : : vector int main ( int , char ∗∗ ) { using V = int ; using A = std : : allocator < V > ; using S = cphstl : : vector < V , A , cphstl : : sliced_array < V , A > > ; S sequence = { 4 , 2, 3, 1 } ; std : : sort ( sequence . begin ( ) , sequence . end ( )) ; assert ( std : : is_sorted ( sequence . begin ( ) , sequence . end ( ))) ; for ( V x : sequence ) { std : : cout < < ” ”; < x < } std : : cout < < ” \ n”; return 0; } jyrki@Janus:~/CPHSTL/Paper/Dynamic-arrays/Test$ make usage g++ -x c++ -std=c++17 -Wall -Wextra -fconcepts -I../Code usage.c++ ./a.out 1 2 3 4

  7. Reflection-based implementation // shrink to fit namespace { template < typename T > concept bool has_shrink_to_fit = requires ( T x ) { { x . shrink_to_fit ( ) } → void ; // member function } ; } template < typename V , typename A , typename K > void vector < V , A , K > : : shrink_to_fit ( ) { constexpr ( has_shrink_to_fit < K > ) { i f kernel . shrink_to_fit ( ) ; } else { // do nothing } }

  8. Goal In a minimal kernel, support all operations at O (1) worst- case cost. Assumptions : For allocators a , b , and integer ∆ ≥ 0: • p = a . allocate (∆) : O (1) worst case for any ∆ • a . deallocate ( p ,∆) : O (1) worst case for any ∆ • a . construct ( p , • ) : O (1) worst case • a . destroy ( p ) : O (1) worst case • a = b : O (1) worst case • word-RAM primitives: O (1) worst case

  9. Memory-management tests 1. Repeat t times ( t = 10 6 ): a) Allocate space for a block of ∆ bytes ( ∆ = 2 k ). b) Deallocate the allocated block without using it. 2. Repeat the above r times ( r = 101) and report the median of the execution times for a single allocate- deallocate pair. memory-management tests; median of the execution times [ns] std :: allocator < char > a ; a . deallocate ( a . allocate (∆),∆); 2 5 2 6 2 7 2 8 2 9 2 10 2 11 2 12 2 13 2 14 2 15 2 16 2 17 2 18 2 19 2 20 2 21 2 22 2 23 2 24 2 25 2 26 35 35 61 61 60 67 68 66 66 68 68 68 50 51 52 52 52 51 51 52 2456 2644 free ( malloc (∆)); 2 5 2 6 2 7 2 8 2 9 2 10 2 11 2 12 2 13 2 14 2 15 2 16 2 17 2 18 2 19 2 20 2 21 2 22 2 23 2 24 2 25 2 26 27 27 49 50 48 57 57 55 55 57 57 57 37 39 39 39 39 39 39 39 2360 2366 I know, I may have a problem, but you have it too!

  10. Textbook solution • doubling if ( n = N ) N 2 N 1. allocate 2 N X : Y : 2. move all from X to Y n = N 3. release X • halving if (4 n ≤ N ) N/ 2 N 1. allocate N/ 2 X : Y : 2. move all from X to Y 4 n ≤ N 3. release X

  11. Folklore solution ( cphstl :: resizable_array ) 2 N N • doubling X : Y : if ( n = N ) n = N allocate 2 N N/ 2 N • halving X : Y : if (4 n ≤ N ) 4 n ≤ N allocate N/ 2 • incremental copying push_back : move 1 from the end of X to Y at the same relative position pop_back : move 2 from the end of X to Y at the same relative position ( X empty ) if release X

  12. Slicing ( cphstl :: sliced_array ) • S : slice capacity (specified at compile time); • slice : C array of cap- acity S ; • # slices : ⌈ n/S ⌉ ; • last slice : can be non-full; • directory : resizable array S 0 1 2 • extra space : at most S elements plus O ( n/S ) pointers

  13. Piling ( cphstl :: pile ) • slice : C array of fixed capacity (specified at run time); • slice capacities exponentially increasing; • # slices : ⌈ lg(max { 2 , n } ) ⌉ ; • last slice : can be non-full; • directory : resizable array 2 0 1 2 2 3 4 8 • extra space : at most n elements plus O (lg n ) pointers

  14. Hashed array tree √ √ • N : fixed capacity (given at run time); • S = 2 ⌊ lg N/ 2 ⌋ ∈ ( N/ 2 . . N ]; • slice : C array of capacity S ; • # slices : ⌈ n/S ⌉ ; • last slice : can be non-full; • directory : C array of capacity ⌈ N/S ⌉ S 0 1 2 √ √ • extra space : at most N elements plus O ( N ) pointers

  15. Piling hashed array trees ( cphstl :: space_efficient_array ) • pile of hashed array trees; • at level i : hashed array tree of capacity max { 2 , 2 i } ; • # levels : ⌈ lg(max { 2 , n } ) ⌉ ; • last slice in the last hashed array tree : can be non-full; • directory : resizable array 0 1 2 3 at most √ n elements plus O ( √ n ) pointers; space • extra space : bound Ω( √ n ) optimal

  16. Summary of efficiencies • S : slice size; • n : current size solution elements pointers push_back | operator [ ] pop_back textbook O (1) O (1) amortized 6 n O (1) resizable O (1) O (1) 12 n O (1) sliced O (1) O (1) n + S O ( n/S ) pile O (1) O (1) 2 n O (lg n ) n + √ n O ( √ n ) space efficient O (1) O (1) • sources : [Sitarski 1996]; [Brodnik, Carlsson, Demaine, Munro, Sedgewick 1999]; [Katajainen, Mortensen 2001]

  17. Space test • overhead : 100% · (space in use − n · sizeof ( int ) )/ n · sizeof ( int ) space overhead after n push_back operations The amount of extra space in use at a specific time 200 resizable vector pile sliced space efficient 150 space overhead [%] 100 50 1.0 0 1×10 6 3×10 6 5×10 6 7×10 6 9×10 6 0.1 1×10 6 3×10 6 # push _ back ’s [ n ]

  18. Subscripting operator V & operator [ ]( N i ) { return ∗ index_to_address ( i ) ; } V ∗ index_to_address ( N i ) const { contiguous array i f ( i < 2) { return directory [0] + i ; V ∗ index_to_address ( N i ) const { } return A + i ; N h = whole_number_logarithm ( i ) ; } return directory [ h ] + i − (1 < < h ) ; } resizable array sliced array V ∗ index_to_address ( N i ) const { i f ( i < X_size ) { V ∗ index_to_address ( N i ) const { return X + i ; return directory [ i > shift ] + ( i bitand mask ) ; > } } return Y + i ; } space-efficient array pile V ∗ index_to_address ( N i ) const { i f ( i < 2) { N whole_number_logarithm ( N x ) { return directory [0] . index_to_address ( i ) ; asm (”bsr % 0, %0 \ n” } : ” = r”( x ) N h = whole_number_logarithm ( i ) ; : ”0” ( x ) N ∆ = i − (1 < < h ) ; ) ; return directory [ h ] . index_to_address (∆) ; return x ; } }

Download Presentation
Download Policy: The content available on the website is offered to you 'AS IS' for your personal information and use only. It cannot be commercialized, licensed, or distributed on other websites without prior consent from the author. To download a presentation, simply click this link. If you encounter any difficulties during the download process, it's possible that the publisher has removed the file from their server.

Recommend


More recommend