123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718(************************************************************************)(* * The Coq Proof Assistant / The Coq Development Team *)(* v * Copyright INRIA, CNRS and contributors *)(* <O___,, * (see version control and CREDITS file for authors & dates) *)(* \VV/ **************************************************************)(* // * This file is distributed under the terms of the *)(* * GNU Lesser General Public License Version 2.1 *)(* * (see LICENSE file for the text of the license) *)(************************************************************************)letenable_profile=falseletword_length=Sys.word_size/8letfloat_of_timet=float_of_intt/.100.lettime_of_floatf=int_of_float(f*.100.)letget_time()=time_of_float(Sys.time())(* Since ocaml 3.01, gc statistics are in float *)letget_alloc()=(* If you are unlucky, a minor collection can occur between the *)(* measurements and produces allocation; we trigger a minor *)(* collection in advance to be sure the measure is not corrupted *)Gc.minor();Gc.allocated_bytes()(* Rem: overhead was 16 bytes in ocaml 3.00 (long int) *)(* Rem: overhead is 100 bytes in ocaml 3.01 (double) *)letget_alloc_overhead=letmark1=get_alloc()inletmark2=get_alloc()inletmark3=get_alloc()in(* If you are unlucky, a major collection can occur between the *)(* measurements; with two measures the risk decreases *)min(mark2-.mark1)(mark3-.mark2)letlast_alloc=ref0.0(* set by init_profile () *)letspent_alloc()=letnow=get_alloc()inletbefore=!last_allocinlast_alloc:=now;now-.before-.get_alloc_overhead(* Profile records *)typeprofile_key={mutableowntime:int;mutabletottime:int;mutableownalloc:float;mutabletotalloc:float;mutableowncount:int;mutableintcount:int;mutableimmcount:int;}letcreate_record()={owntime=0;tottime=0;ownalloc=0.0;totalloc=0.0;owncount=0;intcount=0;immcount=0}letajoute_totallocedw=e.totalloc<-e.totalloc+.dwletajoute_ownallocedw=e.ownalloc<-e.ownalloc+.dwletreset_record(n,e)=e.owntime<-0;e.tottime<-0;e.ownalloc<-0.0;e.totalloc<-0.0;e.owncount<-0;e.intcount<-0;e.immcount<-0(* Profile tables *)letprof_table=ref[]letstack=ref[]letinit_time=ref0letinit_alloc=ref0.0letreset_profile()=List.iterreset_record!prof_tableletinit_profile()=(* We test enable_profile as a way to support declaring profiled
functions in plugins *)if!prof_table<>[]||enable_profilethenbeginletoutside=create_record()instack:=[outside];last_alloc:=get_alloc();init_alloc:=!last_alloc;init_time:=get_time();outside.tottime<--!init_time;outside.owntime<--!init_timeendletajouteno=o.owntime<-o.owntime+n.owntime;o.tottime<-o.tottime+n.tottime;ajoute_ownallocon.ownalloc;ajoute_totallocon.totalloc;o.owncount<-o.owncount+n.owncount;o.intcount<-o.intcount+n.intcount;o.immcount<-o.immcount+n.immcountletajoute_to_list((name,n)ase)l=tryajouten(List.assocnamel);lwithNot_found->e::lletmagic=1249letmerge_profilefilename(curr_table,curr_outside,curr_totalasnew_data)=let(old_table,old_outside,old_total)=tryletc=open_infilenameinifinput_binary_intc<>magicthenPrintf.printf"Incompatible recording file: %s\n"filename;letold_data=input_valuecinclose_inc;old_datawithSys_errormsg->(Printf.printf"Unable to open %s: %s\n"filenamemsg;new_data)inletupdated_data=letupdated_table=List.fold_rightajoute_to_listcurr_tableold_tableinajoutecurr_outsideold_outside;ajoutecurr_totalold_total;(updated_table,old_outside,old_total)inbegin(tryletc=open_out_gen[Open_creat;Open_wronly;Open_trunc;Open_binary]0o644filenameinoutput_binary_intcmagic;output_valuecupdated_data;close_outcwithSys_error_->Printf.printf"Unable to create recording file");updated_dataend(************************************************)(* Compute a rough estimation of time overheads *)(* Time and space are not measured in the same way *)(* Byte allocation is an exact number and for long runs, the total
number of allocated bytes may exceed the maximum integer capacity
(2^31 on 32-bits architectures); therefore, allocation is measured
by small steps, total allocations are computed by adding elementary
measures and carries are controlled from step to step *)(* Unix measure of time is approximate and short delays are often
unperceivable; therefore, total times are measured in one (big)
step to avoid rounding errors and to get the best possible
approximation.
Note: Sys.time is the same as:
Unix.(let x = times () in x.tms_utime +. x.tms_stime)
*)(*
---------- start profile for f1
overheadA| ...
---------- [1w1] 1st call to get_time for f1
overheadB| ...
---------- start f1
real 1 | ...
---------- start profile for 1st call to f2 inside f1
overheadA| ...
---------- [2w1] 1st call to get_time for 1st f2
overheadB| ...
---------- start 1st f2
real 2 | ...
---------- end 1st f2
overheadC| ...
---------- [2w1] 2nd call to get_time for 1st f2
overheadD| ...
---------- end profile for 1st f2
real 1 | ...
---------- start profile for 2nd call to f2 inside f1
overheadA| ...
---------- [2'w1] 1st call to get_time for 2nd f2
overheadB| ...
---------- start 2nd f2
real 2' | ...
---------- end 2nd f2
overheadC| ...
---------- [2'w2] 2nd call to get_time for 2nd f2
overheadD| ...
---------- end profile for f2
real 1 | ...
---------- end f1
overheadC| ...
---------- [1w1'] 2nd call to get_time for f1
overheadD| ...
---------- end profile for f1
When profiling f2, overheadB + overheadC should be subtracted from measure
and overheadA + overheadB + overheadC + overheadD should be subtracted from
the amount for f1
Then the relevant overheads are :
"overheadB + overheadC" to be subtracted to the measure of f as many time as f is called and
"overheadA + overheadB + overheadC + overheadD" to be subtracted to
the measure of f as many time as f calls a profiled function (itself
included)
*)letdummy_last_alloc=ref0.0letdummy_spent_alloc()=letnow=get_alloc()inletbefore=!last_allocinlast_alloc:=now;now-.beforeletdummy_fx=xletdummy_stack=ref[create_record()]letdummy_ov=0letloops=10000lettime_overhead_A_D()=lete=create_record()inletbefore=get_time()infor_i=1toloopsdo(* This is a copy of profile1 for overhead estimation *)letdw=dummy_spent_alloc()inmatch!dummy_stackwith[]->assertfalse|p::_->ajoute_ownallocpdw;ajoute_totallocpdw;e.owncount<-e.owncount+1;ifnot(p==e)thenstack:=e::!stack;lettotalloc0=e.totallocinletintcount0=e.intcountinletdt=get_time()-1ine.tottime<-dt+dummy_ov;e.owntime<-e.owntime+e.tottime;ajoute_ownallocpdw;ajoute_totallocpdw;p.owntime<-p.owntime-e.tottime;ajoute_totallocp(e.totalloc-.totalloc0);p.intcount<-p.intcount+e.intcount-intcount0+1;p.immcount<-p.immcount+1;ifnot(p==e)then(match!dummy_stackwith[]->assertfalse|_::s->stack:=s);dummy_last_alloc:=get_alloc()done;letafter=get_time()inletbeforeloop=get_time()infor_i=1toloopsdo()done;letafterloop=get_time()infloat_of_int((after-before)-(afterloop-beforeloop))/.float_of_intloopslettime_overhead_B_C()=letdummy_x=0inletbefore=get_time()infor_i=1toloopsdotrydummy_last_alloc:=get_alloc();let_r=dummy_fdummy_xinlet_dw=dummy_spent_alloc()inlet_dt=get_time()in()withewhenCErrors.noncriticale->assertfalsedone;letafter=get_time()inletbeforeloop=get_time()infor_i=1toloopsdo()done;letafterloop=get_time()infloat_of_int((after-before)-(afterloop-beforeloop))/.float_of_intloopsletcompute_alloclo=lo/.(float_of_intword_length)(************************************************)(* End a profiling session and print the result *)letformat_profile(table,outside,total)=print_newline();Printf.printf"%-23s %9s %9s %10s %10s %10s\n""Function name""Own time""Tot. time""Own alloc""Tot. alloc""Calls ";letl=List.sort(funpp'->(sndp').tottime-(sndp).tottime)tableinList.iter(fun(name,e)->Printf.printf"%-23s %9.2f %9.2f %10.0f %10.0f %6d %6d\n"name(float_of_timee.owntime)(float_of_timee.tottime)(compute_alloce.ownalloc)(compute_alloce.totalloc)e.owncounte.intcount)l;Printf.printf"%-23s %9.2f %9.2f %10.0f %10.0f %6d\n""others"(float_of_timeoutside.owntime)(float_of_timeoutside.tottime)(compute_allocoutside.ownalloc)(compute_allocoutside.totalloc)outside.intcount;(* Here, own contains overhead time/alloc *)Printf.printf"%-23s %9.2f %9.2f %10.0f %10.0f\n""Est. overhead/total"(float_of_timetotal.owntime)(float_of_timetotal.tottime)(compute_alloctotal.ownalloc)(compute_alloctotal.totalloc);Printf.printf"Time in seconds and allocation in words (1 word = %d bytes)\n"word_lengthletrecording_file=ref""letset_recordings=recording_file:=sletadjust_timeov_bcov_ade=letbc_imm=float_of_inte.owncount*.ov_bcinletad_imm=float_of_inte.immcount*.ov_adinletabcd_all=float_of_inte.intcount*.(ov_ad+.ov_bc)in{ewithtottime=e.tottime-int_of_float(abcd_all+.bc_imm);owntime=e.owntime-int_of_float(ad_imm+.bc_imm)}letclose_profileprint=if!prof_table<>[]thenbeginletdw=spent_alloc()inlett=get_time()inmatch!stackwith|[outside]->outside.tottime<-outside.tottime+t;outside.owntime<-outside.owntime+t;ajoute_ownallocoutsidedw;ajoute_totallocoutsidedw;letov_bc=time_overhead_B_C()(* B+C overhead *)inletov_ad=time_overhead_A_D()(* A+D overhead *)inletadjust(n,e)=(n,adjust_timeov_bcov_ade)inletadjtable=List.mapadjust!prof_tableinletadjoutside=adjust_timeov_bcov_adoutsideinlettotalloc=!last_alloc-.!init_allocinlettotal=create_record()intotal.tottime<-outside.tottime;total.totalloc<-totalloc;(* We compute estimations of overhead, put into "own" fields *)total.owntime<-outside.tottime-adjoutside.tottime;total.ownalloc<-totalloc-.outside.totalloc;letcurrent_data=(adjtable,adjoutside,total)inletupdated_data=match!recording_filewith|""->current_data|name->merge_profile!recording_filecurrent_datainifprintthenformat_profileupdated_data;init_profile()|_->failwith"Inconsistency"endletprint_profile()=close_profiletrueletdeclare_profilename=ifname="___outside___"||name="___total___"thenfailwith("Error: "^name^" is a reserved keyword");lete=create_record()inprof_table:=(name,e)::!prof_table;e(******************************)(* Entry points for profiling *)letprofile1efa=letdw=spent_alloc()inmatch!stackwith[]->assertfalse|p::_->(* We add spent alloc since last measure to current caller own/total alloc *)ajoute_ownallocpdw;ajoute_totallocpdw;e.owncount<-e.owncount+1;ifnot(p==e)thenstack:=e::!stack;lettotalloc0=e.totallocinletintcount0=e.intcountinlett=get_time()intrylast_alloc:=get_alloc();letr=fainletdw=spent_alloc()inletdt=get_time()-tine.tottime<-e.tottime+dt;e.owntime<-e.owntime+dt;ajoute_ownallocedw;ajoute_totallocedw;p.owntime<-p.owntime-dt;ajoute_totallocp(e.totalloc-.totalloc0);p.intcount<-p.intcount+e.intcount-intcount0+1;p.immcount<-p.immcount+1;ifnot(p==e)then(match!stackwith[]->assertfalse|_::s->stack:=s);last_alloc:=get_alloc();rwithreraise->letdw=spent_alloc()inletdt=get_time()-tine.tottime<-e.tottime+dt;e.owntime<-e.owntime+dt;ajoute_ownallocedw;ajoute_totallocedw;p.owntime<-p.owntime-dt;ajoute_totallocp(e.totalloc-.totalloc0);p.intcount<-p.intcount+e.intcount-intcount0+1;p.immcount<-p.immcount+1;ifnot(p==e)then(match!stackwith[]->assertfalse|_::s->stack:=s);last_alloc:=get_alloc();raisereraiseletprofile2efab=letdw=spent_alloc()inmatch!stackwith[]->assertfalse|p::_->(* We add spent alloc since last measure to current caller own/total alloc *)ajoute_ownallocpdw;ajoute_totallocpdw;e.owncount<-e.owncount+1;ifnot(p==e)thenstack:=e::!stack;lettotalloc0=e.totallocinletintcount0=e.intcountinlett=get_time()intrylast_alloc:=get_alloc();letr=fabinletdw=spent_alloc()inletdt=get_time()-tine.tottime<-e.tottime+dt;e.owntime<-e.owntime+dt;ajoute_ownallocedw;ajoute_totallocedw;p.owntime<-p.owntime-dt;ajoute_totallocp(e.totalloc-.totalloc0);p.intcount<-p.intcount+e.intcount-intcount0+1;p.immcount<-p.immcount+1;ifnot(p==e)then(match!stackwith[]->assertfalse|_::s->stack:=s);last_alloc:=get_alloc();rwithreraise->letdw=spent_alloc()inletdt=get_time()-tine.tottime<-e.tottime+dt;e.owntime<-e.owntime+dt;ajoute_ownallocedw;ajoute_totallocedw;p.owntime<-p.owntime-dt;ajoute_totallocp(e.totalloc-.totalloc0);p.intcount<-p.intcount+e.intcount-intcount0+1;p.immcount<-p.immcount+1;ifnot(p==e)then(match!stackwith[]->assertfalse|_::s->stack:=s);last_alloc:=get_alloc();raisereraiseletprofile3efabc=letdw=spent_alloc()inmatch!stackwith[]->assertfalse|p::_->(* We add spent alloc since last measure to current caller own/total alloc *)ajoute_ownallocpdw;ajoute_totallocpdw;e.owncount<-e.owncount+1;ifnot(p==e)thenstack:=e::!stack;lettotalloc0=e.totallocinletintcount0=e.intcountinlett=get_time()intrylast_alloc:=get_alloc();letr=fabcinletdw=spent_alloc()inletdt=get_time()-tine.tottime<-e.tottime+dt;e.owntime<-e.owntime+dt;ajoute_ownallocedw;ajoute_totallocedw;p.owntime<-p.owntime-dt;ajoute_totallocp(e.totalloc-.totalloc0);p.intcount<-p.intcount+e.intcount-intcount0+1;p.immcount<-p.immcount+1;ifnot(p==e)then(match!stackwith[]->assertfalse|_::s->stack:=s);last_alloc:=get_alloc();rwithreraise->letdw=spent_alloc()inletdt=get_time()-tine.tottime<-e.tottime+dt;e.owntime<-e.owntime+dt;ajoute_ownallocedw;ajoute_totallocedw;p.owntime<-p.owntime-dt;ajoute_totallocp(e.totalloc-.totalloc0);p.intcount<-p.intcount+e.intcount-intcount0+1;p.immcount<-p.immcount+1;ifnot(p==e)then(match!stackwith[]->assertfalse|_::s->stack:=s);last_alloc:=get_alloc();raisereraiseletprofile4efabcd=letdw=spent_alloc()inmatch!stackwith[]->assertfalse|p::_->(* We add spent alloc since last measure to current caller own/total alloc *)ajoute_ownallocpdw;ajoute_totallocpdw;e.owncount<-e.owncount+1;ifnot(p==e)thenstack:=e::!stack;lettotalloc0=e.totallocinletintcount0=e.intcountinlett=get_time()intrylast_alloc:=get_alloc();letr=fabcdinletdw=spent_alloc()inletdt=get_time()-tine.tottime<-e.tottime+dt;e.owntime<-e.owntime+dt;ajoute_ownallocedw;ajoute_totallocedw;p.owntime<-p.owntime-dt;ajoute_totallocp(e.totalloc-.totalloc0);p.intcount<-p.intcount+e.intcount-intcount0+1;p.immcount<-p.immcount+1;ifnot(p==e)then(match!stackwith[]->assertfalse|_::s->stack:=s);last_alloc:=get_alloc();rwithreraise->letdw=spent_alloc()inletdt=get_time()-tine.tottime<-e.tottime+dt;e.owntime<-e.owntime+dt;ajoute_ownallocedw;ajoute_totallocedw;p.owntime<-p.owntime-dt;ajoute_totallocp(e.totalloc-.totalloc0);p.intcount<-p.intcount+e.intcount-intcount0+1;p.immcount<-p.immcount+1;ifnot(p==e)then(match!stackwith[]->assertfalse|_::s->stack:=s);last_alloc:=get_alloc();raisereraiseletprofile5efabcdg=letdw=spent_alloc()inmatch!stackwith[]->assertfalse|p::_->(* We add spent alloc since last measure to current caller own/total alloc *)ajoute_ownallocpdw;ajoute_totallocpdw;e.owncount<-e.owncount+1;ifnot(p==e)thenstack:=e::!stack;lettotalloc0=e.totallocinletintcount0=e.intcountinlett=get_time()intrylast_alloc:=get_alloc();letr=fabcdginletdw=spent_alloc()inletdt=get_time()-tine.tottime<-e.tottime+dt;e.owntime<-e.owntime+dt;ajoute_ownallocedw;ajoute_totallocedw;p.owntime<-p.owntime-dt;ajoute_totallocp(e.totalloc-.totalloc0);p.intcount<-p.intcount+e.intcount-intcount0+1;p.immcount<-p.immcount+1;ifnot(p==e)then(match!stackwith[]->assertfalse|_::s->stack:=s);last_alloc:=get_alloc();rwithreraise->letdw=spent_alloc()inletdt=get_time()-tine.tottime<-e.tottime+dt;e.owntime<-e.owntime+dt;ajoute_ownallocedw;ajoute_totallocedw;p.owntime<-p.owntime-dt;ajoute_totallocp(e.totalloc-.totalloc0);p.intcount<-p.intcount+e.intcount-intcount0+1;p.immcount<-p.immcount+1;ifnot(p==e)then(match!stackwith[]->assertfalse|_::s->stack:=s);last_alloc:=get_alloc();raisereraiseletprofile6efabcdgh=letdw=spent_alloc()inmatch!stackwith[]->assertfalse|p::_->(* We add spent alloc since last measure to current caller own/total alloc *)ajoute_ownallocpdw;ajoute_totallocpdw;e.owncount<-e.owncount+1;ifnot(p==e)thenstack:=e::!stack;lettotalloc0=e.totallocinletintcount0=e.intcountinlett=get_time()intrylast_alloc:=get_alloc();letr=fabcdghinletdw=spent_alloc()inletdt=get_time()-tine.tottime<-e.tottime+dt;e.owntime<-e.owntime+dt;ajoute_ownallocedw;ajoute_totallocedw;p.owntime<-p.owntime-dt;ajoute_totallocp(e.totalloc-.totalloc0);p.intcount<-p.intcount+e.intcount-intcount0+1;p.immcount<-p.immcount+1;ifnot(p==e)then(match!stackwith[]->assertfalse|_::s->stack:=s);last_alloc:=get_alloc();rwithreraise->letdw=spent_alloc()inletdt=get_time()-tine.tottime<-e.tottime+dt;e.owntime<-e.owntime+dt;ajoute_ownallocedw;ajoute_totallocedw;p.owntime<-p.owntime-dt;ajoute_totallocp(e.totalloc-.totalloc0);p.intcount<-p.intcount+e.intcount-intcount0+1;p.immcount<-p.immcount+1;ifnot(p==e)then(match!stackwith[]->assertfalse|_::s->stack:=s);last_alloc:=get_alloc();raisereraiseletprofile7efabcdghi=letdw=spent_alloc()inmatch!stackwith[]->assertfalse|p::_->(* We add spent alloc since last measure to current caller own/total alloc *)ajoute_ownallocpdw;ajoute_totallocpdw;e.owncount<-e.owncount+1;ifnot(p==e)thenstack:=e::!stack;lettotalloc0=e.totallocinletintcount0=e.intcountinlett=get_time()intrylast_alloc:=get_alloc();letr=fabcdghiinletdw=spent_alloc()inletdt=get_time()-tine.tottime<-e.tottime+dt;e.owntime<-e.owntime+dt;ajoute_ownallocedw;ajoute_totallocedw;p.owntime<-p.owntime-dt;ajoute_totallocp(e.totalloc-.totalloc0);p.intcount<-p.intcount+e.intcount-intcount0+1;p.immcount<-p.immcount+1;ifnot(p==e)then(match!stackwith[]->assertfalse|_::s->stack:=s);last_alloc:=get_alloc();rwithreraise->letdw=spent_alloc()inletdt=get_time()-tine.tottime<-e.tottime+dt;e.owntime<-e.owntime+dt;ajoute_ownallocedw;ajoute_totallocedw;p.owntime<-p.owntime-dt;ajoute_totallocp(e.totalloc-.totalloc0);p.intcount<-p.intcount+e.intcount-intcount0+1;p.immcount<-p.immcount+1;ifnot(p==e)then(match!stackwith[]->assertfalse|_::s->stack:=s);last_alloc:=get_alloc();raisereraiseletprofile8efabcdghij=letdw=spent_alloc()inmatch!stackwith[]->assertfalse|p::_->(* We add spent alloc since last measure to current caller own/total alloc *)ajoute_ownallocpdw;ajoute_totallocpdw;e.owncount<-e.owncount+1;ifnot(p==e)thenstack:=e::!stack;lettotalloc0=e.totallocinletintcount0=e.intcountinlett=get_time()intrylast_alloc:=get_alloc();letr=fabcdghijinletdw=spent_alloc()inletdt=get_time()-tine.tottime<-e.tottime+dt;e.owntime<-e.owntime+dt;ajoute_ownallocedw;ajoute_totallocedw;p.owntime<-p.owntime-dt;ajoute_totallocp(e.totalloc-.totalloc0);p.intcount<-p.intcount+e.intcount-intcount0+1;p.immcount<-p.immcount+1;ifnot(p==e)then(match!stackwith[]->assertfalse|_::s->stack:=s);last_alloc:=get_alloc();rwithreraise->letdw=spent_alloc()inletdt=get_time()-tine.tottime<-e.tottime+dt;e.owntime<-e.owntime+dt;ajoute_ownallocedw;ajoute_totallocedw;p.owntime<-p.owntime-dt;ajoute_totallocp(e.totalloc-.totalloc0);p.intcount<-p.intcount+e.intcount-intcount0+1;p.immcount<-p.immcount+1;ifnot(p==e)then(match!stackwith[]->assertfalse|_::s->stack:=s);last_alloc:=get_alloc();raisereraiseletprint_logical_statsa=let(c,s,d)=CObj.obj_statsainPrintf.printf"Expanded size: %10d (str: %8d) Depth: %6d\n"(s+c)cdletprint_statsa=let(c1,s,d)=CObj.obj_statsainletc2=CObj.sizeainPrintf.printf"Size: %8d (exp: %10d) Depth: %6d\n"c2(s+c1)d(*
let _ = Gc.set { (Gc.get()) with Gc.verbose = 13 }
*)