123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365open!ImportmoduleStable=structmoduleAllocation_policy=structmoduleV1=structtypet=|Next_fit|First_fit|Best_fit[@@derivingbin_io,compare,equal,hash,sexp]endendendincludeCaml.GcmoduleStat=structmoduleT=struct[%%ifocaml_version>=(4,12,0)]typet=Caml.Gc.stat={minor_words:float;promoted_words:float;major_words:float;minor_collections:int;major_collections:int;heap_words:int;heap_chunks:int;live_words:int;live_blocks:int;free_words:int;free_blocks:int;largest_free:int;fragments:int;compactions:int;top_heap_words:int;stack_size:int;forced_major_collections:int}[@@derivingcompare,hash,bin_io,sexp,fields][%%else]typet=Caml.Gc.stat={minor_words:float;promoted_words:float;major_words:float;minor_collections:int;major_collections:int;heap_words:int;heap_chunks:int;live_words:int;live_blocks:int;free_words:int;free_blocks:int;largest_free:int;fragments:int;compactions:int;top_heap_words:int;stack_size:int}[@@derivingcompare,hash,bin_io,sexp,fields][%%endif]endincludeTincludeComparable.Make(T)endmoduleControl=structmoduleT=struct[%%ifocaml_version<(4,08,0)]typet=Caml.Gc.control={mutableminor_heap_size:int;mutablemajor_heap_increment:int;mutablespace_overhead:int;mutableverbose:int;mutablemax_overhead:int;mutablestack_limit:int;mutableallocation_policy:int;window_size:int}[@@derivingcompare,bin_io,sexp,fields][%%else][@@@ocaml.warning"-3"]typet=Caml.Gc.control={mutableminor_heap_size:int;mutablemajor_heap_increment:int;mutablespace_overhead:int;mutableverbose:int;mutablemax_overhead:int;mutablestack_limit:int;mutableallocation_policy:int;window_size:int;custom_major_ratio:int;custom_minor_ratio:int;custom_minor_max_size:int}[@@derivingcompare,bin_io,sexp,fields][%%endif]endincludeTincludeComparable.Make(T)endmoduleAllocation_policy=structincludeStable.Allocation_policy.V1letto_int=function|Next_fit->0|First_fit->1|Best_fit->2;;end[%%ifocaml_version<(4,08,0)]lettune?logger?minor_heap_size?major_heap_increment?space_overhead?verbose?max_overhead?stack_limit?allocation_policy?window_size()=letold_control_params=get()inletfoptto_stringfield=letold_value=Field.getfieldold_control_paramsinmatchoptwith|None->old_value|Somenew_value->Option.iterlogger~f:(funf->Printf.ksprintff"Gc.Control.%s: %s -> %s"(Field.namefield)(to_stringold_value)(to_stringnew_value));new_valueinletallocation_policy=Option.mapallocation_policy~f:Allocation_policy.to_intinletnew_control_params=Control.Fields.map~minor_heap_size:(fminor_heap_sizestring_of_int)~major_heap_increment:(fmajor_heap_incrementstring_of_int)~space_overhead:(fspace_overheadstring_of_int)~verbose:(fverbosestring_of_int)~max_overhead:(fmax_overheadstring_of_int)~stack_limit:(fstack_limitstring_of_int)~allocation_policy:(fallocation_policystring_of_int)~window_size:(fwindow_sizestring_of_int)insetnew_control_params;;[%%else]lettune?logger?minor_heap_size?major_heap_increment?space_overhead?verbose?max_overhead?stack_limit?allocation_policy?window_size?custom_major_ratio?custom_minor_ratio?custom_minor_max_size()=letold_control_params=get()inletfoptto_stringfield=letold_value=Field.getfieldold_control_paramsinmatchoptwith|None->old_value|Somenew_value->Option.iterlogger~f:(funf->Printf.ksprintff"Gc.Control.%s: %s -> %s"(Field.namefield)(to_stringold_value)(to_stringnew_value));new_valueinletallocation_policy=Option.mapallocation_policy~f:Allocation_policy.to_intinletnew_control_params=Control.Fields.map~minor_heap_size:(fminor_heap_sizestring_of_int)~major_heap_increment:(fmajor_heap_incrementstring_of_int)~space_overhead:(fspace_overheadstring_of_int)~verbose:(fverbosestring_of_int)~max_overhead:(fmax_overheadstring_of_int)~stack_limit:(fstack_limitstring_of_int)~allocation_policy:(fallocation_policystring_of_int)~window_size:(fwindow_sizestring_of_int)~custom_major_ratio:(fcustom_major_ratiostring_of_int)~custom_minor_ratio:(fcustom_minor_ratiostring_of_int)~custom_minor_max_size:(fcustom_minor_max_sizestring_of_int)insetnew_control_params;;[%%endif]letdisable_compaction?logger~allocation_policy()=letallocation_policy=matchallocation_policywith|`Don't_change->None|`Set_topolicy->Somepolicyin(* The value 1_000_000, according to
http://caml.inria.fr/pub/docs/manual-ocaml-4.02/libref/Gc.html
will disable compactions.
*)tune?logger?allocation_policy~max_overhead:1_000_000();;externalminor_words:unit->int="core_kernel_gc_minor_words"externalmajor_words:unit->int="core_kernel_gc_major_words"[@@noalloc]externalpromoted_words:unit->int="core_kernel_gc_promoted_words"[@@noalloc]externalminor_collections:unit->int="core_kernel_gc_minor_collections"[@@noalloc]externalmajor_collections:unit->int="core_kernel_gc_major_collections"[@@noalloc]externalheap_words:unit->int="core_kernel_gc_heap_words"[@@noalloc]externalheap_chunks:unit->int="core_kernel_gc_heap_chunks"[@@noalloc]externalcompactions:unit->int="core_kernel_gc_compactions"[@@noalloc]externaltop_heap_words:unit->int="core_kernel_gc_top_heap_words"[@@noalloc]externalmajor_plus_minor_words:unit->int="core_kernel_gc_major_plus_minor_words"externalallocated_words:unit->int="core_kernel_gc_allocated_words"letzero=Sys.opaque_identity(int_of_string"0")(* The compiler won't optimize int_of_string away so it won't
perform constant folding below. *)letreckeep_aliveo=ifzero<>0thenkeep_alive(Sys.opaque_identityo)moduleFor_testing=structletprepare_heap_to_count_minor_allocation()=(* We call [minor] to empty the minor heap, so that our allocation is unlikely to
trigger a minor gc. *)minor();(* We allocate two words in case the [Gc.minor] finishes a major gc cycle, in which
case it requests a minor gc to occur at the next minor allocation. We don't want
the subsequent minor allocation to trigger a minor GC, because there is a bug
(https://github.com/ocaml/ocaml/issues/7798) in the OCaml runtime that double
counts [Gc.minor_words] in that case. *)ignore(Sys.opaque_identity(ref(Sys.opaque_identity1)):intref);;(* We disable inlining for this function so the GC stats and the call to [f] are never
rearranged. *)let[@cold]measure_internal~on_resultf=letminor_words_before=minor_words()inletmajor_words_before=major_words()in(* We wrap [f ()] with [Sys.opaque_identity] to prevent the return value from being
optimized away. *)letx=Sys.opaque_identity(f())inletminor_words_after=minor_words()inletmajor_words_after=major_words()inletmajor_words_allocated=major_words_after-major_words_beforeinletminor_words_allocated=minor_words_after-minor_words_beforeinon_result~major_words_allocated~minor_words_allocatedx;;letis_zero_alloc(typea)(f:unit->a)=(* Instead of using [Allocation_report.measure], and matching on the result, we use
this construction, in order to have [is_zero_alloc] not allocate itself. This
enables [is_zero_alloc] to be used in a nested way.
This also means we cannot call [prepare_heap_to_count_minor_allocation]. This is
okay, since we do not need a precise count, we only need to check if the count is
zero or not. *)measure_internalf~on_result:(fun~major_words_allocated~minor_words_allocatedvalue->ignore(Sys.opaque_identityvalue:a);major_words_allocated==0&&minor_words_allocated==0);;moduleAllocation_report=structtypet={major_words_allocated:int;minor_words_allocated:int}letcreate~major_words_allocated~minor_words_allocated={major_words_allocated;minor_words_allocated};;endletmeasure_allocationf=prepare_heap_to_count_minor_allocation();measure_internalf~on_result:(fun~major_words_allocated~minor_words_allocatedx->x,Allocation_report.create~major_words_allocated~minor_words_allocated);;endmoduleExpert=structletadd_finalizerxf=tryCaml.Gc.finalise(funx->Exn.handle_uncaught_and_exit(fun()->fx))xwith|Invalid_argument_->(* The type of add_finalizer ensures that the only possible failure
is due to [x] being static data. In this case, we simply drop the
finalizer since static data would never have been collected by the
GC anyway. *)();;(* [add_finalizer_exn] is the same as [add_finalizer]. However, their types in
core_gc.mli are different, and the type of [add_finalizer] guarantees that it always
receives a heap block, which ensures that it will not raise, while
[add_finalizer_exn] accepts any type, and so may raise. *)letadd_finalizer_exnxf=tryCaml.Gc.finalise(funx->Exn.handle_uncaught_and_exit(fun()->fx))xwith|Invalid_argument_->ignore(Heap_block.createx:_Heap_block.toption);(* If [Heap_block.create] succeeds then [x] is static data and so
we can simply drop the finaliser. *)();;letadd_finalizer_lastxf=tryCaml.Gc.finalise_last(fun()->Exn.handle_uncaught_and_exitf)xwith|Invalid_argument_->(* The type of add_finalizer_last ensures that the only possible failure
is due to [x] being static data. In this case, we simply drop the
finalizer since static data would never have been collected by the
GC anyway. *)();;letadd_finalizer_last_exnxf=tryCaml.Gc.finalise_last(fun()->Exn.handle_uncaught_and_exitf)xwith|Invalid_argument_->ignore(Heap_block.createx:_Heap_block.toption);(* If [Heap_block.create] succeeds then [x] is static data and so
we can simply drop the finaliser. *)();;letfinalize_release=Caml.Gc.finalise_releasemoduleAlarm=structtypet=alarmletsexp_of_t_="<gc alarm>"|>[%sexp_of:string]letcreatef=create_alarm(fun()->Exn.handle_uncaught_and_exitf)letdelete=delete_alarmendend