afterAll

Specify a function to invoke after all parallel.Futures complete

Description

example

outputFuture = afterAll(futures,funtocall,nout) automatically evaluates funtocall on the output arguments of all the futures in futures when they are all complete, and returns outputFuture to hold the result. afterAll evaluates funtocall on the vertical concatenation of the output arguments of all futures. If the number of output arguments of the elements in futures differ, afterAll uses the minimum and disregards the ending output arguments. afterAll invokes funtocall with nout output arguments.

A useful application for afterAll is to update user interfaces such as plots and apps during parallel computations using parfeval. For example, you can send several computations to workers using parfeval and update your user interface when all of them finish using afterAll.

example

outputFuture = afterAll(futures,funtocall,nout,'PassFuture',passFuture) behaves the same if passFuture is false. If passFuture is true, afterAll invokes funtocall on the array of futures futures and not on their output arguments. This happens even if elements of futures encountered errors.

Examples

collapse all

You can use afterAll to automatically invoke functions on all of the combined outputs of your parfeval computations.

Use parfeval to compute random vectors in the workers. With default preferences, parfeval creates a parpool automatically if there is not one already created.

for idx = 1:10
    f(idx) = parfeval(@rand, 1, 1000, 1);
end

Display the maximum element among all of those vectors after they are created. afterAll executes the function handle on the combined output of all the futures when they all become ready.

afterAll(f, @(r) disp(max(r)), 0);
    0.9998

You can combine afterEach and afterAll to automatically invoke more functions on the results of futures. Both afterEach and afterAll generate future variables that can be used again in afterEach and afterAll.

Use parfeval to compute random vectors in the workers. With default preferences, parfeval creates a parpool automatically if there is not one already created.

for idx= 1:10
    f(idx) = parfeval(@rand, 1, 1000, 1);
end
Starting parallel pool (parpool) using the 'local' profile ...
connected to 8 workers.

Compute the largest element in each of those vectors when they become ready. afterEach executes the function handle on the output of each future when they become ready and creates another future to hold the results.

maxFuture = afterEach(f, @(r) max(r), 1);

To compute the minimum value among them, call afterAll on this new future. afterAll executes a function on the combined output arguments of all the futures after they all complete. In this case, afterAll executes the function min on the outputs of maxFuture after completing and creates another future to hold the result.

minFuture = afterAll(maxFuture, @(r) min(r), 1);

You can fetch the result using fetchOutputs. fetchOutput waits until the future completes to gather the results.

fetchOutputs(minFuture)
ans = 0.9973

You can check the result of afterEach by calling fetchOutputs on its future variable.

fetchOutputs(maxFuture)
ans = 10×1

    0.9996
    0.9989
    0.9994
    0.9973
    1.0000
    1.0000
    0.9989
    0.9994
    0.9998
    0.9999

You can perform asynchronous computations on workers using parfeval and leave the user interface responsive. Use afterEach to update the user interface when intermediate computations are ready. Use afterAll to update the user interface when all the computations are ready.

Create a simple user interface using a waitbar.

h = waitbar(0, 'Waiting...');

Use parfeval to carry out time-consuming computations in the workers, for example, eigenvalues of random matrices. The computations happen asynchronously and the user interface updates during computation. With default preferences, parfeval creates a parpool automatically if there is not one already created.

for idx = 1:100
    f(idx) = parfeval(@(n) real(eig(randn(n))), 1, 5e2); 
end

Compute the largest value in each of the computations when they become ready using afterEach. Update the proportion of finished futures in the waitbar when each of them completes using afterEach.

maxFuture = afterEach(f, @max, 1);
updateWaitbarFuture = afterEach(f, @(~) waitbar(sum(strcmp('finished', {f.State}))/numel(f), h), 1);

Close the waitbar when all the computations are done. Use afterAll on updateWaitbarFuture to continue automatically with a close operation. afterAll obtains the figure handle from updateWaitbarFuture and executes its function on it.

closeWaitbarFuture = afterAll(updateWaitbarFuture, @(h) delete(h), 0);

Show a histogram after all the maximum values are computed. Use afterAll on maxFuture to continue the operation automatically. afterAll obtains the maximum values from maxFuture and calls histogram on them.

showsHistogramFuture = afterAll(maxFuture, @histogram, 0);

When computations for future variables result in an error, by default, afterAll does not evaluate its function. If you want to handle any errors, for example, you have a user interface that you want to update, you can use the name-value pair PassFuture. When set to true, the future variable is passed to the callback function. You can call fetchOutputs on it, process the outputs, and handle any possible errors.

Send computations to the workers using parfeval. With default preferences, parfeval creates a parpool automatically if there is not one already created. If your parfeval computations result in an error, the future variable errors, and its Error property reflects it.

errorFuture = parfeval( @(n) randn(n), 0, 0.5);
Starting parallel pool (parpool) using the 'Local' profile ...
connected to 4 workers.
wait(errorFuture);
errorFuture.Error
ans = 
  ParallelException with properties:

     identifier: 'MATLAB:NonIntegerInput'
        message: 'Size inputs must be integers.'
          cause: {}
    remotecause: {[1×1 MException]}
          stack: [1×1 struct]

If you use afterAll on that future, the callback function is not evaluated. In the code below, the msgbox is not executed because the future errors.

afterAll(errorFuture, @() msgbox('Operation completed'), 0);

To handle futures that result in an error, use the name-value pair PassFuture when calling afterAll. The future variable is passed to the callback function instead of its outputs. Call fetchOutputs on it, and process its outputs. If the future results in an error, fetchOutputs throws an error that you can catch and handle. For example, the following code shows an error dialog box.

afterAll(errorFuture, @handleError, 0, 'PassFuture',true);

function handleError (f)
try
    output = fetchOutputs(f);
    % Do something with the output
catch
    errordlg('Operation failed');
end
end

Input Arguments

collapse all

Futures, specified as an array of parallel.Future. funtocall is invoked on the combined outputs of all its elements when they all become ready. You can use parfeval to create futures.

If any element of futures encounters an error, funtocall is not invoked, and outputFuture completes with an error. To see if the future completes with an error, you can check the Error property of outputFuture. If you cancel an element of futures, this results in the same behavior as if the element encountered an error.

Example: future = parfeval(@rand,1,1000,1); afterAll(future,@max,1);

Data Types: parallel.Future

Function to execute, specified as a function to call on the combined output arguments of all the futures in futures when they all become ready. funtocall is evaluated on the MATLAB® client, not on the parallel pool workers.

Example: funtocall = @max

Data Types: function handle

Number of outputs, specified as an integer, expected from funtocall.

Example: afterAll(futures,@max,1)

Data Types: scalar

Indicator, specified as a logical scalar that determines the type of input arguments to funtocall. If set to true, the future array futures is passed to funtocall. Otherwise, the output arguments of all futures in futures are passed to funtocall. This argument is optional and is false by default.

You can use this approach if you want to handle any errors. Set passFuture to true so that afterAll invokes funtocall on the outputs of futures, even if they encountered errors. You must call fetchOutputs on the input argument to funtocall to extract the results. If the future results in an error, fetchOutputs throws an error that you can catch and handle.

Example: afterAll(futures,@(f) disp(fetchOutputs(f)),0,'PassFuture',true)

Data Types: logical scalar

Output Arguments

collapse all

Future, returned as a parallel.Future to hold the results of evaluating funtocall on the combined output arguments of all the futures in futures when they all become ready.

To extract the results, call fetchOutputs on outputFuture:

outputFutures = afterAll(futures,funtocall,nout);
[out1,out2,...,outM] = fetchOutputs(outputFutures);
Note that this is equivalent to calling funtocall after fetching the outputs in futures, except that afterAll calls automatically funtocall when all elements in futures complete:
[tmp1,tmp2,...,tmpN] = fetchOutputs(futures);
[out1,out2,...,outM] = funtocall(tmp1,tmp2,...tmpN)
N is the number of outputs from futures and M is the number of outputs specified in afterAll with the input argument nout.

Tips

  • Use afterAll on any of the futures returned from parfeval, parfevalOnAll, afterEach, afterAll, or an array containing a combination of them. For example, use afterAll to automatically invoke more functions on the results of another afterEach or afterAll. You can invoke afterAll on futures before and after they finish.

  • Use cancel on a future returned from afterAll to cancel its execution. If you invoke afterAll on a canceled future, this results in the same behavior as if the future encountered an error.

Introduced in R2018a