Home > atmlab > handy > CachedData.m

CachedData

PURPOSE ^

SYNOPSIS ^

This is a script file.

DESCRIPTION ^

CROSS-REFERENCE INFORMATION ^

This function calls: This function is called by:

SUBFUNCTIONS ^

DOWNLOAD ^

CachedData.m

SOURCE CODE ^

0001 classdef CachedData < handle
0002     % Cache data rather than re-generate every time
0003     %
0004     % Sometimes, it can be somewhat tricky to implement code in a way that
0005     % prevents re-reading or re-calculating the exact some thing with only
0006     % a short time in between. Don't despair! Here is the class you'll
0007     % love. Just use its methods to store and retrieve data.
0008     %
0009     % Entries are stored by string. Internally, it uses a structure, so you
0010     % may use <a href="matlab:help genvarname">genvarname</a> to generate valid names to use for caching.
0011     %
0012     %
0013     % CachedData Properties:
0014     %
0015     %   maxsize -       Maximum permitted cache size in bytes
0016     %
0017     % CachedData Methods:
0018     %
0019     %   get_entry -     Retrieve entry
0020     %   has_entry -     Check whether entry exists
0021     %   set_entry -     Set new entry and, if needed, remove old
0022     %   clear -         Completely clear the cache
0023     %   cachesize -     Get current cache-size in bytes
0024     %   evaluate -      Evaluate expression and cache result, or read
0025     %                   result from cache
0026     
0027     % $Id: CachedData.m 8720 2013-10-21 20:41:39Z gerrit $
0028     properties
0029         % maximum permitted cache size in bytes
0030         %
0031         % default 100 MiB
0032         maxsize = 100*2^20;
0033     end
0034     
0035     properties (GetAccess = private, SetAccess = private)
0036         cache;
0037     end
0038     
0039     methods
0040         function self = CachedData()
0041             self = self@handle();
0042             self.cache = struct();
0043         end
0044         
0045         function data = get_entry(self, s)
0046             % Retrieve entry from cache
0047             %
0048             % FORMAT
0049             %
0050             %   data = c.get_entry(s)
0051             
0052             data = self.cache.(s);
0053         end
0054         
0055         function b = has_entry(self, s)
0056             % check whether entry exists in cache
0057             %
0058             % FORMAT
0059             %
0060             %   b = c.has_entry(s)
0061             
0062             b = isfield(self.cache, s);
0063         end
0064         
0065         function set_entry(self, s, d)
0066             % sets cache entry and removes old entry if needed
0067             %
0068             % Set a cache entry and, if the size is exceeded, remove the
0069             % oldest entry.
0070             %
0071             % FORMAT
0072             %
0073             %   c.set_entry(s, d)
0074             %
0075             % IN
0076             %
0077             %   s   string      string by which it is stored
0078             %   d   any         data to be stored
0079             
0080             logtext(atmlab('OUT'), 'Setting cache entry: %s (new size: ', s);
0081             self.cache.(s) = d;
0082             fprintf(atmlab('OUT'), '%d entries, %s)\n', length(fieldnames(self.cache)), nbytes2string(self.cachesize()));
0083             if self.toolarge()
0084                 self.del_old_entry();
0085             end
0086         end        
0087         
0088         function clear(self)
0089             % clear the cache completely
0090             self.cache = struct();
0091         end
0092         
0093         function c = cachesize(self)
0094             % get size in bytes
0095             dummy = self.cache; %#ok<NASGU>
0096             X = whos('dummy');
0097             c = X.bytes;
0098         end
0099         
0100         function varargout = evaluate(self, no, func, varargin)
0101             % Execute func(arg1, arg2, ...) if output not already cached
0102             %
0103             % This method calculates a key from all its arguments. If no
0104             % value is stored in association with this key, it executes
0105             % func(arg1, arg2, ...) and stores the results with the key,
0106             % then returns the results. If there is a value already stored,
0107             % it returns the value(s). The first argument is a number,
0108             % it represents the number of output arguments taken from the
0109             % expression.
0110             %
0111             % FORMAT
0112             %
0113             %   out = cd.evaluate(no, @func, arg1, arg2, ...)
0114             %
0115             % IN
0116             %
0117             %   no      number of output arguments. Needed because
0118             %           there may be a difference between y = foo(...)
0119             %           and [y, z] = foo(...)
0120             %
0121             %   func    function handle to function to be evaluated
0122             %   arg1    1st argument passed to function
0123             %    ...
0124             %   argN    nth argument passed to function
0125             %   'EXTRA' literal string 'EXTRA'.  The remaining arguments
0126             %   WILL be used to calculate the hash, but WON'T be passed on
0127             %   to others.  Use this e.g. when calculating obj.meth(args)
0128             %   for different objects (pass obj.name or so).
0129             %
0130             % OUT
0131             %
0132             %   output is as for func(arg1, ..., argN).
0133             %
0134             % EXAMPLE
0135             %
0136             %   If you have an expensive function 'y = ackermann(m, n)',
0137             %   then you can cache as follows:
0138             %
0139             %   >> c = CachedData(); % create cache object
0140             %   >> y = c.evaluate(1, @ackermann, 4, 4) % come back a few million years later
0141             %   >> y = c.evaluate(1, @ackermann, 4, 4) % get result immediately
0142             %
0143 
0144             % generate a unique (?) string
0145             nm = genvarname(DataHash([{no, func}, varargin]));
0146             
0147             extra = 0;
0148             args = varargin;
0149             % check if any equal to 'EXTRA'
0150             for i = 1:length(varargin)
0151                 if isequal(varargin{i}, 'EXTRA')
0152                     extra = i;
0153                     args = varargin(1:extra-1);
0154                     break;
0155                 end
0156             end
0157             if self.has_entry(nm)
0158                 logtext(atmlab('OUT'), 'getting %s([%d arguments]) from cache (%s)\n', ...
0159                     func2str(func), length(args), nm);
0160                 varargout = self.get_entry(nm);
0161             else
0162                 logtext(atmlab('OUT'), 'executing, then caching %s([%d arguments])\n', ...
0163                     func2str(func), length(args));
0164                 [varargout{1:no}] = func(args{:});
0165                 self.set_entry(nm, varargout);
0166             end
0167         end
0168     end
0169     
0170     methods (Access = private)
0171         function del_old_entry(self)
0172             % Remove oldest entry in cache.
0173             %
0174             % FORMAT
0175             %
0176             %   c.del_old_entry()
0177             flds = fieldnames(self.cache);
0178             logtext(atmlab('OUT'), 'Pruning cache entry %s\n', flds{1});
0179             self.cache = rmfield(self.cache, flds{1});
0180             logtext(atmlab('OUT'), 'New size: %d entries, %d bytes\n', length(fieldnames(self.cache)), self.cachesize());
0181         end
0182         
0183 
0184         function b = toolarge(self)
0185             % whether or not to remove old entry
0186             %
0187             % Check whether the current size exceeds the total size.
0188             b = self.cachesize() > self.maxsize;
0189         end
0190         
0191 
0192     end
0193 end

Generated on Mon 15-Sep-2014 13:31:28 by m2html © 2005