带有许多匿名函数的Matlab并行代码导致内存错误

带有许多匿名函数的Matlab并行代码导致内存错误,matlab,parfor,Matlab,Parfor,我有一个用许多不同的输入/参数解决科学问题的代码。我使用一个并行for循环来迭代一系列参数,并在内存使用方面遇到了麻烦。我已经尽了最大努力来组合一个代表我的代码的MWE 基本上,对于每个参数组合,我在几个不同的解算器选项上运行一个小循环。在我的真实代码中,这是改变解算器公差和使用的方程,我们有一些不同的转换,可以帮助调节。每种计算都是一种针对小型ODE系统3方程的有效打靶方法,但每种计算都非常复杂且通常很僵硬,有一个优化例程调用ODE解算器。每次运行都需要几秒/分钟,并行化开销可以忽略不计,并且

我有一个用许多不同的输入/参数解决科学问题的代码。我使用一个并行for循环来迭代一系列参数,并在内存使用方面遇到了麻烦。我已经尽了最大努力来组合一个代表我的代码的MWE

基本上,对于每个参数组合,我在几个不同的解算器选项上运行一个小循环。在我的真实代码中,这是改变解算器公差和使用的方程,我们有一些不同的转换,可以帮助调节。每种计算都是一种针对小型ODE系统3方程的有效打靶方法,但每种计算都非常复杂且通常很僵硬,有一个优化例程调用ODE解算器。每次运行都需要几秒/分钟,并行化开销可以忽略不计,并且加速比几乎与内核数成正比

要解释下面的代码,请从驱动程序开始。首先在MWE中定义一些参数a和f,并将它们保存在文件中。文件名在函数之间传递。然后在本例中创建3组解算器参数,选择要使用的ode解算器、公差和方程组。然后输入for循环,在每次迭代时使用每个解算器参数集在其他一些参数c上循环,以调用优化函数。最后,我保存了一个包含每次迭代结果的临时文件,这样在服务器停机时不会丢失所有内容。这些文件大约为1kB,我只有大约10000个,所以总体大小大约为10MB。在主循环之后,我将所有内容重新组合成单个向量

“方程”函数创建要求解的实际微分方程,这是通过使用switch语句来选择要返回的方程来完成的。objectiveFunction函数使用str2func指定ODE解算器,调用方程以获得要求解的方程,然后求解它们并计算目标函数值

问题是似乎存在某种内存泄漏。过了一段时间后,代码速度减慢,最终出现内存不足错误,运行在48个内核上,可用内存约为380GB,ode15s给出了错误。随着时间的推移,内存使用量的增加是相当缓慢的,但确实存在,我不知道是什么原因导致了它

具有10000个值c的MWE需要相当长的时间才能运行1000,实际上可能已经足够了,而且每个工作进程的内存使用量确实会随着时间的推移而增加。我认为文件加载/保存和作业分发会造成相当大的开销,这与我的实际代码不同,但这不会影响内存使用

我的问题是,是什么导致了内存使用的缓慢增长

我对造成问题的原因的看法是:

使用str2func不是很好,我应该使用一个开关来代替,并且接受必须显式地将解算器写入代码中吗? ODE解算器中一直被调用的所有匿名函数都会保留工作区数据,而不会在每次迭代结束时释放它 抑制的警告会导致问题:我抑制了大量ODE步长警告这不应该是一个因素,因为导致问题的错误在2017a中已修复,并且我使用的服务器运行2017b fminbnd或ode15s中的某些内容实际上正在泄漏内存 从代码性能和代码编写的角度来看,我想不出一种方法可以很好地、有效地处理1和2,我怀疑3或4是否真的是问题所在

以下是驱动程序功能:

function [xi,mfv] = driver()

% a and f are used in all cases. In actual code these are defined in a
% separate function
paramFile = 'params';
a = 4;
f = @(x) 2*x;

% this filename (params) gets passed around from function to function
save('params.mat','a','f')

% The struct setup has specifc options for the each iteration
setup(1).method = 'ode45'; % any ODE solver can be used here
setup(1).atol = 1e-3; % change the ODE solver tolerance
setup(1).eqs = 'second'; % changes what equations are solved

setup(2).method = 'ode15s';
setup(2).atol = 1e-3;
setup(2).eqs = 'second';

setup(3).method = 'ode15s';
setup(3).atol = 1e-4;
setup(3).eqs = 'first';

c = linspace(0,1);

parfor i = 1:numel(c) % loop over parameter c
    xi = 0;
    minFVal = inf;
    for j = 1:numel(setup) % loop over each set configuration setup

        % find optimal initial condition and record corresponding value of
        % objective function
        [xInitial,fval] = fminsearch(@(x0) objectiveFunction(x0,c(i),...
            paramFile,setup(j)),1);

        if fval<minFVal % keep the best solution
            xi = xInitial;
            minFVal = fval;
        end
    end
    % save some variables
    saveInParForLoop(['tempresult_' num2str(i)],xi,minFVal);
end

% Now combine temporary files into single vectors
xi = zeros(size(c)); mfv = xi;
for i = 1:numel(c)
    S = load(['tempresult_' num2str(i) '.mat'],'xi','minFVal');
    xi(i) = S.xi;
    mfv(i) = S.minFVal;
end

% delete the temporary files now that the data has been consolidated
for i = 1:numel(c)
    delete(['tempresult_' num2str(i) '.mat']);
end
end

function saveInParForLoop(filename,xi,minFVal)
% you can't save directly in a parfor loop, this is the workaround
save(filename,'xi','minFVal')
end
这是评估目标函数的函数

function val = objectiveFunction(x0,c,paramFile,setup)

load(paramFile,'a')

% specify the ODE solver and AbsTol from s
solver = str2func(setup.method);
options = odeset('AbsTol',setup.atol);

% get the differential equation and transform equations
[der,transform] = equations(paramFile,setup);
dxdt = @(t,y) der(y);

% solve the IVP
[~,y] = solver(dxdt,0:.05:1,x0,options);

% calculate the objective function value
val = norm(transform(y)-c*a);

如果您运行此代码,它将创建100个临时文件,然后删除它们,它还将创建不会被删除的params文件。您将需要并行计算工具箱。

您可能会遇到这个已知问题:。这在刚刚发布的R2019b中被标记为已修复。由此造成的泄漏很小,但持续存在,因此可能需要几天才能显现出来。

我想你可能是对的。我会在本地测试r2019b,看看。很高兴听到这个消息!
function val = objectiveFunction(x0,c,paramFile,setup)

load(paramFile,'a')

% specify the ODE solver and AbsTol from s
solver = str2func(setup.method);
options = odeset('AbsTol',setup.atol);

% get the differential equation and transform equations
[der,transform] = equations(paramFile,setup);
dxdt = @(t,y) der(y);

% solve the IVP
[~,y] = solver(dxdt,0:.05:1,x0,options);

% calculate the objective function value
val = norm(transform(y)-c*a);