12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061openCoretypet=|Online_profiler|Offline_profiler|Any_profilerletnow_no_calibrate()=Time_stamp_counter.now()|>Time_stamp_counter.to_time_ns~calibrator:(forceTime_stamp_counter.calibrator)let%bench"now_no_calibrate"=now_no_calibrate()(* When we last ran the slow tasks *)letlast_slow_tasks=ref(now_no_calibrate())(* NB: Time_stamp_counter calibrates at startup *)letslow_tasks:(t*(unit->unit))listref=ref[]letslow_tasks_every_ns=1_000_000_000letadd_slow_taskkindf=slow_tasks:=(kind,f)::!slow_taskslet()=add_slow_taskAny_profiler(fun()->Time_stamp_counter.Calibrator.calibrate(forceTime_stamp_counter.calibrator))letmaybe_do_slow_tasks'kindnowreluctance=(* We don't want to pay for a [now] call to work out whether we should do slow tasks.
If Time_stamp_counter gets so far out of sync with reality that the value below
is not good enough to compare with values on the order of one second, then we have
bigger problems, not least because Time_stamp_counter's EWMA isn't going to catch up
quickly enough for the next measurement to be good. *)letdiff=Time_ns.diffnow!last_slow_tasks|>Time_ns.Span.to_int_ns|>absinifdiff>(slow_tasks_every_ns*reluctance)thenbeginList.iter!slow_tasks~f:(fun(orig_kind,g)->iforig_kind=kind||orig_kind=Any_profilertheng());last_slow_tasks:=nowendletnowkind~reluctance()=letx=now_no_calibrate()inmaybe_do_slow_tasks'kindxreluctance;(* It is OK to take the value we were given /before/ calibration as "now" because
Time_stamp_counter provides monotonicity and smoothness: it won't jump. *)xletmaybe_do_slow_taskskind~reluctance=maybe_do_slow_tasks'kind(now_no_calibrate())reluctancelet%bench"now"=nowAny_profiler~reluctance:1()let%bench"maybe_do_slow_tasks (r=4)"=maybe_do_slow_tasksAny_profiler~reluctance:4