123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194(*****************************************************************************)(* *)(* Open Source License *)(* Copyright (c) 2019,2020 DaiLambda, Inc. <contact@dailambda.jp> *)(* *)(* Permission is hereby granted, free of charge, to any person obtaining a *)(* copy of this software and associated documentation files (the "Software"),*)(* to deal in the Software without restriction, including without limitation *)(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *)(* and/or sell copies of the Software, and to permit persons to whom the *)(* Software is furnished to do so, subject to the following conditions: *)(* *)(* The above copyright notice and this permission notice shall be included *)(* in all copies or substantial portions of the Software. *)(* *)(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*)(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *)(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *)(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*)(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *)(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *)(* DEALINGS IN THE SOFTWARE. *)(* *)(*****************************************************************************)(* Node cache with some easy heuristics *)openUtilsopenArray.Syntaxtypestat={ever_hit:int;ever_added:int}typeconfig={threshold_at_shrink:int(* max elems survive each shrink *);threshold_absolute:int(* max elems of any time *);shrink_ratio:float(* how much we shrink *)}letconfig_disabled={threshold_at_shrink=0;threshold_absolute=0;shrink_ratio=0.0}moduleMake(Key:sigtypetvalequal:t->t->boolvalhash:t->intend)=structmoduleTbl=Hashtbl.Make(structtypet=Key.tletequal=Key.equallethash=Key.hashend)letvalidate_configc=assert(c.threshold_at_shrink<=c.threshold_absolute&&0.<=c.shrink_ratio&&c.shrink_ratio<=1.0)type'at={tbl:(Key.t,('a*intref))Hashtbl.t(* int ref for the score.
Size: 16 words, 128bytes in 64bit arch
for (Hash.t * (Index.t * int ref)) *);counters:intarray(* counter.(i) : sum of entries that i equals the value of int ref in tbl *);mutablestat:stat;config:config}letis_disabledt=t.config.threshold_absolute=0letget_statt=t.statletmax_cntr=65536letincr_countersti=t.counters.!(i)<-t.counters.!(i)+1letdecr_countersti=t.counters.!(i)<-t.counters.!(i)-1letcreateconfig=validate_configconfig;{tbl=Hashtbl.create0;counters=Array.make(max_cntr+1)0;stat={ever_hit=0;ever_added=0};config}letsizet=Hashtbl.lengtht.tblletupdate_countertcntrn=leti=!cntrindecr_countersti;cntr:=n;incr_counterstnletfind_opttnh=ifis_disabledtthenNoneelsematchHashtbl.find_optt.tblnhwith|None->None|Some(i,cntr)->letcnt=Int.minmax_cntr(!cntr+256)inupdate_countertcntrcnt;(* completely no logical idea *)t.stat<-{t.statwithever_hit=t.stat.ever_hit+1};Someiletpp_statppft=Format.fprintfppf"size=%d added=%d hit=%d ratio=%.2f"(sizet)t.stat.ever_addedt.stat.ever_hit(floatt.stat.ever_hit/.float(t.stat.ever_added+t.stat.ever_hit))letshrink'tthreshold=letround_up_pow2v=letv=v-1inletv=vlor(vlsr1)inletv=vlor(vlsr2)inletv=vlor(vlsr4)inletv=vlor(vlsr8)inletv=vlor(vlsr16)inv+1inletlog2_of_pow2v=(* log2 using de Bruijn sequence
Assume v is a power of 2 *)[|0;1;28;2;29;14;24;3;30;22;20;15;25;17;4;8;31;27;13;23;21;19;16;7;26;12;18;6;11;5;10;9|].((v*0x077cb531)lsr27land0b11111)inletslide_arrayaryn=letrecslidearyni=ifi+n<Array.lengtharythen(ary.!(i)<-ary.!(i+n);slidearyn(i+1))elseifi<Array.lengtharythen(ary.!(i)<-0;slidearyn(i+1))else(ary.!(0)<-0)inslidearyn0in(* Slide array [counters] until the sum of counters descreses n, a power of 2 *)letslide_counterstn=letrecftnim=ifn<=mtheni-1elseletm=m+t.counters.(i)inftn(i+1)minletslide_i=ftn00inifslide_i=0then0elseletslide_i=round_up_pow2slide_iinlet()=slide_arrayt.countersslide_iinlog2_of_pow2slide_i+1inletsz=sizetinifsz>thresholdthenbeginletgoal=int_of_float@@floatthreshold*.t.config.shrink_ratioinLog.notice"node_cache shrinking from %d to %d"szgoal;ifEnvconf.use_reachable_wordsthenLog.notice"node_cache shrinking from %.2fMB"(Utils.reachable_mbst);(* XXX Very simple inefficient loop. Sometimes overclean things *)letshift_i=slide_counterst(sz-goal)inletf()=Hashtbl.filter_map_inplace(fun_nh(i,cntr)->letn=!cntrlsrshift_iinifn=0thenNoneelseSome(i,refn))t.tblinlet(),secs=with_timefinletsz_current=sizetinassert(sz_current<=goal);Log.notice"node_cache shrank from %d to %d in %a: %a"szsz_currentMtime.Span.ppsecspp_statt;ifEnvconf.use_reachable_wordsthenLog.notice"node_cache shrank to %.2fMB"(Utils.reachable_mbst);endletshrinkt=ifis_disabledtthen()elseshrink'tt.config.threshold_at_shrinkletaddtnhi=ifis_disabledtthen()elsebeginletcntr=matchHashtbl.find_optt.tblnhwith|Some(_,cntr)->letcnt=Int.minmax_cntr(!cntr+256)inupdate_countertcntrcnt;cntr|None->letcntr=ref256inincr_counterst!cntr;cntrinHashtbl.replacet.tblnh(i,cntr);(* survives 8 shrink loops at least *)t.stat<-{t.statwithever_added=t.stat.ever_added+1};shrink'tt.config.threshold_absoluteendend