我有一个关于提高从MATLAB工作区清除不再需要的变量的性能的问题。
假设工作空间中存在一些应保留的变量。这些变量可以存储如下:
VarsToKeep = who
还假设在存储变量之后,会生成许多新变量。有时,我需要清除那些新生成的变量,我现在这样做如下:
eval(['clearvars -except ' cell2str(VarsToKeep,' ')]);
然而,这个操作似乎需要很长时间(在我的情况下超过5秒)。
由于性能是我环境中的一个主要问题,我想知道是否有更高性能的MATLAB命令用于该操作。
答案 0 :(得分:4)
您可以简单地使用函数语法eval
来调用()
,并使用clearvars
索引将VarsToKeep
传递给它,而不是使用{:}
来创建comma-separated list
clearvars('-except', VarsToKeep{:});
至于为什么它很慢,它实际上取决于你要移除多少变量。如果您有更多变量,则需要更长时间才能清除。
正如您在评论中所述,您有近500个要阻止被清除的变量。如果我们查看clearvars
的内部,有几个原因可以解释为什么要指定这么多要排除的变量很慢。
在内部,clearvars
创建一个正则表达式来确定要保留的变量。在您的情况下,您已明确指定了所有变量名称,因此必须将它们连接成一个大的正则表达式。
例如,如果我们想保留变量A
,B
和C
,则此正则表达式将类似于:
'^(?!(A|B|C)$).'
这个正则表达式基本上只匹配不是我们感兴趣的变量的东西:
regexp({'A', 'AA', 'B', 'AB', 'C', 'D'}, '^(?!(A|B|C)$).');
% [] [1] [] [1] [] [1]
这个正则表达式只是传递给内置clear
,以便通过以下方式进行实际清算:
clear -regexp '^(?!(A|B|C)$).'
clear
然后必须将每个变量与此正则表达式进行比较,以确定是否将其删除。
现在重要的是要记住,正则表达式不是计算上最友好的东西,而且随着它们变得越来越复杂,它们变得越来越糟糕。在添加要排除的变量时,此正则表达式会不断增大。此外,必须在工作空间中为每个变量评估此正则表达式,以便快速添加。
正如一个概念证明,如果你有25个变量,每个25个字符,正则表达式将是这样的
^(?!(onhwbcwijwjjoxyowepmnjeac|jkowjywrerpfamjpdtcisttpy|qtaihttmztryenwyfdzhnunsw|fyhvhmybvbqulietxwitalcjd|noeszudvzieizcbvpraycicnt|gkhdpwticanasbjfyrgjytzlp|nfrrgwghhalhlzawaqtqzdxkd|ritwzxekjcctmyooeuoutufod|sfuimpzzcavgxyuhqhbrttrjn|zquelkgrexmgbogtzegyineay|qyjuxjkfkpnluafyownikibtv|xxoprkwzvrkkvcozvvlhhlaft|nkvuoaxsiztuixtbmmbdaoijb|hdsdopqyndjsbuvefvkcxzohl|pzlitikbyysnpwewzraiifmgi|zfucjwulrnzluxqyohsdmophc|gbdiiftvfbsqoregmmzpemadw|rjdfzlznmshxpvbvqhcwhsuud|ekbluwjwevpgsjbnqjvzybxul|jfmgqyvomuhprelxnolizptxn|iyhnvmdyvrenfhpsmfawqvqga|jcfbtajkidnimopxmawzblfmq|yyccqfoftjqmcbeainaeweeyk|jelyftqgcqkepnpyzdkrpqpam|mbucicotugqiksqkpgryhzwev)$).
如果您想对正则表达式部分所花费的时间进行基准测试,您可以执行以下操作:
% Construct a regular expression of all of your variables
regex = sprintf('^(?!(%s)$).', strjoin(VarsToKeep, '|'));
% Now match all variables to this regex.
matches = regexp(VarsToKeep, regex);
这解释了为什么即使你在工作区中保留所有变量,它仍然非常慢,因为MATLAB仍然必须构造这个巨大的正则表达式并将它与每个变量进行比较发现你排除了所有这些。
请注意,这只是开销,实际上并不包括清除基础数据。
不是使用所有这些正则表达式匹配,在运行代码之前和之后获取变量列表可能会更快,然后使用setdiff
或ismember
来确定那些添加,然后使用clear
明确清除这些。
% Keep track of the variables before we started
beforeVars = who;
% Do stuff
% Get the list of variables after we're done
afterVars = who;
% Figure out which ones were added
toRemove = afterVars(~ismember(afterVars, beforeVars));
% Now clear these variables explicitly (no regular expressions involved)
clear(toRemove{:})
如果您在工作区中定义了大变量,这仍然需要很长时间,但至少您不会浪费太多时间来确定要删除的变量。
我真的很好奇我得到了什么样的表现,所以我设计了一个快速的小基准来进行测试。基本上我在全局工作空间中创建N
变量(具有特定长度的随机名称)并为它们分配所有随机标量值(它们不应该花费任何时间来清除)。然后我应用这两种方法来删除它们的 half 。
function testclear
% Range of sizes to test (I don't have all day so I only tested 5)
nVars = round(linspace(1, 500, 5));
times1 = zeros(1, numel(nVars));
times2 = zeros(1, numel(nVars));
for n = 1:numel(nVars)
%% TEST THE CLEARVARS WAY
% Now create twice as many variables (we will clear half)
createVariables(2 * nVars(n));
% Now we're going to clear half the variables and time it
tic
evalin('base', 'clearvars(''-except'', W{1:ceil(numel(W)/2)})');
times1(n) = toc;
% Now clear everything for the next run
evalin('base', 'clear(W{:})');
%% EXPLICITLY PASS TO CLEAR
createVariables(2 * nVars(n));
evalin('base', 'beforeVars = W(1:ceil(numel(W)/2));')
evalin('base', 'afterVars = W((ceil(numel(W)/2) + 1):end);')
tic
evalin('base', 'toRemove = afterVars(~ismember(afterVars, beforeVars));');
evalin('base', 'clear(toRemove{:});')
times2(n) = toc;
% Now clear everything for the next run
evalin('base', 'clear(W{:})');
end
figure;
plot(nVars, times1, nVars, times2);
xlabel('Number of Variables to Keep')
ylabel('Execution Time (sec)')
legend({'clearvars -except', 'clear'});
end
function createVariables(N)
for k = 1:N
% Create a random variable name
varname = randsample('a':'z', 25, 1);
% Assign that variable within the workspace
evalin('base', [varname, '= rand(1);']);
end
% Get a list of all variables
evalin('base', 'W=who;');
% Add 'W' to the list so it doesn't get cleared
evalin('base', 'W = [''W''; W];');
end
嗯......我认为这个图表说明哪个更快。
我运行了类似的测试,就像你提出的那样所有变量都在-except
列表中,并且它产生了类似的结果。