6

Inside a parfor loop, I am trying to call a function that accesses a global to no avail.

The function

function a = getA()
   global OPTIONS;
   a=OPTIONS.PROBLEM.A;
end

The loop:

parfor i=1:3
    b=getA();
end

The error:

Error using parallel_function (line 589)

Attempt to reference field of non-structure array.

What am I doing wrong?

Adriaan
  • 17,741
  • 7
  • 42
  • 75
olamundo
  • 23,991
  • 34
  • 108
  • 149
  • I propose a quick fix without rewriting your entire code in this answer: https://stackoverflow.com/a/53653710/4262057 – WJA Dec 06 '18 at 14:37

2 Answers2

8

From the documentation on parfor:

The body of a parfor-loop cannot contain global or persistent variable declarations.

In the context of your problem, i.e., calling a function within the parfor that in turn references a global, this translates into: "parfor will probably not give expected or meaningful results".

This makes perfect sense. Consider the following

Lab 1:         Lab 2: 

GetB();        GetB();

if the contents of GetB() is this:

function GetB()
    global B;

    %# do something useful

    B = rand;

 end

what will the value of B be when it is referenced on Lab 1? and on Lab 2? How are the different outcomes of rand communicated? It's going to be a mess!

Writing code suited for parfor loops can be a real pain when that code comes from something that only had normal for-loops in mind. Generally, when you know beforehand you're going to write a computationally intensive piece of Matlab code, write all functions and loops as parfor loops right from the beginning. That is the only way that bugs like these will not cost you a day on transcoding your functions.

Converting from for to parfor is not at all trivial.

Phil Goddard
  • 10,571
  • 1
  • 16
  • 28
Rody Oldenhuis
  • 37,726
  • 7
  • 50
  • 96
  • Yes, I also saw that - but it is a little ambiguous: Does that mean I cannot even reference existing global variables? the "Declaration" inside getA() is a reference to an existing global variable, not creating a new one. And is there a way to overcome this? – olamundo Aug 30 '12 at 12:00
  • 2
    @noam see last edit. The easiest fix for this is to un-global the `global` and pass it around as an argument to all functions that need it. – Rody Oldenhuis Aug 30 '12 at 12:05
  • @noam OK, last edit on this; a bit of background. Hope this helps. – Rody Oldenhuis Aug 30 '12 at 12:10
  • It is categorically not the case that calling a function within PARFOR that access `global` data is "illegal". However, as you point out, the results are quite likely to be unexpected as the different "global" variables are unconnected. – Edric Aug 30 '12 at 12:34
  • @Edric you're right, *illegal* is not true. All global variables loose their "globalness", though. – Rody Oldenhuis Aug 30 '12 at 13:11
  • Well running MATLAB 7.11.0 on Linux, `global` variables appear to exist (i.e. `exist('x','var')` returns `true`), but may fail to evaluate when you subsequently ask for the value (i.e. `tmp=x` --> `Undefined function or variable 'x'`). It may depend on which thread you are in. Personally I think this is bad behaviour on MATLAB's part... – Sanjay Manohar Aug 21 '14 at 11:18
  • Speaking about non-meaningful/unexpected results: https://stackoverflow.com/a/49688601/5211833 – Adriaan Nov 21 '18 at 08:28
6

GLOBAL data is hard to use inside PARFOR because each worker is a separate MATLAB process, and global variables are not synchronised from the client (or any other process) to the workers. It would work if you initialized the global data from a separate function on the workers. (As Rody points out, using the global keyword directly in the body of a PARFOR loop is not allowed - however, separate functions can do this). So, it would be legal to do this:

parfor ii=1:matlabpool('size')
  myFcnWhichSetsUpGlobalData(); %# defines global OPTIONS
end
parfor ii=1:N
  result(ii) = myFcnWhichUsesGlobalData(); %# reads global OPTIONS
end

I would personally attempt to remove GLOBAL data from your application - it will make it work better with PARFOR, and it will make dependencies clearer.

One further option to explore is my Worker Object Wrapper which is designed to stop you having to transfer data to the workers multiple times. You might use it this way:

options = buildOptions();
w_options = WorkerObjWrapper(options);
parfor ii=1:N
  result(ii) = myFcnNeedingOptions(ii, w_options.Value);
end
Edric
  • 23,676
  • 2
  • 38
  • 40
  • would the global variables for individual workers persist, once the control passes out of the `for` loop containing `myFcnWhichSetsUpGlobalData()` function OR control comes out of the file containing `myFcnWhichSetsUpGlobalData()` ? – Abhinav Oct 30 '17 at 12:07
  • Yes, the global variables on the worker processes continue to work as normal - but they are oblivious to changes made on other worker processes. (Same goes for persistent variables). – Edric Oct 30 '17 at 15:43
  • So when do these Glo-Cal variables actually cease to exist? When the `main()` dies ? Also, is there currently a way to access the values of variables local to a worker/processor from client program ? – Abhinav Oct 30 '17 at 23:55