diff options
-rw-r--r-- | byterun/major_gc.c | 51 | ||||
-rw-r--r-- | byterun/major_gc.h | 5 | ||||
-rw-r--r-- | byterun/weak.c | 74 | ||||
-rw-r--r-- | stdlib/weak.ml | 168 | ||||
-rw-r--r-- | test/Makefile | 2 | ||||
-rw-r--r-- | test/Results/weaktest.out | 1 | ||||
-rw-r--r-- | test/weaktest.ml | 73 |
7 files changed, 280 insertions, 94 deletions
diff --git a/byterun/major_gc.c b/byterun/major_gc.c index 18da47721..3c903740a 100644 --- a/byterun/major_gc.c +++ b/byterun/major_gc.c @@ -48,12 +48,13 @@ extern char *caml_fl_merge; /* Defined in freelist.c. */ static char *markhp, *chunk, *limit; -static int gc_subphase; /* Subphase_main, Subphase_weak, Subphase_final */ -#define Subphase_main 10 -#define Subphase_weak 11 -#define Subphase_final 12 +int caml_gc_subphase; /* Subphase_{main,weak1,weak2,final} */ static value *weak_prev; +#ifdef DEBUG +static unsigned long major_gc_counter = 0; +#endif + static void realloc_gray_vals (void) { value *new; @@ -111,9 +112,10 @@ static void start_cycle (void) caml_gc_message (0x01, "Starting new major GC cycle\n", 0); caml_darken_all_roots(); caml_gc_phase = Phase_mark; - gc_subphase = Subphase_main; + caml_gc_subphase = Subphase_main; markhp = NULL; #ifdef DEBUG + ++ major_gc_counter; caml_heap_check (); #endif } @@ -126,6 +128,7 @@ static void mark_slice (intnat work) mlsize_t size, i; caml_gc_message (0x40, "Marking %ld words\n", work); + caml_gc_message (0x40, "Subphase = %ld\n", caml_gc_subphase); gray_vals_ptr = gray_vals_cur; while (work > 0){ if (gray_vals_ptr > gray_vals){ @@ -187,12 +190,12 @@ static void mark_slice (intnat work) chunk = caml_heap_start; markhp = chunk; limit = chunk + Chunk_size (chunk); - }else if (gc_subphase == Subphase_main){ + }else if (caml_gc_subphase == Subphase_main){ /* The main marking phase is over. Start removing weak pointers to dead values. */ - gc_subphase = Subphase_weak; + caml_gc_subphase = Subphase_weak1; weak_prev = &caml_weak_list_head; - }else if (gc_subphase == Subphase_weak){ + }else if (caml_gc_subphase == Subphase_weak1){ value cur, curfield; mlsize_t sz, i; header_t hd; @@ -200,10 +203,6 @@ static void mark_slice (intnat work) cur = *weak_prev; if (cur != (value) NULL){ hd = Hd_val (cur); - if (Color_hd (hd) == Caml_white){ - /* The whole array is dead, remove it from the list. */ - *weak_prev = Field (cur, 0); - }else{ sz = Wosize_hd (hd); for (i = 1; i < sz; i++){ curfield = Field (cur, i); @@ -227,18 +226,36 @@ static void mark_slice (intnat work) } } } + weak_prev = &Field (cur, 0); + work -= Whsize_hd (hd); + }else{ + /* Subphase_weak1 is done. Start removing dead weak arrays. */ + caml_gc_subphase = Subphase_weak2; + weak_prev = &caml_weak_list_head; + } + }else if (caml_gc_subphase == Subphase_weak2){ + value cur; + header_t hd; + + cur = *weak_prev; + if (cur != (value) NULL){ + hd = Hd_val (cur); + if (Color_hd (hd) == Caml_white){ + /* The whole array is dead, remove it from the list. */ + *weak_prev = Field (cur, 0); + }else{ weak_prev = &Field (cur, 0); } - work -= Whsize_hd (hd); + work -= 1; }else{ - /* Subphase_weak is done. Handle finalised values. */ + /* Subphase_weak2 is done. Handle finalised values. */ gray_vals_cur = gray_vals_ptr; caml_final_update (); gray_vals_ptr = gray_vals_cur; - gc_subphase = Subphase_final; + caml_gc_subphase = Subphase_final; } }else{ - Assert (gc_subphase == Subphase_final); + Assert (caml_gc_subphase == Subphase_final); /* Initialise the sweep phase. */ gray_vals_cur = gray_vals_ptr; caml_gc_sweep_hp = caml_heap_start; @@ -352,7 +369,7 @@ intnat caml_major_collection_slice (intnat howmuch) if (p < dp) p = dp; if (p < caml_extra_heap_resources) p = caml_extra_heap_resources; - caml_gc_message (0x40, "allocated_words = %" + caml_gc_message (0x40, "allocated_words = %" ARCH_INTNAT_PRINTF_FORMAT "u\n", caml_allocated_words); caml_gc_message (0x40, "extra_heap_resources = %" diff --git a/byterun/major_gc.h b/byterun/major_gc.h index 5607a2675..5e48e3431 100644 --- a/byterun/major_gc.h +++ b/byterun/major_gc.h @@ -33,6 +33,7 @@ typedef struct { #define Chunk_block(c) (((heap_chunk_head *) (c)) [-1]).block extern int caml_gc_phase; +extern int caml_gc_subphase; extern uintnat caml_allocated_words; extern double caml_extra_heap_resources; extern uintnat caml_dependent_size, caml_dependent_allocated; @@ -41,6 +42,10 @@ extern uintnat caml_fl_size_at_phase_change; #define Phase_mark 0 #define Phase_sweep 1 #define Phase_idle 2 +#define Subphase_main 10 +#define Subphase_weak1 11 +#define Subphase_weak2 12 +#define Subphase_final 13 CAMLextern char *caml_heap_start; extern uintnat total_heap_size; diff --git a/byterun/weak.c b/byterun/weak.c index 1f121af3a..e85468601 100644 --- a/byterun/weak.c +++ b/byterun/weak.c @@ -45,6 +45,24 @@ CAMLprim value caml_weak_create (value len) #define None_val (Val_int(0)) #define Some_tag 0 +static void do_set (value ar, mlsize_t offset, value v) +{ + if (Is_block (v) && Is_young (v)){ + /* modified version of Modify */ + value old = Field (ar, offset); + Field (ar, offset) = v; + if (!(Is_block (old) && Is_young (old))){ + if (caml_weak_ref_table.ptr >= caml_weak_ref_table.limit){ + CAMLassert (caml_weak_ref_table.ptr == caml_weak_ref_table.limit); + caml_realloc_ref_table (&caml_weak_ref_table); + } + *caml_weak_ref_table.ptr++ = &Field (ar, offset); + } + }else{ + Field (ar, offset) = v; + } +} + CAMLprim value caml_weak_set (value ar, value n, value el) { mlsize_t offset = Long_val (n) + 1; @@ -53,22 +71,10 @@ CAMLprim value caml_weak_set (value ar, value n, value el) caml_invalid_argument ("Weak.set"); } if (el != None_val){ - value v; Assert (Wosize_val (el) == 1); - v = Field (el, 0); - if (Is_block (v) && Is_young (v)){ - /* modified version of Modify */ - value old = Field (ar, offset); - Field (ar, offset) = v; - if (!(Is_block (old) && Is_young (old))){ - if (caml_weak_ref_table.ptr >= caml_weak_ref_table.limit){ - CAMLassert (caml_weak_ref_table.ptr == caml_weak_ref_table.limit); - caml_realloc_ref_table (&caml_weak_ref_table); - } - *caml_weak_ref_table.ptr++ = &Field (ar, offset); - } - }else{ - Field (ar, offset) = v; - } + Assert (Wosize_val (el) == 1); + do_set (ar, offset, Field (el, 0)); + }else{ + Field (ar, offset) = caml_weak_none; } return Val_unit; } @@ -149,3 +155,39 @@ CAMLprim value caml_weak_check (value ar, value n) } return Val_bool (Field (ar, offset) != caml_weak_none); } + +CAMLprim value caml_weak_blit (value ars, value ofs, + value ard, value ofd, value len) +{ + mlsize_t offset_s = Long_val (ofs) + 1; + mlsize_t offset_d = Long_val (ofd) + 1; + mlsize_t length = Long_val (len); + long i; + Assert (Is_in_heap (ars)); + Assert (Is_in_heap (ard)); + if (offset_s < 1 || offset_s + length > Wosize_val (ars)){ + caml_invalid_argument ("Weak.blit"); + } + if (offset_d < 1 || offset_d + length > Wosize_val (ard)){ + caml_invalid_argument ("Weak.blit"); + } + if (caml_gc_phase == Phase_mark && caml_gc_subphase == Subphase_weak1){ + for (i = 0; i < length; i++){ + value v = Field (ars, offset_s + i); + if (v != caml_weak_none && Is_block (v) && Is_in_heap (v) + && Is_white_val (v)){ + Field (ars, offset_s + i) = caml_weak_none; + } + } + } + if (offset_d < offset_s){ + for (i = 0; i < length; i++){ + do_set (ard, offset_d + i, Field (ars, offset_s + i)); + } + }else{ + for (i = length - 1; i >= 0; i--){ + do_set (ard, offset_d + i, Field (ars, offset_s + i)); + } + } + return Val_unit; +} diff --git a/stdlib/weak.ml b/stdlib/weak.ml index 317c3729f..bf2899492 100644 --- a/stdlib/weak.ml +++ b/stdlib/weak.ml @@ -26,6 +26,7 @@ external set : 'a t -> int -> 'a option -> unit = "caml_weak_set";; external get: 'a t -> int -> 'a option = "caml_weak_get";; external get_copy: 'a t -> int -> 'a option = "caml_weak_get_copy";; external check: 'a t -> int -> bool = "caml_weak_check";; +external blit: 'a t -> int -> 'a t -> int -> int -> unit = "caml_weak_blit";; let fill ar ofs len x = if ofs < 0 || len < 0 || ofs + len > length ar @@ -37,23 +38,6 @@ let fill ar ofs len x = end ;; -let blit ar1 of1 ar2 of2 len = - if of1 < 0 || of1 + len > length ar1 || of2 < 0 || of2 + len > length ar2 - then raise (Invalid_argument "Weak.blit") - else begin - if of2 > of1 then begin - for i = 0 to len - 1 do - set ar2 (of2 + i) (get ar1 (of1 + i)) - done - end else begin - for i = len - 1 downto 0 do - set ar2 (of2 + i) (get ar1 (of1 + i)) - done - end - end -;; - - (** Weak hash tables *) module type S = sig @@ -83,27 +67,35 @@ module Make (H : Hashtbl.HashedType) : (S with type data = H.t) = struct type t = { mutable table : data weak_t array; - mutable totsize : int; (* sum of the bucket sizes *) - mutable limit : int; (* max ratio totsize/table length *) + mutable hashes : int array array; + mutable limit : int; (* bucket size limit *) + mutable oversize : int; (* number of oversize buckets *) + mutable rover : int; (* for internal bookkeeping *) };; - let get_index t d = (H.hash d land max_int) mod (Array.length t.table);; + let get_index t h = (h land max_int) mod (Array.length t.table);; + + let limit = 7;; + let over_limit = 2;; let create sz = let sz = if sz < 7 then 7 else sz in let sz = if sz > Sys.max_array_length then Sys.max_array_length else sz in { table = Array.create sz emptybucket; - totsize = 0; - limit = 3; + hashes = Array.create sz [| |]; + limit = limit; + oversize = 0; + rover = 0; };; let clear t = for i = 0 to Array.length t.table - 1 do t.table.(i) <- emptybucket; + t.hashes.(i) <- [| |]; done; - t.totsize <- 0; - t.limit <- 3; + t.limit <- limit; + t.oversize <- 0; ;; let fold f t init = @@ -126,85 +118,139 @@ module Make (H : Hashtbl.HashedType) : (S with type data = H.t) = struct Array.iter (iter_bucket 0) t.table ;; + let rec count_bucket i b accu = + if i >= length b then accu else + count_bucket (i+1) b (accu + (if check b i then 1 else 0)) + ;; + let count t = - let rec count_bucket i b accu = - if i >= length b then accu else - count_bucket (i+1) b (accu + (if check b i then 1 else 0)) - in Array.fold_right (count_bucket 0) t.table 0 ;; - let next_sz n = min (3*n/2 + 3) (Sys.max_array_length - 1);; + let next_sz n = min (3 * n / 2 + 3) Sys.max_array_length;; + let prev_sz n = ((n - 3) * 2 + 2) / 3;; + + let test_shrink_bucket t = + let bucket = t.table.(t.rover) in + let hbucket = t.hashes.(t.rover) in + let len = length bucket in + let prev_len = prev_sz len in + let live = count_bucket 0 bucket 0 in + if live <= prev_len then begin + let rec loop i j = + if j > prev_len then begin + if check bucket i then loop (i + 1) j + else if check bucket j then begin + blit bucket j bucket i 1; + hbucket.(i) <- hbucket.(j); + loop (i + 1) (j - 1); + end else loop i (j - 1); + end; + in + loop 0 (length bucket - 1); + if prev_len = 0 then begin + t.table.(t.rover) <- emptybucket; + t.hashes.(t.rover) <- [| |]; + end else begin + Obj.truncate (Obj.repr bucket) (prev_len + 1); + Obj.truncate (Obj.repr hbucket) prev_len; + end; + if len > t.limit && prev_len <= t.limit then t.oversize <- t.oversize - 1; + end; + t.rover <- (t.rover + 1) mod (Array.length t.table); + ;; let rec resize t = let oldlen = Array.length t.table in let newlen = next_sz oldlen in if newlen > oldlen then begin let newt = create newlen in - newt.limit <- t.limit + 100; (* prevent resizing of newt *) fold (fun d () -> add newt d) t (); - (* assert Array.length newt.table = newlen; *) t.table <- newt.table; - (* t.limit <- t.limit + 2; -- performance bug *) + t.hashes <- newt.hashes; + t.limit <- newt.limit; + t.oversize <- newt.oversize; + t.rover <- t.rover mod Array.length newt.table; + end else begin + t.limit <- max_int; (* maximum size already reached *) + t.oversize <- 0; end - and add_aux t d index = + and add_aux t d h index = let bucket = t.table.(index) in + let hashes = t.hashes.(index) in let sz = length bucket in let rec loop i = if i >= sz then begin - let newsz = min (sz + 3) (Sys.max_array_length - 1) in - if newsz <= sz then failwith "Weak.Make : hash bucket cannot grow more"; + let newsz = min (3 * sz / 2 + 3) (Sys.max_array_length - 1) in + if newsz <= sz then failwith "Weak.Make: hash bucket cannot grow more"; let newbucket = weak_create newsz in + let newhashes = Array.make newsz 0 in blit bucket 0 newbucket 0 sz; - set newbucket i (Some d); + Array.blit hashes 0 newhashes 0 sz; + set newbucket sz (Some d); + newhashes.(sz) <- h; t.table.(index) <- newbucket; - t.totsize <- t.totsize + (newsz - sz); - if t.totsize > t.limit * Array.length t.table then resize t; + t.hashes.(index) <- newhashes; + if sz <= t.limit && newsz > t.limit then begin + t.oversize <- t.oversize + 1; + for i = 0 to over_limit do test_shrink_bucket t done; + end; + if t.oversize > Array.length t.table / over_limit then resize t; end else begin if check bucket i - then loop (i+1) - else set bucket i (Some d) - end + then loop (i + 1) + else begin + set bucket i (Some d); + hashes.(i) <- h; + end; + end; in loop 0; - and add t d = add_aux t d (get_index t d) + and add t d = + let h = H.hash d in + add_aux t d h (get_index t h); ;; let find_or t d ifnotfound = - let index = get_index t d in + let h = H.hash d in + let index = get_index t h in let bucket = t.table.(index) in + let hashes = t.hashes.(index) in let sz = length bucket in let rec loop i = - if i >= sz then ifnotfound index - else begin + if i >= sz then ifnotfound h index + else if h = hashes.(i) then begin match get_copy bucket i with | Some v when H.equal v d -> begin match get bucket i with | Some v -> v - | None -> loop (i+1) + | None -> loop (i + 1) end - | _ -> loop (i+1) - end + | _ -> loop (i + 1) + end else loop (i + 1) in loop 0 ;; - let merge t d = find_or t d (fun index -> add_aux t d index; d);; + let merge t d = find_or t d (fun h index -> add_aux t d h index; d);; - let find t d = find_or t d (fun index -> raise Not_found);; + let find t d = find_or t d (fun h index -> raise Not_found);; let find_shadow t d iffound ifnotfound = - let index = get_index t d in + let h = H.hash d in + let index = get_index t h in let bucket = t.table.(index) in + let hashes = t.hashes.(index) in let sz = length bucket in let rec loop i = - if i >= sz then ifnotfound else begin + if i >= sz then ifnotfound + else if h = hashes.(i) then begin match get_copy bucket i with | Some v when H.equal v d -> iffound bucket i - | _ -> loop (i+1) - end + | _ -> loop (i + 1) + end else loop (i + 1) in loop 0 ;; @@ -214,20 +260,22 @@ module Make (H : Hashtbl.HashedType) : (S with type data = H.t) = struct let mem t d = find_shadow t d (fun w i -> true) false;; let find_all t d = - let index = get_index t d in + let h = H.hash d in + let index = get_index t h in let bucket = t.table.(index) in + let hashes = t.hashes.(index) in let sz = length bucket in let rec loop i accu = if i >= sz then accu - else begin + else if h = hashes.(i) then begin match get_copy bucket i with | Some v when H.equal v d -> begin match get bucket i with - | Some v -> loop (i+1) (v::accu) - | None -> loop (i+1) accu + | Some v -> loop (i + 1) (v :: accu) + | None -> loop (i + 1) accu end - | _ -> loop (i+1) accu - end + | _ -> loop (i + 1) accu + end else loop (i + 1) accu in loop 0 [] ;; diff --git a/test/Makefile b/test/Makefile index f8219eeb5..46fb2acb8 100644 --- a/test/Makefile +++ b/test/Makefile @@ -28,7 +28,7 @@ CODERUNPARAMS=OCAMLRUNPARAM='o=100' BYTE_EXE=fib.byt takc.byt taku.byt sieve.byt quicksort.byt quicksort.fast.byt \ fft.byt fft.fast.byt soli.byt soli.fast.byt boyer.byt kb.byt \ nucleic.byt bdd.byt hamming.byt sorts.byt \ - almabench.byt almabench.fast.byt + almabench.byt almabench.fast.byt weaktest.byt CODE_EXE=$(BYTE_EXE:.byt=.out) diff --git a/test/Results/weaktest.out b/test/Results/weaktest.out new file mode 100644 index 000000000..2ae28399f --- /dev/null +++ b/test/Results/weaktest.out @@ -0,0 +1 @@ +pass diff --git a/test/weaktest.ml b/test/weaktest.ml new file mode 100644 index 000000000..92ab5576d --- /dev/null +++ b/test/weaktest.ml @@ -0,0 +1,73 @@ +(* $Id$ *) + +let debug = false;; + +open Printf;; + +module Hashed = struct + type t = string list;; + let equal x y = + eprintf "equal: %s / %s\n" (List.hd x) (List.hd y); + x = y + ;; + let hash x = Hashtbl.hash (List.hd x);; +end;; + +module HT = Weak.Make (Hashed);; + +let tbl = HT.create 7;; + +let r = ref [];; + +let bunch = + if Array.length Sys.argv < 2 + then 10000 + else int_of_string Sys.argv.(1) +;; + +Random.init 314;; + +let random_string n = + let result = String.create n in + for i = 0 to n - 1 do + result.[i] <- Char.chr (32 + Random.int 95); + done; + result +;; + +let added = ref 0;; +let mistakes = ref 0;; + +let print_status () = + let (len, entries, sumbuck, buckmin, buckmed, buckmax) = HT.stats tbl in + if entries > bunch * (!added + 1) then begin + if debug then begin + printf "\n===================\n"; + printf "len = %d\n" len; + printf "entries = %d\n" entries; + printf "sum of bucket sizes = %d\n" sumbuck; + printf "min bucket = %d\n" buckmin; + printf "med bucket = %d\n" buckmed; + printf "max bucket = %d\n" buckmax; + printf "GC count = %d\n" (Gc.quick_stat ()).Gc.major_collections; + flush stdout; + end; + incr mistakes; + end; + added := 0; +;; + +Gc.create_alarm print_status;; + +for j = 0 to 99 do + r := []; + incr added; + + for i = 1 to bunch do + let c = random_string 7 in + r := c :: !r; + HT.add tbl !r; + done; +done;; + +if !mistakes < 5 then printf "pass\n" else printf "fail\n";; |