How to use persistent variables inside parfor method of a custom object class?
Mostra commenti meno recenti
There is a similar entry on using persistent variables with spmd/parfor (https://www.mathworks.com/matlabcentral/answers/233648-spmd-and-persistent-variables), but the solution doesn't appear to work when applied to object classes.
Here's a very simplified example where I'd like the persistent variable inside my object class to persist across parfor iterations called by another method in the class:
classdef example
methods(Static)
function output = parforFun(n)
output = cell(n,1);
parfor x = 1:n
output{x} = example.persistentFun;
% Do something with output (not shown here)
end
output = cell2mat(output); % Ideally, every index of "output" is identical
end
function output = persistentFun
% Return the persistent variable myConstant
persistent myConstant
if isempty(myConstant)
myConstant = rand; % Define the term. Real code involves calling web service.
end
output = myConstant;
end
end
end
Why do this, you ask? In my real application, "example" is a web service class I created. I'm getting an authentication token to a web service in persistentFun so I don't want each worker to ask for its own authentication token. ***Importantly, I can't move the persistentFun call before the parfor and then distribute the token, because the token expires periodically and may expire while my parfor is running. Therefore, I want to call PersistentFun inside the parfor and not before, then let persistentFun determine when to request a new token and distribute to all workers. Also, I'd like to avoid the clunky hack of writing the token to a file and then fetching it, if possible.
Back to the example....
Now call output = example.parforFun(5) and see if the value of output is different for every index, which would indicate each parfor is using a different value for persistent myConstant.
output = example.parforFun(5)
output =
0.350625385914416
1.91633571411975
1.85656023059334
1.17686837315114
4.30311363531808
You can see that the persistent "myConstant" changes between parfor calls when I would like for it to be constant. Of course, if you set a large enough input value (approaching or greater than number of workers) then you start to see repeating values, which indicates that each worker is starting to get called more than once and is retrieving its persistent variable.
By the way, the idea of putting into a separate file (i.e. creating a @example folder, adding to path, and putting everything in there) and using "addAttachedFiles(gcp,{'persistentFun.m'})" doesn't seem to resolve this issue.
I'm not seeing an obvious way to do this without writing the token to a file and then reading, but I'd like to avoid this. Any ideas?
Thanks,
Paul S
2 Commenti
Walter Roberson
il 3 Set 2019
I suggest using parfeval() to dispatch workers and parfEvalOnAll to update the tokens.
Paul Shoemaker
il 3 Set 2019
Risposta accettata
Più risposte (3)
Here's one way to do it, sort of along the lines that Walter proposed, but with externally scoped variables instead of persistent variables,
function test
n=10;
Token=rand;
updateExecuted=false(1,n);
tic;
for i = 1:n
future(i) = parfeval(@LongTask,1);
if rand>0.7%Simulate token updates at random times
updateToken();
updateExecuted(i)=true;
end
end
toc; %Elapsed time is 0.026488 seconds.
tic;
for i = 1:n
[completedIdx,value] = fetchNext(future);
output(completedIdx) = value;
end
toc; %Elapsed time is 5.022906 seconds.
output,
updateExecuted,
function t=LongTask
t=Token;
pause(5); %simulate some fake time-consuming step
end
function updateToken
Token=rand;
end
end
Executing this on (12 workers), we see that the output of the workers remains constant between the points where token updates occurred.
>> test
output =
0.9303 0.9303 0.9303 0.9303 0.9303 0.9750 0.9750 0.7657 0.7657 0.7657
updateExecuted =
1×10 logical array
0 0 0 0 1 0 1 0 0 0
6 Commenti
Walter Roberson
il 3 Set 2019
The disadvantage of the above is that all of the workers get queued at the beginning, just some with different tokens. Instead what you need to do is only queue enough to complete within the token update period.
Walter Roberson
il 3 Set 2019
Matt, I do not recall at the moment: do externally scoped variables get updated on workers, and if so at what point? Surely not immediately ?
Because if a shared variable did get updated nearly immediately on the children, then the structure of queuing everything could work, provided that you do not take the local copy of the token,
t=Token;
I am thinking about the issue that you might take a local copy of a 30 minute token just before a new one is available, which would be a problem if you only have (say) 10 minutes left in token validity.
My understanding was that there is no local copy of externally scoped variables. The worker communicates back to the host every time it needs to be read or written. If that were not true, it is not clear to me why, in my example, the token values returned by the workers reflect the token updates made on the host.
Walter Roberson
il 4 Set 2019
Interesting. That sounds expensive though.
I guess it's also possible the value is cloned to the worker when parFeval is issued... I really don't know.
But I think in the scenario described by the OP, a decision is made at some point by the worker to accept a token as valid. So, some desynchronization with the host copy of the token must inevitably be tolerated.
Paul Shoemaker
il 4 Set 2019
Paul Shoemaker
il 4 Set 2019
0 voti
Paul Shoemaker
il 15 Set 2019
Categorie
Scopri di più su Background and Parallel Processing in Centro assistenza e File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!