feat: use AList for tick callbacks; emit GC events on tick

it's cleaner to emit GC events on ticks rather than on GC,
because it avoids both spamming if the GC is very active, and
emitting nothing when there are few allocations.
This commit is contained in:
Simon Cruanes 2023-12-20 15:46:39 -05:00
parent 2ac799f10d
commit bd8b483e81
No known key found for this signature in database
GPG key ID: EBFFF6F283F3A2B4
3 changed files with 13 additions and 13 deletions

View file

@ -257,7 +257,7 @@ module type EMITTER = sig
val push_logs : Logs.resource_logs list -> unit val push_logs : Logs.resource_logs list -> unit
val set_on_tick_callbacks : (unit -> unit) list ref -> unit val set_on_tick_callbacks : (unit -> unit) AList.t -> unit
val tick : unit -> unit val tick : unit -> unit
@ -288,7 +288,7 @@ let mk_emitter ~stop ~(config : Config.t) () : (module EMITTER) =
let batch_logs : Logs.resource_logs list Batch.t = let batch_logs : Logs.resource_logs list Batch.t =
Batch.make ?batch:config.batch_logs ?timeout () Batch.make ?batch:config.batch_logs ?timeout ()
let on_tick_cbs_ = Atomic.make (ref []) let on_tick_cbs_ = Atomic.make (AList.make ())
let set_on_tick_callbacks = Atomic.set on_tick_cbs_ let set_on_tick_callbacks = Atomic.set on_tick_cbs_
@ -384,7 +384,7 @@ let mk_emitter ~stop ~(config : Config.t) () : (module EMITTER) =
with e -> with e ->
Printf.eprintf "on tick callback raised: %s\n" Printf.eprintf "on tick callback raised: %s\n"
(Printexc.to_string e)) (Printexc.to_string e))
!(Atomic.get on_tick_cbs_); (AList.get @@ Atomic.get on_tick_cbs_);
() ()
(* thread that calls [tick()] regularly, to help enforce timeouts *) (* thread that calls [tick()] regularly, to help enforce timeouts *)

View file

@ -431,15 +431,14 @@ let create_backend ?(stop = Atomic.make false)
ret ()); ret ());
} }
let on_tick_cbs_ = Atomic.make (ref []) let on_tick_cbs_ = Atomic.make (AList.make ())
let set_on_tick_callbacks = Atomic.set on_tick_cbs_ let set_on_tick_callbacks = Atomic.set on_tick_cbs_
let tick () = let tick () =
sample_gc_metrics_if_needed (); sample_gc_metrics_if_needed ();
Backend_impl.send_event backend Event.E_tick; Backend_impl.send_event backend Event.E_tick;
let l = Atomic.get on_tick_cbs_ in List.iter (fun f -> f ()) (AList.get @@ Atomic.get on_tick_cbs_)
List.iter (fun f -> f ()) !l
let cleanup () = Backend_impl.shutdown backend let cleanup () = Backend_impl.shutdown backend
end in end in

View file

@ -86,7 +86,7 @@ module Collector = struct
(** Should be called regularly for background processing, (** Should be called regularly for background processing,
timeout checks, etc. *) timeout checks, etc. *)
val set_on_tick_callbacks : (unit -> unit) list ref -> unit val set_on_tick_callbacks : (unit -> unit) AList.t -> unit
(** Give the collector the list of callbacks to be executed (** Give the collector the list of callbacks to be executed
when [tick()] is called. Each such callback should be short and when [tick()] is called. Each such callback should be short and
reentrant. Depending on the collector's implementation, it might be reentrant. Depending on the collector's implementation, it might be
@ -162,7 +162,7 @@ module Collector = struct
(* hidden *) (* hidden *)
open struct open struct
let on_tick_cbs_ = ref [] let on_tick_cbs_ = AList.make ()
let backend : backend option ref = ref None let backend : backend option ref = ref None
end end
@ -198,7 +198,7 @@ module Collector = struct
let[@inline] rand_bytes_8 () = !Rand_bytes.rand_bytes_8 () let[@inline] rand_bytes_8 () = !Rand_bytes.rand_bytes_8 ()
let on_tick f = on_tick_cbs_ := f :: !on_tick_cbs_ let[@inline] on_tick f = AList.add on_tick_cbs_ f
(** Do background work. Call this regularly if the collector doesn't (** Do background work. Call this regularly if the collector doesn't
already have a ticker thread or internal timer. *) already have a ticker thread or internal timer. *)
@ -1138,7 +1138,8 @@ end
These metrics are emitted after each GC collection. *) These metrics are emitted after each GC collection. *)
module GC_metrics : sig module GC_metrics : sig
val basic_setup : unit -> unit val basic_setup : unit -> unit
(** Setup a hook that will emit GC statistics regularly *) (** Setup a hook that will emit GC statistics on every tick (assuming
a ticker thread) *)
val get_runtime_attributes : unit -> Span.key_value list val get_runtime_attributes : unit -> Span.key_value list
(** Get OCaml name and version runtime attributes *) (** Get OCaml name and version runtime attributes *)
@ -1158,13 +1159,13 @@ end = struct
let get_runtime_attributes () = Lazy.force runtime_attributes let get_runtime_attributes () = Lazy.force runtime_attributes
let basic_setup () = let basic_setup () =
(* emit metrics when GC is called *) let on_tick () =
let on_gc () =
match Collector.get_backend () with match Collector.get_backend () with
| None -> () | None -> ()
| Some (module C) -> C.signal_emit_gc_metrics () | Some (module C) -> C.signal_emit_gc_metrics ()
in in
ignore (Gc.create_alarm on_gc : Gc.alarm)
Collector.on_tick on_tick
let bytes_per_word = Sys.word_size / 8 let bytes_per_word = Sys.word_size / 8