我正在研究一个调用多个子函数(在同一文件中(的复杂函数。为了传递数据,偶尔使用setappdata
/getappdata
机制。此外,一些子函数包含persistent
变量(初始化一次以保存以后的计算(。
我一直在考虑是否可以在并行池中的多个工作线程上执行此功能,但担心可能会有一些意外的数据共享(否则每个工作线程都是唯一的(。
我的问题是- 我如何判断global
和/或persistent
和/或appdata
中的数据是在工人之间共享还是每个工人的唯一?
几个可能相关的事情:
- 就我而言,任务是完全并行的,它们的结果不应以任何方式相互影响(并行化只是为了节省时间(。
- 不会创建任何临时文件或文件夹,因此不存在一个工作人员错误地读取另一个工作人员留下的文件的风险。
- 所有
persistent
和应用程序数据存储的变量都是在parfor
的子函数中创建/分配的。
我知道每个工人对应于一个具有自己的内存空间(大概是global
/persistent
/appdata
工作区(的新进程。基于这一点和这个官方评论,我想说这种共享很可能不会发生......但是我们如何确定呢?
相关资料:
- 这个问答。
- 此文档页面。
测试起来非常简单,我们将分两个阶段进行。
第 1 步:手动生成"工人">
首先,创建以下 3 个函数:
%% Worker 1:
function q52623266_W1
global a; a = 5;
setappdata(0, 'a', a);
someFuncInSameFolder();
end
%% Worker 2:
function q52623266_W2
global a; disp(a);
disp(getappdata(0,'a'));
someFuncInSameFolder();
end
function someFuncInSameFolder()
persistent b;
if isempty(b)
b = 10;
disp('b is now set!');
else
disp(b);
end
end
接下来,我们启动 2 个 MATLAB 实例(代表并行池的两个不同工作线程(,然后在其中一个实例上运行q52623266_W1
,等待它完成,然后在另一个实例上运行q52623266_W2
。如果数据是共享的,则第二个实例将打印一些内容。这导致(在R2018b上(在:
>> q52623266_W1
b is now set!
>> q52623266_W2
b is now set!
这意味着数据不会被共享。到目前为止一切顺利,但人们可能想知道这是否代表一个实际的并行池。因此,我们可以稍微调整一下函数并继续下一步。
第 2 步:自动生成工人
function q52623266_Host
spmd(2)
if labindex == 1
setupData();
end
labBarrier; % make sure that the setup stage was executed.
if labindex == 2
readData();
end
end
end
function setupData
global a; a = 5;
setappdata(0, 'a', a);
someFunc();
end
function readData
global a; disp(a);
disp(getappdata(0,'a'));
someFunc();
end
function someFunc()
persistent b;
if isempty(b)
b = 10;
disp('b is now set!');
else
disp(b);
end
end
运行上述操作,我们得到:
>> q52623266_Host
Starting parallel pool (parpool) using the 'local' profile ...
connected to 2 workers.
Lab 1:
b is now set!
Lab 2:
b is now set!
这再次意味着数据不会被共享。请注意,在第二步中,我们使用了spmd
,其功能应类似于本测试目的的parfor
。
还有另一个不共享数据的问题咬了我。
持久性变量甚至不会从当前工作区复制到工作线程。
为了演示,创建了一个带有持久变量的简单函数(MATLAB 2017a(:
function [ output_args ] = testPersist( input_args )
%TESTPERSIST Simple persistent variable test.
persistent var
if (isempty(var))
var = 0;
end
if (nargin == 1)
var = input_args;
end
output_args = var;
end
并执行一个简短的脚本:
testPersist(123); % Set persistent variable to 123.
tpData = zeros(100,1);
parfor i = 1 : 100
tpData(i) = testPersist;
testPersist(i);
end
any(tpData == 0) % This implies the worker started from 0 instead of 123 as specified in the first row.
输出1
- 工作人员忽略了父工作区中的 123 个,重新开始。
检查tpData
中的值还可以显示每个工作线程如何完成其工作,方法是注意"tpData(14( = 15 - 这意味着完成 15 的工人继续下一个 14">
因此,创建一个工作线程=创建与您面前打开的MATLAB实例完全无关的全新MATLAB实例。为每个工人单独。
我从中得到的教训 = 不要使用简单的持久变量作为模拟配置文件。只要不使用parfor,它工作得很好,看起来很优雅......但后来却可怕地破裂了。使用对象。