Skip to content

Commit be7e345

Browse files
committed
Implement a generic collection mechanism for lexical env lookup caches.
This also repurposes the `All_Diags_Trace` GNATCOLL trace which was introduced recently but in fact never used.
1 parent 7ea84bf commit be7e345

14 files changed

+575
-42
lines changed

langkit/compile_context.py

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,35 @@ def kind_name(self) -> names.Name:
307307
return names.Name('Exception') + self.name
308308

309309

310+
@dataclasses.dataclass
311+
class CacheCollectionConf:
312+
"""
313+
Describes a strategy to use for automatic cache collection.
314+
"""
315+
316+
threshold_increment: int
317+
"""
318+
Indicates the number of additional lexical env cache entries needed after
319+
the last cache collection to trigger a new one. To give an example,
320+
assume that we reached the current threshold set to 1000 entries. Hence, a
321+
collection is attempted. If after this collection the number of entries is
322+
back to, say 800, then we add to it this ``threshold_increment``, say 200,
323+
to get the new threshold. This means a new collection will be attempted
324+
when we reach 1000 entries again. Must be positive.
325+
"""
326+
327+
decision_heuristic: Optional[LibraryEntity] = None
328+
"""
329+
The heuristic to use to decide whether a given unit should have its lexical
330+
env caches collected or not. When left to ``None``, this will use the
331+
default heuristic which will end up clearing the caches of all units when
332+
the collection threshold is reached.
333+
"""
334+
335+
def __post_init__(self):
336+
assert self.threshold_increment > 0
337+
338+
310339
class CompileCtx:
311340
"""State holder for native code emission."""
312341

@@ -348,7 +377,8 @@ def __init__(self,
348377
build_date: Optional[str] = None,
349378
standalone: bool = False,
350379
property_exceptions: Set[str] = set(),
351-
generate_unparser: bool = False):
380+
generate_unparser: bool = False,
381+
cache_collection_conf: Optional[CacheCollectionConf] = None):
352382
"""Create a new context for code emission.
353383
354384
:param lang_name: string (mixed case and underscore: see
@@ -455,6 +485,9 @@ def __init__(self,
455485
456486
:param generate_unparser: If true, generate a pretty printer for the
457487
given grammar. False by default.
488+
489+
:param cache_collection_conf: If not None, setup the automatic cache
490+
collection mechanism with this configuration.
458491
"""
459492
from langkit.python_api import PythonAPISettings
460493
from langkit.ocaml_api import OCamlAPISettings
@@ -688,6 +721,13 @@ def __init__(self,
688721
Whether there is a RefEnvs action in environment specs.
689722
"""
690723

724+
self.cache_collection_conf: Optional[CacheCollectionConf] = (
725+
cache_collection_conf
726+
)
727+
"""
728+
The cache collection configuration to use for this language.
729+
"""
730+
691731
self.template_lookup_extra_dirs: List[str] = (
692732
template_lookup_extra_dirs or []
693733
)
@@ -1037,6 +1077,13 @@ def add_with_clause(self, from_pkg, source_kind, to_pkg, use_clause=False,
10371077
self.with_clauses[(from_pkg, source_kind)].append(
10381078
(to_pkg, use_clause, is_private))
10391079

1080+
@property
1081+
def cache_collection_enabled(self) -> bool:
1082+
"""
1083+
Return whether the automatic cache collection mechanism is enabled.
1084+
"""
1085+
return self.cache_collection_conf is not None
1086+
10401087
@property
10411088
def sorted_logic_functors(self) -> List[Tuple[PropertyDef, int]]:
10421089
return sorted(

langkit/support/langkit_support-lexical_envs_impl.adb

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -170,14 +170,31 @@ package body Langkit_Support.Lexical_Envs_Impl is
170170
------------------------
171171

172172
procedure Reset_Lookup_Cache (Self : Lexical_Env) is
173-
Env : constant Lexical_Env_Access := Unwrap (Self);
173+
use Lookup_Cache_Maps;
174+
175+
Env : constant Lexical_Env_Access := Unwrap (Self);
176+
C : Cursor := First (Env.Lookup_Cache);
177+
Removed : Long_Long_Integer := 0;
174178
begin
175-
for C of Env.Lookup_Cache loop
176-
C.Elements.Destroy;
179+
-- Use an explicit cursor iteration, as a `for of` loop introduces a
180+
-- non-negligible runtime overhead.
181+
while Has_Element (C) loop
182+
declare
183+
Elements : Lookup_Result_Item_Vectors.Vector renames
184+
Env.Lookup_Cache.Reference (C).Elements;
185+
begin
186+
Removed := Removed + Long_Long_Integer (Elements.Length);
187+
Elements.Destroy;
188+
C := Next (C);
189+
end;
177190
end loop;
178191

179192
Env.Lookup_Cache.Clear;
180193
Env.Lookup_Cache_Valid := True;
194+
195+
if Removed > 0 and then Env.Node /= No_Node then
196+
Notify_Cache_Updated (Env.Node, -Removed);
197+
end if;
181198
end Reset_Lookup_Cache;
182199

183200
-----------------------
@@ -1248,6 +1265,10 @@ package body Langkit_Support.Lexical_Envs_Impl is
12481265
(Res_Key, Val, Cached_Res_Cursor, Inserted);
12491266
end;
12501267

1268+
if Env.Node /= No_Node then
1269+
Notify_Cache_Looked_Up (Env.Node);
1270+
end if;
1271+
12511272
if Inserted then
12521273
Need_Cache := True;
12531274
Outer_Results := Local_Results;
@@ -1274,6 +1295,9 @@ package body Langkit_Support.Lexical_Envs_Impl is
12741295
null;
12751296
when Computed =>
12761297
Local_Results.Concat (Res_Val.Elements);
1298+
if Env.Node /= No_Node then
1299+
Notify_Cache_Hit (Env.Node);
1300+
end if;
12771301
return;
12781302
when None =>
12791303
Need_Cache := True;
@@ -1401,6 +1425,10 @@ package body Langkit_Support.Lexical_Envs_Impl is
14011425
Caches_Trace.Trace ("INSERTING CACHE ENTRY " & Log_Id);
14021426
end if;
14031427

1428+
if Env.Node /= No_Node then
1429+
Notify_Cache_Updated
1430+
(Env.Node, Long_Long_Integer (Local_Results.Length));
1431+
end if;
14041432
Env.Lookup_Cache.Include (Res_Key, (Computed, Local_Results));
14051433
Outer_Results.Concat (Local_Results);
14061434
Local_Results := Outer_Results;
@@ -2540,7 +2568,7 @@ package body Langkit_Support.Lexical_Envs_Impl is
25402568

25412569
procedure Reset_Caches (Self : Lexical_Env) is
25422570
begin
2543-
Unwrap (Self).Lookup_Cache_Valid := False;
2571+
Reset_Lookup_Cache (Self);
25442572
end Reset_Caches;
25452573

25462574
----------------------

langkit/support/langkit_support-lexical_envs_impl.ads

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,21 @@ generic
9797
-- Return the current version number of caches corresponding to Node's
9898
-- context, for cache invalidation purposes.
9999

100+
with procedure Notify_Cache_Updated
101+
(Node : Node_Type; Delta_Amount : Long_Long_Integer) is null;
102+
-- Callback procedure used when the number of entries in the lookup cache
103+
-- of the lexical env held by the given node has changed by the given
104+
-- amount (positive: N entries were added, negative: N entries were
105+
-- removed). May trigger a cache collection.
106+
107+
with procedure Notify_Cache_Looked_Up (Node : Node_Type) is null;
108+
-- Callback procedure used when the lookup cache associated with the given
109+
-- node was looked up.
110+
111+
with procedure Notify_Cache_Hit (Node : Node_Type) is null;
112+
-- Callback procedure used when the lookup cache associated with the given
113+
-- node successfully returned a cached entry after a lookup.
114+
100115
type Inner_Env_Assoc is private;
101116
with function Get_Key
102117
(Self : Inner_Env_Assoc) return Symbol_Type is <>;

0 commit comments

Comments
 (0)