Search code examples
matlabparfor

fitoption cleared inside parfor?


I'm trying to speedup the fit of a large amount of data with MATLAB by using parallelism, and I'm facing quite a strange problem.

I tried to isolate the problem hoping someone might have a clue about what's happening. Here's the sample code:

f = @(a, x) a*x;
fitopt = fitoptions(f);

for i = 1:5
  disp(fitopt);
end

If I run the code above, the contents of fitopt will be written 5 times as expected. However, if I replace the for by a parfor loop, fitopt is set to empty and nothing is displayed.

Any clue what could be going wrong?

My config: MATLAB 2014a on windows 7, 64 bit


Solution

  • The following is based on MATLAB version R2016b...

    As best as I can tell, this appears to be an issue with MATLAB's ability to correctly broadcast (or not) certain class objects to the parfor workers. The fitoptions function returns an object of class curvefit.nlsqoptions in your example. Going through the Curve Fitting Toolbox code, one can see that these objects are still defined using an older UDD class system (a good introduction by Donn Shull can be found on Undocumented MATLAB). Although the newer MCOS (MATLAB Class Object System) was introduced many years ago, you can still find a number of objects defined using UDD.

    In your example, fitopt should be broadcast (i.e. copied) to each of the workers, but instead fitopt is set to [] within the parfor loop. If we try this with another class object defined using the newer class system (like those created by fittype), then the broadcasting works fine. For example, this test code:

    f = @(a, x) a*x;
    uddObj = fitoptions(f);
    mcosObj = fittype(f);
    
    parfor i = 1:3
      fprintf('\nWorker %d:\n', i);
      uddObj
      mcosObj
    end
    

    produces this output:

    Worker 2:
    
    uddObj =
         []
    
    mcosObj = 
         General model:
         mcosObj(a,x) = a*x
    
    Worker 1:
    
    uddObj =
         []
    
    mcosObj = 
         General model:
         mcosObj(a,x) = a*x
    
    Worker 3:
    
    uddObj =
         []
    
    mcosObj = 
         General model:
         mcosObj(a,x) = a*x
    

    So, in short, it appears that parfor loops can't correctly broadcast UDD style objects. One workaround would be to create these objects within the parfor loop, avoiding the broadcasting:

    f = @(a, x) a*x;
    
    parfor i = 1:5
      fitopt = fitoptions(f);  % Function handle f broadcasts just fine
      disp(fitopt);
    end
    

    You could also create a sliced variable by creating an array of objects that you index into:

    f = @(a, x) a*x;
    fitopt = repmat(fitoptions(f), [1 5]);
    
    parfor i = 1:5
      disp(fitopt(i));
    end