From 236a0c43ce166e746db3de4d316170bc64a047b3 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 16 Nov 2019 17:15:06 -0600 Subject: [PATCH 1/5] feat: add `CCVector.clear_and_reset` --- src/core/CCVector.ml | 4 ++++ src/core/CCVector.mli | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/src/core/CCVector.ml b/src/core/CCVector.ml index 1fa696b8..882a4d35 100644 --- a/src/core/CCVector.ml +++ b/src/core/CCVector.ml @@ -131,6 +131,10 @@ let clear v = OUnit.assert_bool "empty_after_clear" (Iter.is_empty (to_seq v)); *) +let clear_and_reset v = + v.size <- 0; + v.vec <- [||] + let is_empty v = v.size = 0 let push_unsafe_ v x = diff --git a/src/core/CCVector.mli b/src/core/CCVector.mli index 24d93821..98e2f202 100644 --- a/src/core/CCVector.mli +++ b/src/core/CCVector.mli @@ -53,6 +53,11 @@ val init : int -> (int -> 'a) -> ('a, 'mut) t val clear : ('a, rw) t -> unit (** Clear the content of the vector. *) +val clear_and_reset : ('a, rw) t -> unit +(** Clear the content of the vector, and deallocate the underlying array, + removing references to all the elements. + @since NEXT_RELEASE *) + val ensure_with : init:'a -> ('a, rw) t -> int -> unit (** Hint to the vector that it should have at least the given capacity. @param init if [capacity v = 0], used as a filler From 1239960c423b6b67ee7ffc73254cd95dc174acc5 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 16 Nov 2019 17:15:20 -0600 Subject: [PATCH 2/5] wip: improved gc behavior for ccvector --- src/core/CCVector.ml | 50 ++++++++++++++++++++++++++++++++++++++++++- src/core/CCVector.mli | 3 ++- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/src/core/CCVector.ml b/src/core/CCVector.ml index 882a4d35..454bc104 100644 --- a/src/core/CCVector.ml +++ b/src/core/CCVector.ml @@ -70,12 +70,23 @@ let init n f = { let array_is_empty_ v = Array.length v.vec = 0 +external as_float_arr : 'a array -> float array = "%identity" +external as_obj_arr : 'a array -> Obj.t array = "%identity" + +let fill_with_junk_ (a:_ array) i len : unit = + if Obj.(tag (repr a) = double_array_tag) then ( + Array.fill (as_float_arr a) i len 0.; + ) else ( + Array.fill (as_obj_arr a) i len (Obj.repr ()); + ) + (* assuming the underlying array isn't empty, resize it *) let resize_ v newcapacity = assert (newcapacity >= v.size); assert (not (array_is_empty_ v)); let new_vec = Array.make newcapacity v.vec.(0) in Array.blit v.vec 0 new_vec 0 v.size; + fill_with_junk_ new_vec v.size (newcapacity-v.size); v.vec <- new_vec; () @@ -84,10 +95,35 @@ let resize_ v newcapacity = ensure v 200; capacity v >= 200 *) +(*$T + let v = create() in push v 0.; push v 1.; push v 2.; 3=length v + let v = create() in push v 1.; push v 2.; push v 3.; 6. = (get v 0 +. get v 1 +. get v 2) + let v = create() in push v 0; push v 1; push v 2; 3=length v + let v = create() in push v 1; push v 2; push v 3; 6 = (get v 0 + get v 1 + get v 2) + let v = create() in push v "a"; push v "b"; push v "c"; 3=length v + let v = create() in push v "a"; push v "b"; push v "c"; "abc" = String.concat "" (to_list v) +*) + +(*$R + let v = create() in + push v 0.; push v 1.; + clear v; + push v 0.; push v 1.; push v 7.; push v 10.; push v 12.; + shrink v 2; + assert_equal 1. (fold (+.) 0. v); + clear v; + assert_equal 0 (size v); + push v 0.; push v 1.; push v 7.; push v 10.; push v 12.; + assert_equal (1. +. 7. +. 10. +. 12.) (fold (+.) 0. v); + *) + (* grow the array, using [x] as a filler if required *) let grow_with_ v ~filler:x = if array_is_empty_ v then ( - v.vec <- Array.make 4 x + let len = 4 in + v.vec <- Array.make len x; + (* do not really use [x], it was just for knowing the type *) + fill_with_junk_ v.vec 0 len; ) else ( let n = Array.length v.vec in let size = min (2 * n + 3) Sys.max_array_length in @@ -135,6 +171,18 @@ let clear_and_reset v = v.size <- 0; v.vec <- [||] +(* TODO*) +(* + let v = create() in + let a = Weak.create 1 in + push v ("hello"^"world"); + Weak.set a 0 (Some (get v 0)); + Gc.full_major(); Gc.compact(); + OUnit.assert_bool "is alive" (Weak.check a 0); + Gc.full_major(); Gc.compact(); + OUnit.assert_equal None (Weak.get a 0); +*) + let is_empty v = v.size = 0 let push_unsafe_ v x = diff --git a/src/core/CCVector.mli b/src/core/CCVector.mli index 98e2f202..c2da68e9 100644 --- a/src/core/CCVector.mli +++ b/src/core/CCVector.mli @@ -45,7 +45,8 @@ val return : 'a -> ('a, 'mut) t @since 0.14 *) val make : int -> 'a -> ('a, 'mut) t -(** [make n x] makes a vector of size [n], filled with [x]. *) +(** [make n x] makes a vector of size [n], filled with [x]. + The element [x] will possibly live as long as the vector. *) val init : int -> (int -> 'a) -> ('a, 'mut) t (** Init the vector with the given function and size. *) From 858616606b46419d808fbc42961292faa738230b Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 19 Nov 2019 14:46:33 -0600 Subject: [PATCH 3/5] add `CCVector.shrink_to_fit` to limit memory usage --- src/core/CCVector.ml | 15 +++++++++++++++ src/core/CCVector.mli | 5 ++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/core/CCVector.ml b/src/core/CCVector.ml index 454bc104..4bff2543 100644 --- a/src/core/CCVector.ml +++ b/src/core/CCVector.ml @@ -453,6 +453,21 @@ let shrink v n = ) *) +let shrink_to_fit v : unit = + if v.size = 0 then ( + v.vec <- [| |] + ) else if v.size < Array.length v.vec then ( + v.vec <- Array.sub v.vec 0 v.size + ) + +(*$QR + (gen Q.small_int) (fun v -> + let v' = copy v in + shrink_to_fit v; + to_list v = to_list v' + ) +*) + let sort' cmp v = (* possibly copy array (to avoid junk at its end), then sort the array *) let a = diff --git a/src/core/CCVector.mli b/src/core/CCVector.mli index c2da68e9..7271e2f9 100644 --- a/src/core/CCVector.mli +++ b/src/core/CCVector.mli @@ -1,4 +1,3 @@ - (* This file is free software, part of containers. See file "license" for more details. *) (** {1 Growable, mutable vector} *) @@ -124,6 +123,10 @@ val shrink : ('a, rw) t -> int -> unit (** Shrink to the given size (remove elements above this size). Does nothing if the parameter is bigger than the current size. *) +val shrink_to_fit : ('a, _) t -> unit +(** Shrink internal array to fit the size of the vector + @since NEXT_RELEASE *) + val member : eq:('a -> 'a -> bool) -> 'a -> ('a, _) t -> bool (** Is the element a member of the vector? *) From 1d589cf4ac34223df36d8da847cfae2d954e1465 Mon Sep 17 00:00:00 2001 From: Fardale Date: Sat, 14 Dec 2019 16:55:33 +0100 Subject: [PATCH 4/5] use array_is_empty_ instead of direct check --- src/core/CCVector.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/CCVector.ml b/src/core/CCVector.ml index 4bff2543..d4932bf5 100644 --- a/src/core/CCVector.ml +++ b/src/core/CCVector.ml @@ -145,14 +145,14 @@ let ensure_assuming_not_empty_ v ~size = ) let ensure_with ~init v size = - if Array.length v.vec = 0 then ( + if array_is_empty_ v then ( v.vec <- Array.make size init ) else ( ensure_assuming_not_empty_ v ~size ) let ensure v size = - if Array.length v.vec > 0 then ( + if not (array_is_empty_ v) then ( ensure_assuming_not_empty_ v ~size ) From bbdcd93417d6a64801cd49da9ba7e651fd08dabf Mon Sep 17 00:00:00 2001 From: Fardale Date: Sat, 14 Dec 2019 17:36:25 +0100 Subject: [PATCH 5/5] wip: improved gc behavior for ccvector --- src/core/CCVector.ml | 51 +++++++++++++++++++++---------------------- src/core/CCVector.mli | 12 +++++----- 2 files changed, 30 insertions(+), 33 deletions(-) diff --git a/src/core/CCVector.ml b/src/core/CCVector.ml index d4932bf5..4289cf73 100644 --- a/src/core/CCVector.ml +++ b/src/core/CCVector.ml @@ -22,6 +22,16 @@ type 'a vector = ('a, rw) t type 'a ro_vector = ('a, ro) t +external as_float_arr : 'a array -> float array = "%identity" +external as_obj_arr : 'a array -> Obj.t array = "%identity" + +let fill_with_junk_ (a:_ array) i len : unit = + if Obj.(tag (repr a) = double_array_tag) then ( + Array.fill (as_float_arr a) i len 0.; + ) else ( + Array.fill (as_obj_arr a) i len (Obj.repr ()); + ) + let freeze v = { size=v.size; vec=v.vec; @@ -37,9 +47,12 @@ let create () = { vec = [| |]; } -let create_with ?(capacity=128) x = { +let create_with ?(capacity=128) x = + let vec = Array.make capacity x in + fill_with_junk_ vec 0 capacity; + { size = 0; - vec = Array.make capacity x; + vec } (*$T @@ -70,16 +83,6 @@ let init n f = { let array_is_empty_ v = Array.length v.vec = 0 -external as_float_arr : 'a array -> float array = "%identity" -external as_obj_arr : 'a array -> Obj.t array = "%identity" - -let fill_with_junk_ (a:_ array) i len : unit = - if Obj.(tag (repr a) = double_array_tag) then ( - Array.fill (as_float_arr a) i len 0.; - ) else ( - Array.fill (as_obj_arr a) i len (Obj.repr ()); - ) - (* assuming the underlying array isn't empty, resize it *) let resize_ v newcapacity = assert (newcapacity >= v.size); @@ -146,7 +149,8 @@ let ensure_assuming_not_empty_ v ~size = let ensure_with ~init v size = if array_is_empty_ v then ( - v.vec <- Array.make size init + v.vec <- Array.make size init; + fill_with_junk_ v.vec 0 size ) else ( ensure_assuming_not_empty_ v ~size ) @@ -249,7 +253,8 @@ let remove v i = if i < v.size - 1 then v.vec.(i) <- v.vec.(v.size - 1); (* remove one element *) - v.size <- v.size - 1 + v.size <- v.size - 1; + fill_with_junk_ v.vec v.size 1 let append_seq a seq = seq (fun x -> push a x) @@ -378,7 +383,8 @@ let pop_exn v = let new_size = v.size - 1 in v.size <- new_size; let x = v.vec.(new_size) in - if new_size > 0 then v.vec.(new_size) <- v.vec.(0); (* free last element *) + (* free last element *) + fill_with_junk_ v.vec new_size 1; x let pop v = @@ -430,10 +436,8 @@ let shrink v n = let old_size = v.size in if n < old_size then ( v.size <- n; - if n > 0 then ( - (* free elements by erasing them *) - Array.fill v.vec n (old_size-n) v.vec.(0); - ) + (* free elements by erasing them *) + fill_with_junk_ v.vec n (old_size-n); ) (*$R @@ -611,9 +615,7 @@ let filter' p v = ) else incr i done; (* free elements *) - if !j > 0 && !j < v.size then ( - Array.fill v.vec !j (v.size - !j) v.vec.(0); - ); + fill_with_junk_ v.vec !j (v.size - !j); v.size <- !j (*$T @@ -756,7 +758,6 @@ let filter_map f v = to_list (filter_map f v) = CCList.filter_map f l) *) -(* TODO: free elements *) let filter_map_in_place f v = let i = ref 0 in (* cur element *) let j = ref 0 in (* cur insertion point *) @@ -772,9 +773,7 @@ let filter_map_in_place f v = incr j done; (* free elements *) - if !j > 0 && !j < v.size then ( - Array.fill v.vec !j (v.size - !j) v.vec.(0); - ); + fill_with_junk_ v.vec !j (v.size - !j); v.size <- !j (*$QR diff --git a/src/core/CCVector.mli b/src/core/CCVector.mli index 7271e2f9..dc0b07b4 100644 --- a/src/core/CCVector.mli +++ b/src/core/CCVector.mli @@ -35,17 +35,15 @@ val create : unit -> ('a, rw) t (** Create a new, empty vector. *) val create_with : ?capacity:int -> 'a -> ('a, rw) t -(** Create a new vector, using the given value as a filler. - @param capacity the size of the underlying array. - {b caution}: the value will likely not be GC'd before the vector is. *) +(** Create a new vector, the value is used to enforce the type the new vector. + @param capacity the size of the underlying array. *) val return : 'a -> ('a, 'mut) t (** Singleton vector. @since 0.14 *) val make : int -> 'a -> ('a, 'mut) t -(** [make n x] makes a vector of size [n], filled with [x]. - The element [x] will possibly live as long as the vector. *) +(** [make n x] makes a vector of size [n], filled with [x]. *) val init : int -> (int -> 'a) -> ('a, 'mut) t (** Init the vector with the given function and size. *) @@ -60,8 +58,8 @@ val clear_and_reset : ('a, rw) t -> unit val ensure_with : init:'a -> ('a, rw) t -> int -> unit (** Hint to the vector that it should have at least the given capacity. - @param init if [capacity v = 0], used as a filler - element for the underlying array (see {!create_with}). + @param init if [capacity v = 0], used to enforce the type of the vector + (see {!create_with}). @since 0.14 *) val ensure : ('a, rw) t -> int -> unit