%% Compare Performance of Multithreading and ProcessPool
% In this example, you compare how fast functions run on the client and on a ProcessPool. Some MATLAB functions make use of multithreading. Tasks that use these functions perform better on multiple threads than a single thread. Therefore, if you use these functions on a machine with many cores, a local cluster can perform worse than multithreading on the client.
% The supporting function clientFasterThanPool, listed at the end of this example, returns true if multiple executions are performed faster on the client than a parfor-loop. The syntax is the same as parfeval: use a function handle as the first argument, the number of outputs as the second argument, and then give all required arguments for the function.
%
% First, create a local ProcessPool.

p = parpool('local');

% Check how fast the eig function runs by using the clientFasterThanPool supporting function. Create an anonymous function with eig to represent your function call.

[~, t_client, t_pool] = clientFasterThanPool(@(N) eig(randn(N)), 0, 500)

% The parallel pool computes the answer faster than the client. Divide t_client by maxNumCompThreads to find the time taken per thread on the client.

t_client/maxNumCompThreads

% Workers are single threaded by default. The result indicates that the time taken per thread is similar on both the client and the pool, as the value of t_pool is roughly 1.5 times the value of t_client/maxNumCompThreads. The eig function does not benefit from multithreading.
% Next, check how fast the lu function runs by using the clientFasterThanPool supporting function.

[~, t_client, t_pool] = clientFasterThanPool(@(N) lu(randn(N)), 0, 500)

% The parallel pool typically computes the answer faster than the client if your local machine has four or more cores. Divide t_client by maxNumCompThreads to find the time taken per thread.

t_client/maxNumCompThreads

% This result indicates that the time taken per thread is much less on the client than the pool, as the value of t_pool is roughly 3 times the value of t_client/maxNumCompThreads. Each thread is used for less computational time, indicating that lu uses multithreading.
%
%% Define Helper Function
% The supporting function clientFasterThanPool checks whether a computation is faster on the client than on a parallel pool. It takes as input a function handle fcn and a variable number of input arguments (in1, in2, ...). clientFasterThanPool executes fcn(in1, in2, ...) on both the client and the active parallel pool. As an example, if you wish to test rand(500), your function handle must be in the following form:

fcn = @(N) rand(N);

% Then, use clientFasterThanPool(fcn,500).
%

function [result, t_multi, t_single] = clientFasterThanPool(fcn,numout,varargin)
    % Preallocate cell array for outputs
    outputs = cell(numout);
    
    % Client
    tic
    for i = 1:200
        if numout == 0
            fcn(varargin{:});
        else
            [outputs{1:numout}] = fcn(varargin{:});
        end
    end
    t_multi = toc;
    
    % Parallel pool
    vararginC = parallel.pool.Constant(varargin);
    tic
    parfor i = 1:200
        % Preallocate cell array for outputs
        outputs = cell(numout);
        
        if numout == 0
            fcn(vararginC.Value{:});
        else
            [outputs{1:numout}] = fcn(vararginC.Value{:});
        end
    end
    t_single = toc;
    
    % If multhreading is quicker, return true
    result = t_single > t_multi;
end

% Copyright 2020 The MathWorks, Inc.