0001 function varargout=eidors_cache( command, varargin )
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069
0070
0071
0072
0073
0074
0075
0076
0077
0078
0079
0080
0081
0082
0083
0084
0085
0086
0087
0088
0089
0090
0091
0092
0093
0094
0095
0096
0097
0098
0099
0100
0101
0102
0103
0104
0105
0106
0107
0108
0109
0110
0111
0112
0113
0114
0115
0116
0117
0118
0119
0120
0121
0122
0123 if nargin==1 && ischar(command) && strcmp(command,'UNIT_TEST');
0124 do_unit_test; return; end
0125
0126 global eidors_objects;
0127 if nargin<1
0128 fprintf('EIDORS_CACHE: current max memory = %.0f MB\n', ...
0129 eidors_objects.max_cache_size/(1024*1024));
0130 ww= whos('eidors_objects');
0131 fprintf('EIDORS_CACHE: cache memory used = %.0f MB\n', ...
0132 ww.bytes/(1024*1024));
0133 fprintf('EIDORS_CACHE: current priority = %d\n', ...
0134 eidors_objects.cache_priority);
0135 return;
0136 elseif nargin > 1
0137 limit = varargin{1};
0138 end
0139
0140
0141
0142
0143
0144 if isa(command, 'function_handle') || ...
0145 (ischar(command) && ...
0146 any(exist(command) == [2,3]))
0147
0148
0149 if isa(command, 'function_handle')
0150 str = func2str(command);
0151 if str(1) == '@'
0152 error('Cannot cache anonymous functions');
0153 end
0154 end
0155 [varargout{1:nargout}] = ...
0156 cache_shorthand(command, varargin{:});
0157 return
0158 end
0159
0160
0161 switch command
0162 case 'init'
0163 eidors_objects.cache_enable = 1;
0164 eidors_objects.cache_disabled_on = [];
0165 eidors_objects.cache_debug_enable = 0;
0166 eidors_objects.cache_debug_enabled_on = [];
0167 eidors_obj('cache_init');
0168
0169 case 'clear_all'
0170 remove_objids
0171
0172 case 'clear'
0173 switch nargin
0174 case 2
0175 eidors_cache('clear_name',limit);
0176 case 1
0177 eidors_cache('clear_all');
0178 otherwise
0179 error('Wrong number of inputs');
0180 end
0181
0182 case 'cache_size'
0183 if nargin==2
0184 if ischar(limit); limit= str2num(limit); end
0185 eidors_objects.max_cache_size = limit;
0186 else
0187 varargout{1}= eidors_objects.max_cache_size;
0188 end
0189
0190 case 'cache_path'
0191 if nargin == 1
0192 varargout{1}= eidors_objects.cache_path;
0193 else
0194 eidors_objects.cache_path = varargin{1};
0195 end
0196
0197 case 'eidors_path'
0198 if nargin == 1
0199 varargout{1}= eidors_objects.eidors_path;
0200 else
0201 eidors_objects.eidors_path = varargin{1};
0202 end
0203 case {'disable' 'off'}
0204 if nargin == 1
0205 eidors_objects.cache_enable = 0;
0206 eidors_objects.cache_disabled_on = {};
0207 else
0208 eidors_objects.cache_enable = 0.5;
0209 if isfield(eidors_objects,'cache_disabled_on')
0210 if ~any(strcmp(eidors_objects.cache_disabled_on, limit))
0211 eidors_objects.cache_disabled_on = [...
0212 eidors_objects.cache_disabled_on; {limit}];
0213 end
0214 else
0215 eidors_objects.cache_disabled_on = {limit};
0216 end
0217 end
0218 case {'enable' 'on'}
0219 if nargin == 1
0220 eidors_objects.cache_enable = 1;
0221 eidors_objects.cache_disabled_on = {};
0222 else
0223 if isfield(eidors_objects,'cache_disabled_on')
0224 idx = strcmp(eidors_objects.cache_disabled_on, limit);
0225 eidors_objects.cache_disabled_on(idx) = [];
0226 else
0227 eidors_objects.cache_disabled_on = [];
0228 end
0229 if isempty(eidors_objects.cache_disabled_on)
0230 eidors_objects.cache_enable = 1;
0231 end
0232 end
0233 case 'status'
0234 if nargin == 1
0235 try
0236 varargout{1} = eidors_objects.cache_enable;
0237 catch
0238 varargout{1} = 1;
0239 end
0240 else
0241 if isfield(eidors_objects,'cache_disabled_on')
0242 idx = strcmp(eidors_objects.cache_disabled_on, limit);
0243 varargout{1} = double(~any(idx));
0244 end
0245 end
0246 case 'debug_status'
0247 if nargin == 1
0248 varargout{1} = eidors_objects.cache_debug_enable;
0249 else
0250 if isfield(eidors_objects,'cache_debug_enabled_on')
0251 idx = ismember(limit,eidors_objects.cache_debug_enabled_on);
0252 varargout{1} = idx | eidors_objects.cache_debug_enable==1;
0253 end
0254 end
0255 case 'debug_on'
0256 if nargin == 1
0257 eidors_objects.cache_debug_enable = 1;
0258 eidors_objects.cache_debug_enabled_on = {};
0259 else
0260 eidors_objects.cache_debug_enable = 0.5;
0261 if isfield(eidors_objects,'cache_debug_enabled_on')
0262 if ~any(strcmp(eidors_objects.cache_debug_enabled_on, limit))
0263 eidors_objects.cache_debug_enabled_on = [...
0264 eidors_objects.cache_debug_enabled_on; {limit}];
0265 end
0266 else
0267 eidors_objects.cache_debug_enabled_on = {limit};
0268 end
0269 end
0270 case 'debug_off'
0271 if nargin == 1
0272 eidors_objects.cache_debug_enable = 0;
0273 eidors_objects.cache_debug_enabled_on = {};
0274 else
0275 if isfield(eidors_objects,'cache_debug_enabled_on')
0276 idx = strcmp(eidors_objects.cache_debug_enabled_on, limit);
0277 eidors_objects.cache_debug_enabled_on(idx) = [];
0278 else
0279 eidors_objects.cache_debug_enabled_on = [];
0280 end
0281 if isempty(eidors_objects.cache_debug_enabled_on)
0282 eidors_objects.cache_debug_enable = 0;
0283 end
0284 end
0285 case 'boost_priority'
0286 try
0287 varargout{1}= eidors_objects.cache_priority;
0288 catch
0289 varargout{1}= 0;
0290 end
0291 if nargin==2
0292 if ischar(limit); limit= str2double(limit); end
0293 varargout{1} = varargout{1} + limit;
0294 end
0295 eidors_objects.cache_priority = varargout{1};
0296
0297 case {'list', 'show_objs'}
0298 if nargin == 2
0299 cache_list(limit);
0300 else
0301 cache_list;
0302 end
0303
0304
0305 case 'clear_max'
0306 if ischar(limit); limit= str2double(limit); end
0307 try
0308 c = eidors_objects.cache.cols;
0309 catch
0310 return
0311 end
0312
0313 priidx = get_cache_priority;
0314 [jnk, idx] = sort(priidx);
0315 tot= cumsum([eidors_objects.cache.meta{idx,c.size}]);
0316 rmidx = idx(tot > limit);
0317 remove_objids( rmidx );
0318
0319 case 'clear_old'
0320 if ischar(limit); limit= str2num(limit); end
0321 try
0322 c = eidors_objects.cache.cols;
0323 catch
0324 return
0325 end
0326 idx = find([eidors_objects.cache.meta{:,c.time}] < limit);
0327 remove_objids( idx );
0328
0329 case 'clear_new'
0330 if ischar(limit); limit= str2num(limit); end
0331 try
0332 c = eidors_objects.cache.cols;
0333 catch
0334 return
0335 end
0336 idx = find([eidors_objects.cache.meta{:,c.time}] > limit);
0337 remove_objids( idx );
0338
0339 case 'clear_model_library'
0340
0341 delete([eidors_objects.model_cache,'/*.mat']);
0342
0343 case 'clear_name'
0344 idx = clear_names_cache( limit );
0345 remove_objids( idx );
0346
0347 case 'dump'
0348 varargout{1} = eidors_objects;
0349
0350 case 'load'
0351 eidors_objects = limit;
0352
0353 otherwise
0354 error('command %s not understood',command);
0355 end
0356
0357 function cache_list (order)
0358 global eidors_objects;
0359 try
0360 meta = eidors_objects.cache.meta;
0361 catch
0362 return
0363 end
0364 if nargin == 0
0365 order = 'time';
0366 end
0367
0368 c = eidors_objects.cache.cols;
0369 if isempty(meta)
0370 fprintf('No objects in cache\n');
0371 return
0372 end
0373 meta(:,c.time) = cellstr(datestr([meta{:,c.time}],'yyyy-mm-dd HH:MM:SS.FFF'));
0374 N = size(meta,2);
0375
0376
0377 meta(:,N+1) = num2cell(get_cache_priority);
0378 switch order
0379 case 'time'
0380 meta = mysortrows(meta,c.time);
0381 case {'prop','name'}
0382 meta = mysortrows(meta,c.prop);
0383 case 'rank'
0384 meta = mysortrows(meta,N+1);
0385 case 'size'
0386 meta = mysortrows(meta,-c.size);
0387 case 'effort'
0388 meta = mysortrows(meta,-c.effort);
0389 case 'count'
0390 meta = mysortrows(meta,-c.count);
0391 otherwise
0392 error('Unrecognized sort order');
0393 end
0394
0395 meta = meta';
0396 fprintf('CACHE__: Date+Time bytes Score_szPrio CountXEffort Score_eff # obj_id ___\n');
0397 fprintf('%s b=%9.0d [%4d] p=%02d t=%3dx%.2e [%4d] i=%4d: %s { %s }\n', ...
0398 meta{[c.time,c.size,c.score_sz,c.prio,c.count,c.effort,c.score_eff,N+1,c.obj_id, c.prop],:});
0399
0400 function priidx = get_cache_priority
0401 global eidors_objects;
0402 priidx = [];
0403 if isfield(eidors_objects.cache, 'meta') && isfield(eidors_objects.cache, 'cols')
0404 meta = eidors_objects.cache.meta;
0405 c = eidors_objects.cache.cols;
0406 [jnk, priidx] = mysortrows(meta,[-c.score_eff c.score_sz -c.time]);
0407 priidx(priidx) = 1:size(meta,1);
0408 end
0409
0410
0411
0412 function objid = clear_names_cache( name )
0413 objid=[];
0414 global eidors_objects;
0415 try
0416 c = eidors_objects.cache.cols;
0417 objid = find(strcmp(name, eidors_objects.cache.meta(:,c.prop)));
0418 end
0419
0420
0421 function remove_objids(idx, names, sizes)
0422 global eidors_objects;
0423 try
0424 c = eidors_objects.cache.cols;
0425 catch
0426 eidors_obj('cache_init');
0427 return
0428 end
0429 if nargin == 0
0430 idx = 1:size(eidors_objects.cache.meta,1);
0431 end
0432 if isempty(idx)
0433 return
0434 end
0435 switch eidors_cache('debug_status')
0436 case 1
0437 debug_msg( eidors_objects.cache.meta(idx,c.obj_id), ...
0438 eidors_objects.cache.meta(idx,c.prop), 'removed');
0439 case 0.5
0440 db = eidors_cache('debug_status', eidors_objects.cache.meta(idx,c.prop));
0441 debug_msg( eidors_objects.cache.meta(idx(db),c.obj_id), ...
0442 eidors_objects.cache.meta(idx(db),c.prop), 'removed');
0443 end
0444 total_size = sum(cell2mat(eidors_objects.cache.meta(idx,c.size)));
0445 N = numel(idx);
0446 if numel(idx) == size(eidors_objects.cache.meta,1)
0447 eidors_objects = rmfield(eidors_objects,'cache');
0448 eidors_obj('cache_init');
0449 else
0450 eidors_objects.cache = rmfield(eidors_objects.cache, ...
0451 eidors_objects.cache.meta(idx,c.obj_id));
0452 eidors_objects.cache.meta(idx,:) = [];
0453 eidors_objects.cache.size = eidors_objects.cache.size - total_size;
0454 end
0455
0456 eidors_msg('Removed %d objects with %d bytes from cache', ...
0457 N, total_size, 2 );
0458
0459
0460
0461 function varargout = cache_shorthand(fhandle, varargin)
0462
0463 args = varargin{1};
0464 if ~iscell(args)
0465 args = {args};
0466 end
0467
0468 if nargin >2
0469 opt = varargin{2};
0470 else
0471 opt = struct;
0472 end
0473 if ischar(opt)
0474 fstr = opt; clear opt;
0475 opt.fstr = fstr;
0476 end
0477 if isfield(opt, 'cache_obj');
0478 cache_obj = opt.cache_obj;
0479 if ~iscell(cache_obj)
0480 cache_obj = {cache_obj};
0481 end
0482 else
0483 cache_obj = args;
0484 end
0485 try
0486 fstr = opt.fstr;
0487 catch
0488 fstr = func2str(fhandle);
0489 end
0490 if isfield(opt, 'log_level')
0491 level_in = opt.log_level;
0492 level_out = opt.log_level;
0493 else
0494 level_in = 4;
0495 level_out = 3;
0496 end
0497 cache_to_disk = false;
0498 if isfield(opt, 'cache_to_disk')
0499 cache_locn = opt.cache_to_disk;
0500 if cache_locn
0501 cache_to_disk = true;
0502 end
0503 if cache_locn==true
0504 cache_locn = '.';
0505 end
0506 if isfield(opt,'fstr')
0507 cache_str = opt.fstr;
0508 else
0509 cache_str = 'eidors_cache';
0510 end
0511 end
0512
0513 [varargout,obj_id] = eidors_obj('get-cache', cache_obj, fstr );
0514 if length(varargout)==0 && cache_to_disk
0515 savename = [cache_locn,'/',cache_str,'_',obj_id,'.mat'];
0516 if exist(savename,'file')
0517 load(savename)
0518 end
0519 end
0520 if numel(varargout) < nargout
0521 eidors_msg('@@ (Re)calculating %s',fstr, level_in);
0522 t0 = tic;
0523 [varargout{1:nargout}] = ...
0524 feval(fhandle, args{:});
0525 t = toc(t0);
0526 if isfield(opt,'boost_priority');
0527 eidors_cache('boost_priority',opt.boost_priority);
0528 end
0529
0530
0531 if cache_to_disk
0532 eidors_msg('@@ Caching to %s', savename, level_in+1);
0533 save(savename,'varargout','-V7');
0534 else
0535 eidors_obj('set-cache', cache_obj, fstr, varargout, t);
0536 end
0537
0538 if isfield(opt,'boost_priority');
0539 eidors_cache('boost_priority',-opt.boost_priority);
0540 end
0541 return
0542 end
0543 eidors_msg('%s: Using cached value',fstr,level_out);
0544
0545 function debug_msg(id,name,action)
0546 global eidors_objects;
0547 if nargin < 3
0548 action = name;
0549 name = fieldnames(eidors_objects.(id));
0550 end
0551 if isempty(id), return, end
0552 if ~iscell(name) name = {name}; end
0553 if ~iscell(id) id = {id}; end
0554
0555 str = sprintf('EIDORS_CACHE: %s %%s { %%s }\\n', action);
0556 arr = [id, name]';
0557
0558 fprintf(str, arr{:});
0559
0560
0561 function do_unit_test
0562 ll= eidors_msg('log_level');
0563 eidors_msg('log_level',5);
0564 eidors_cache
0565 eidors_cache('clear_all');
0566 eidors_cache
0567 eidors_obj('set-cache', rand(1) , 't1', rand(2e3));
0568 eidors_obj('set-cache', rand(1) , 't2', rand(2e3));
0569 eidors_obj('set-cache', rand(1) , 't3', rand(2e3));
0570 eidors_cache
0571 eidors_cache list
0572 eidors_cache('clear_name','t3');
0573 eidors_cache
0574 eidors_cache list
0575 eidors_cache('clear_max', 34e6);
0576 eidors_cache
0577 eidors_cache('boost_priority', 1);
0578 eidors_cache
0579 eidors_cache('boost_priority', -1);
0580 eidors_cache
0581 [v1] = eidors_cache(@test_function,{3,4});
0582 [v2] = eidors_cache(@test_function,{3,4});
0583 unit_test_cmp('shorthand 1 param:',v1,v2);
0584 [v3 v4] = eidors_cache(@test_function,{3,4});
0585 unit_test_cmp('Expect Fail', v3, v4,-inf);
0586 [v5 v6 ] = eidors_cache(@test_function,{3,4});
0587 unit_test_cmp('shorthand 2 params:',v4, v6);
0588 [v5 v6 ] = eidors_cache(@test_function,{3,4, 5});
0589 opt.cache_obj = 5;
0590 [v5 v6 ] = eidors_cache(@test_function,{3,4, 5}, opt);
0591 [v7 v8 ] = eidors_cache(@test_function,{1,2, 3}, opt);
0592 unit_test_cmp('shorthand cache_obj:',v6, v8);
0593 eidors_cache clear_all
0594 opt = struct;
0595 [v7 v8 ] = eidors_cache(@test_function,{1,2, 3}, opt);
0596 opt.boost_priority = 2;
0597 [v7 v8 ] = eidors_cache(@test_function,{3,4, 5}, opt);
0598 eidors_cache show_objs
0599 eidors_cache
0600 eidors_msg('log_level',ll);
0601 try
0602 eidors_cache(@(x) x^2, 3);
0603 catch
0604 eidors_msg('Error on anonymous function: correct',2);
0605 end
0606 test_debug
0607 test_priority
0608 test_disk_caching
0609
0610 function [v1 v2] = test_function(a,b,c,d)
0611 v1 = rand(1);
0612 v2 = rand(1);
0613
0614 function [meta,idx] = mysortrows(meta, cols)
0615 if ~exist('OCTAVE_VERSION')
0616 [meta,idx] = sortrows(meta, cols);
0617 else
0618 metm = cell2mat(meta(:,3:end));
0619 cols = ( abs(cols) - 2 ) .* sign(cols);
0620 [~,idx] = sortrows(metm, cols);
0621 meta = meta(idx,:);
0622 end
0623
0624
0625 function test_debug
0626 fprintf('\n\n************************\n CACHE DEBUG: VERBOSE OUTPUT\n************************\n');
0627 eidors_cache clear
0628 eidors_obj('set-cache',{5}, 'test1',50);
0629 eidors_obj('set-cache',{5}, 'test2',500);
0630 eidors_obj('set-cache',{10}, 'test1',100);
0631 eidors_cache show_objs
0632 eidors_cache debug_off
0633 eidors_cache debug_on test2
0634 eidors_cache clear_name test2
0635 eidors_cache show_objs
0636
0637 eidors_obj('set-cache',{5}, 'test2',500);
0638 eidors_cache debug_off
0639 eidors_cache debug_on test1
0640 eidors_cache clear_name test2
0641 eidors_cache('clear_max',0)
0642 eidors_cache('show_objs')
0643 eidors_cache debug_off
0644 fprintf('\n\n************************\n CACHE DEBUG: DEBUG FINISHED\n************************\n');
0645
0646 function test_disk_caching
0647 tstdir = 'eidors_cache_test_dir1234321';
0648 [~]=rmdir(tstdir,'s');
0649 [~]=mkdir(tstdir);
0650 disp('============= DISK =============');
0651 opt = struct('cache_to_disk',tstdir);
0652 eidors_cache clear; global eidors_objects
0653 esz = length(fieldnames(eidors_objects.cache));
0654 unit_test_cmp('disk 00',length(dir([tstdir,'/*.mat'])),0)
0655 [v1]= eidors_cache( @sin, {1:100},opt);
0656 unit_test_cmp('disk 01',length(dir([tstdir,'/*.mat'])),1)
0657 unit_test_cmp('disk 02',0, ...
0658 length(fieldnames(eidors_objects.cache))-esz);
0659 obj_id = eidors_var_id({1:100,'sin'});
0660 cfname = [tstdir,'/eidors_cache_',obj_id,'.mat'];
0661 unit_test_cmp('disk 03',length(dir(cfname)),1);
0662 loader = load(cfname);
0663 unit_test_cmp('disk 04',loader.varargout{1},v1);
0664
0665 [v1]= eidors_cache( @sin, {1:100},opt);
0666 unit_test_cmp('disk 05',length(dir([tstdir,'/*.mat'])),1)
0667 unit_test_cmp('disk 06',0, ...
0668 length(fieldnames(eidors_objects.cache))-esz);
0669 loader = load(cfname);
0670 unit_test_cmp('disk 07',loader.varargout{1},v1);
0671 [~]=rmdir(tstdir,'s');
0672 disp('============= ~DISK =============');
0673 eidors_cache clear; global eidors_objects
0674 opt = struct('cache_to_disk',false);
0675 esz = length(fieldnames(eidors_objects.cache));
0676 [v1]= eidors_cache( @sin, {1:100},opt);
0677 unit_test_cmp('not disk 01',1, ...
0678 length(fieldnames(eidors_objects.cache))-esz);
0679 obj_id = eidors_var_id({1:100,'sin'});
0680 unit_test_cmp('not disk 02',1, ...
0681 length(fieldnames(eidors_objects.cache))-esz);
0682 unit_test_cmp('not disk 03',isfield(eidors_objects.cache,obj_id),1);
0683 unit_test_cmp('not disk 04',eidors_objects.cache.(obj_id),[v1]);
0684
0685 [v1]= eidors_cache( @sin, {1:100},opt);
0686 unit_test_cmp('not disk 05',1, ...
0687 length(fieldnames(eidors_objects.cache))-esz);
0688
0689
0690 function test_priority
0691 eidors_cache clear
0692 eidors_obj('set-cache',{1}, 'slow_small', zeros(10) ,10); pause(.1)
0693
0694
0695 eidors_obj('set-cache',{1}, 'slow_new', zeros(100),10); pause(.1)
0696
0697
0698 eidors_obj('set-cache',{1}, 'slow_big1', zeros(100),10); pause(.1)
0699
0700
0701 eidors_obj('set-cache',{1}, 'slow_big2', zeros(100),20); pause(.1)
0702
0703
0704 eidors_obj('set-cache',{1}, 'fast_small', zeros(2) , 1); pause(.1)
0705
0706
0707 eidors_obj('set-cache',{1}, 'fast_big', zeros(100), 1); pause(.1)
0708
0709
0710 eidors_obj('get-cache',{1}, 'slow_new');
0711
0712 eidors_cache list
0713
0714
0715