LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
Cache.m
1classdef Cache < StatefulNode
2 % Multi-level cache node with hit/miss class switching
3 %
4 % Models cache memory systems with multiple levels and replacement strategies.
5 %
6 % Copyright (c) 2012-2026, Imperial College London
7 % All rights reserved.
8
9 properties
10 cap;
11 schedPolicy;
12 schedStrategy;
13 replacestrategy;
14 admissionProb; % q-LRU admission probability on a miss (1.0 = always admit)
15 popularity;
16 nLevels;
17 itemLevelCap;
18 items;
19 accessProb;
20 graph;
21 totalCacheCapacity; % sum(itemLevelCap)
22 retrievalSystemCapacity; % 0 until setRetrievalSystem is called; nitems-totalCacheCapacity otherwise
23 retrievalSystemQueueIndices; % containers.Map jobinClassIdx -> [queue node indices]
24 retrievalClassIndices; % set of indices of retrieval classes (used in afterEventCache READ)
25 retrievalRoutingEntries; % cell array of [fromCls,toCls,srcNode,dstNode,prob] routing tuples;
26 % link() injects these into the routing matrix P (the auto-generated
27 % retrieval classes are not part of the user-supplied P).
28 end
29
30 methods
31 %Constructor
32 function self = Cache(model, name, nitems, itemLevelCap, replStrat, graph)
33 % CACHE Create a Cache node instance
34 %
35 % @brief Creates a Cache node with configurable levels and replacement strategy
36 % @param model Network model to add the cache to
37 % @param name String identifier for the cache node
38 % @param nitems Total number of cacheable items
39 % @param itemLevelCap Vector specifying capacity of each cache level
40 % @param replStrat Replacement strategy (LRU, FIFO, Random, etc.)
41 % @param graph Optional graph structure for cache hierarchy
42 % @return self Cache instance configured for the given model
43 %
44 % The constructor creates a multi-level cache with the specified
45 % total items and per-level capacities. The replacement strategy
46 % determines how items are evicted when cache levels become full.
47
48 self@StatefulNode(name);
49 if model.isMatlabNative()
50 if ~exist('itemLevelCap','var')
51 levels = 1;
52 end
53 classes = model.getClasses();
54 self.input = Buffer(classes);
55 self.output = Dispatcher(classes);
56 self.schedPolicy = SchedStrategyType.NP;
57 self.schedStrategy = SchedStrategy.FCFS;
58 self.items = ItemSet(model, [name,'_','Items'], nitems, self);
59 self.nLevels = nnz(itemLevelCap);
60 self.cap = Inf; % job capacity
61 self.accessProb = {};
62 self.itemLevelCap = itemLevelCap; % item capacity
63 self.totalCacheCapacity = sum(itemLevelCap);
64 self.retrievalSystemCapacity = 0; % no retrieval system by default
65 if self.totalCacheCapacity > nitems
66 line_error(mfilename,sprintf('The number of items is smaller than the capacity of %s.',name));
67 end
68 self.retrievalSystemQueueIndices = containers.Map('KeyType','int32','ValueType','any');
69 self.retrievalClassIndices = [];
70 self.retrievalRoutingEntries = {};
71 self.replacestrategy = replStrat;
72 self.admissionProb = 1.0; % default: always admit on a miss (overridden for q-LRU)
73 %probHit = min(sum(itemLevelCap)/nitems,1.0); % initial estimate of hit probability
74 %self.setResultHitProb(probHit);
75 %self.setResultMissProb(1-probHit);
76 self.server = CacheClassSwitcher(classes, nitems, itemLevelCap); % replace Server created by Queue
77 self.popularity = {};
78 self.setModel(model);
79 self.model.addNode(self);
80 if nargin<6
81 self.graph = [];
82 else
83 self.graph = graph;
84 end
85 elseif model.isJavaNative()
86 self.setModel(model);
87 if nargin<6 || isempty(graph)
88 self.obj = jline.lang.nodes.Cache(model.obj, name, nitems, itemLevelCap, replStrat);
89 else
90 self.obj = jline.lang.nodes.Cache(model.obj, name, nitems, itemLevelCap, replStrat, graph);
91 end
92 self.index = model.obj.getNodeIndex(self.obj);
93 end
94 end
95
96 % function setMissTime(self, distribution)
97 % SETMISSTIME(DISTRIBUTION)
98
99 % itemclass = self.items;
100 % self.server.serviceProcess{1, itemclass.index} = {[], ServiceStrategy.SD, distribution};
101 % end
102 %
103 % function setHitTime(self, distribution, level)
104 % SETHITTIME(DISTRIBUTION, LEVEL)
105
106 % itemclass = self.items;
107 % if ~exist('level','var')
108 % levels = 2:self.nLevels;
109 % else
110 % levels = level;
111 % end
112 % for level = levels
113 % self.server.serviceProcess{1+level, itemclass.index} = {[], ServiceStrategy.SD, distribution};
114 % end
115 % end
116
117 function self = reset(self)
118 % SELF = RESET()
119 %
120 % Reset internal data structures when the network model is
121 % reset
122 self.server.actualHitProb = sparse([]);
123 self.server.actualMissProb = sparse([]);
124 self.server.actualDelayedHitProb = sparse([]);
125 self.server.actualHitProbList = sparse([]);
126 self.server.actualItemProb = sparse([]);
127 self.server.actualResidT = sparse([]);
128 end
129
130 function self = setResultResidT(self, actualResidT)
131 self.server.actualResidT = actualResidT;
132 end
133
134 function p = getResidT(self)
135 p = full(self.server.actualResidT);
136 end
137
138 function tc = getTotalCacheCapacity(self)
139 tc = self.totalCacheCapacity;
140 end
141
142 function rc = getRetrievalSystemCapacity(self)
143 rc = self.retrievalSystemCapacity;
144 end
145
146 function m = getRetrievalClasses(self)
147 m = self.server.retrievalClasses;
148 end
149
150 function idx = getRetrievalClassIndices(self)
151 idx = self.retrievalClassIndices;
152 end
153
154 function q = getRetrievalSystemQueueIndicesFor(self, jobinClassIdx)
155 % q = getRetrievalSystemQueueIndicesFor(jobinClassIdx)
156 % Return node indices of the queues comprising the retrieval system for the
157 % given (0-indexed) arrival class, or [] if no retrieval system is set.
158 if isKey(self.retrievalSystemQueueIndices, int32(jobinClassIdx))
159 q = self.retrievalSystemQueueIndices(int32(jobinClassIdx));
160 else
161 q = [];
162 end
163 end
164
165 function setRetrievalClass(self, jobinClass, joboutClass, item)
166 % SETRETRIEVALCLASS(jobinClass, joboutClass, item)
167 % item is 1-based (MATLAB convention).
168 self.server.retrievalClasses(item, jobinClass.index) = joboutClass.index;
169 end
170
171 function self = setResultHitProb(self, actualHitProb)
172 self.server.actualHitProb = actualHitProb;
173 end
174
175 function self = setResultMissProb(self, actualMissProb)
176 self.server.actualMissProb = actualMissProb;
177 end
178
179 function self = setResultDelayedHitProb(self, actualDelayedHitProb)
180 % SETRESULTDELAYEDHITPROB Per-class delayed-hit fraction
181 % (requests arriving for an item whose fetch is already in
182 % progress in the retrieval system). Zero for caches without a
183 % retrieval system.
184 self.server.actualDelayedHitProb = actualDelayedHitProb;
185 end
186
187 function p = getHitRatio(self)
188 % GETHITRATIO Actual (true) hit fraction per class: the item is
189 % resident in the cache. Delayed hits are reported separately by
190 % getDelayedHitRatio.
191 p = full(self.server.actualHitProb);
192 end
193
194 function p = getMissRatio(self)
195 p = full(self.server.actualMissProb);
196 end
197
198 function p = getDelayedHitRatio(self)
199 % GETDELAYEDHITRATIO Actual delayed-hit fraction per class
200 % (empty/zero when the cache has no retrieval system).
201 if isprop(self.server, 'actualDelayedHitProb')
202 p = full(self.server.actualDelayedHitProb);
203 else
204 p = [];
205 end
206 end
207
208 function self = setResultHitProbList(self, actualHitProbList)
209 % SETRESULTHITPROBLIST Per-class, per-list (per-level) hit
210 % fraction matrix [classes x lists]; rows sum to getHitRatio.
211 self.server.actualHitProbList = actualHitProbList;
212 end
213
214 function p = getHitRatioByList(self)
215 % GETHITRATIOBYLIST Per-class, per-list hit fraction matrix
216 % [classes x lists]; empty when not computed by the solver.
217 if isprop(self.server, 'actualHitProbList')
218 p = full(self.server.actualHitProbList);
219 else
220 p = [];
221 end
222 end
223
224 function self = setResultItemProb(self, actualItemProb)
225 % SETRESULTITEMPROB Per-item occupancy matrix [items x (lists+1)];
226 % column 1 = miss (item not cached), columns 2..end = per-list.
227 self.server.actualItemProb = actualItemProb;
228 end
229
230 function p = getItemProb(self)
231 % GETITEMPROB Per-item occupancy matrix [items x (lists+1)]: column 1
232 % is the miss probability, columns 2..end the per-list probabilities;
233 % empty when not computed by the solver.
234 if isprop(self.server, 'actualItemProb')
235 p = full(self.server.actualItemProb);
236 else
237 p = [];
238 end
239 end
240
241 function setHitClass(self, jobinclass, joboutclass)
242 % SETHITCLASS(JOBINCLASS, JOBOUTCLASS)
243
244 self.server.hitClass(jobinclass.index) = joboutclass.index;
245 end
246
247 function setMissClass(self, jobinclass, joboutclass)
248 % SETMISSCLASS(JOBINCLASS, JOBOUTCLASS)
249
250 self.server.missClass(jobinclass.index) = joboutclass.index;
251 end
252
253
254 function setRead(self, jobclass, distribution)
255 % SETREAD(JOBCLASS, DISTRIBUTION)
256
257 itemclass = self.items;
258 if distribution.isDiscrete
259 self.popularity{itemclass.index, jobclass.index} = distribution.copy;
260 if self.popularity{itemclass.index, jobclass.index}.support(2) ~= itemclass.nitems
261 line_error(mfilename,sprintf('The reference model is defined on a number of items different from the ones used to instantiate %s.',self.name));
262 end
263 switch class(distribution)
264 case 'Zipf'
265 self.popularity{itemclass.index, jobclass.index}.setParam(2, 'n', itemclass.nitems);
266 end
267 % self.probselect(itemclass.index, jobclass.index) = probselect;
268 else
269 line_error(mfilename,'A discrete popularity distribution is required.');
270 end
271 end
272
273 function setReadItemEntry(self, jobclass, popularity, cardinality)
274 % SETREAD(JOBCLASS, DISTRIBUTION)
275
276 if popularity.isDiscrete
277
278 self.popularity{jobclass.index} = popularity.copy;
279 switch class(popularity)
280 case 'Zipf'
281 self.popularity{jobclass.index}.setParam(2, 'n', cardinality);
282 end
283
284 else
285 line_error(mfilename,'A discrete popularity distribution is required.');
286 end
287 end
288 function setAccessProb(self, R)
289 % SETACCESSCOSTS(R)
290
291 self.accessProb = R;
292 end
293
294 function setAdmissionProb(self, q)
295 % SETADMISSIONPROB(Q)
296 % Probability q in [0,1] of admitting a missed item into the cache
297 % (q-LRU). Only used when the replacement strategy is QLRU.
298 if q < 0 || q > 1
299 line_error(mfilename,'The admission probability q must lie in [0,1].');
300 end
301 self.admissionProb = q;
302 end
303
304
305 function setProbRouting(self, class, destination, probability)
306 % SETPROBROUTING(CLASS, DESTINATION, PROBABILITY)
307
308 setRouting(self, class, RoutingStrategy.PROB, destination, probability);
309 end
310
311 function hitClass = getHitClass(self)
312 % HITCLASS = GETHITCLASS
313 %
314 % For an incoming job of class r, HITCLASS(r) is the new class
315 % of that job after a hit
316
317 hitClass = self.server.hitClass;
318 end
319
320 function missClass = getMissClass(self)
321 % MISSCLASS = GETMISSCLASS
322 %
323 % For an incoming job of class r, MISSCLASS(r) is the new class
324 % of that job after a miss
325
326 missClass = self.server.missClass;
327 end
328
329 function addRetrievalRoutingEntry(self, fromCls, toCls, srcNode, dstNode, prob, allowZero)
330 % ADDRETRIEVALROUTINGENTRY(fromCls, toCls, srcNode, dstNode, prob, allowZero)
331 % Register a routing edge for an auto-generated retrieval class. link()
332 % injects all such entries into the routing matrix P. Entries are 1-based
333 % class indices and 1-based node indices; prob is the routing probability.
334 % Later entries override earlier ones for the same (fromCls,toCls,src,dst).
335 % With allowZero=true an explicit prob==0 entry is recorded so it can
336 % override (delete) a default edge inherited from the read class; the
337 % internal broadcast path keeps allowZero=false and drops zero edges.
338 if nargin < 7
339 allowZero = false;
340 end
341 if prob < 0 || (prob == 0 && ~allowZero)
342 return
343 end
344 self.retrievalRoutingEntries{end+1} = [fromCls, toCls, srcNode, dstNode, prob];
345 end
346
347 function setItemRoutingProbability(self, jobinClass, item, source, dest, probability)
348 % SETITEMROUTINGPROBABILITY(jobinClass, item, source, dest, probability)
349 % Probability of routing the retrieval class for `item` between two nodes of
350 % the retrieval system. `source`/`dest` are either a retrieval queue or the
351 % cache itself: pass the cache as `source` for a cache->queue entry, or as
352 % `dest` for a queue->cache exit.
353 rClassIdx = self.server.retrievalClasses(item, jobinClass.index);
354 if rClassIdx <= 0
355 line_error(mfilename,'No retrieval class defined for the given class/item; call setRetrievalSystem first.');
356 end
357 self.addRetrievalRoutingEntry(rClassIdx, rClassIdx, source.index, dest.index, probability, true);
358 end
359
360 function setItemRoutingProb(self, jobinClass, item, source, dest, probability)
361 % Short alias for setItemRoutingProbability.
362 self.setItemRoutingProbability(jobinClass, item, source, dest, probability);
363 end
364
365 function setRetrievalSystem(self, jobinClass, missClass, queues)
366 % SETRETRIEVALSYSTEM(jobinClass, missClass, queues)
367 %
368 % Initialise the retrieval system through which a request that misses the cache
369 % is fetched. The request switches to a per-item retrieval class that circulates
370 % the queues and returns to the cache, where the returning READ logs it as a
371 % miss (switching into `missClass`). getResidT reports the per-class
372 % queueing time in the retrieval sub-network.
373 %
374 % Arguments:
375 % jobinClass arrival JobClass that can route through the retrieval system
376 % missClass JobClass into which a completed retrieval transitions
377 % queues single Queue or array/cell of Queue nodes comprising the system
378 %
379 % Routing and service are NOT passed here; they are taken from the read class:
380 % - service: the read class's service distribution at each queue. Call
381 % queue.setService(jobinClass, ...) beforehand; override per item with
382 % queue.setItemServiceRate(cache, jobinClass, item, rate).
383 % - routing: the read class's routing among the retrieval queues drawn in the
384 % top-level routing matrix P; override per item with
385 % setItemQueueEntryProbability / setItemRoutingProbability / setItemQueueExitProbability.
386
387 % --- normalise inputs ---
388 if isa(queues, 'Queue')
389 queueArr = {queues};
390 elseif iscell(queues)
391 queueArr = queues;
392 elseif isnumeric(queues)
393 line_error(mfilename,'queues must be a Queue or a cell/array of Queues.');
394 else
395 queueArr = num2cell(queues);
396 end
397 nQueues = numel(queueArr);
398 nItems = self.items.nitems;
399 self.retrievalSystemCapacity = nItems - self.totalCacheCapacity;
400
401 if nQueues == 0
402 line_error(mfilename,'Retrieval system cannot be initialised with no stations.');
403 end
404
405 % inherit the read class's service distribution at each queue as the per-item default
406 serviceDistByQueue = cell(1, nQueues);
407 for q = 1:nQueues
408 sp = queueArr{q}.serviceProcess;
409 if numel(sp) >= jobinClass.index && ~isempty(sp{jobinClass.index})
410 serviceDistByQueue{q} = sp{jobinClass.index};
411 else
412 line_error(mfilename, sprintf(['No service distribution for the read class at queue "%s"; ' ...
413 'call queue.setService(readClass, ...) before setRetrievalSystem.'], queueArr{q}.name));
414 end
415 end
416
417 % --- record queue node indices ---
418 queueIdxs = zeros(1, nQueues);
419 for q = 1:nQueues
420 queueIdxs(q) = queueArr{q}.index;
421 end
422 self.retrievalSystemQueueIndices(int32(jobinClass.index-1)) = queueIdxs;
423
424 % --- create one retrieval class per item ---
425 retrievalList = cell(1, nItems);
426 for i = 1:nItems
427 if isa(jobinClass, 'ClosedClass')
428 refStation = jobinClass.refstat;
429 retrievalList{i} = ClosedClass(self.model, [jobinClass.name '_retrievalClass_' num2str(i)], 0, refStation, 0);
430 else
431 retrievalList{i} = OpenClass(self.model, [jobinClass.name '_retrievalClass_' num2str(i)], 0);
432 end
433 self.retrievalClassIndices(end+1) = retrievalList{i}.index;
434 end
435
436 % --- per-item retrieval class setup ---
437 % Each item's retrieval class reads item i (one-hot popularity) and, on the
438 % returning READ, is logged as a miss. Its routing through the queues is NOT set
439 % here: it is inherited at link() from the read class's routing in P, overridable
440 % per item via the setItem* methods.
441 for i = 1:nItems
442 rClass = retrievalList{i};
443
444 % switch arrival jobinClass -> retrieval class for item i
445 self.setRetrievalClass(jobinClass, rClass, i);
446
447 % the retrieval class triggers a read of item i (one-hot popularity)
448 itemPopularity = zeros(1, nItems);
449 itemPopularity(i) = 1.0;
450 self.popularity{self.items.index, rClass.index} = DiscreteSampler(itemPopularity);
451
452 % on the returning READ the retrieval is logged as a miss
453 self.setMissClass(rClass, missClass);
454
455 for sourceQueueIdx = 1:nQueues
456 sourceQueue = queueArr{sourceQueueIdx};
457
458 % service for the retrieval class at this queue (inherited read-class service)
459 sourceQueue.setService(rClass, serviceDistByQueue{sourceQueueIdx}.copy());
460
461 % at most one retrieval in flight at a time for this class
462 if length(sourceQueue.classCap) < rClass.index
463 sourceQueue.classCap((length(sourceQueue.classCap)+1):rClass.index) = Inf;
464 end
465 sourceQueue.classCap(rClass.index) = 1;
466 end
467 end
468 end
469 end
470end
Definition fjtag.m:157
Definition Station.m:245