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