Code covered by the BSD License  

Highlights from
taskMap: show your distributed computing tasks

image thumbnail

taskMap: show your distributed computing tasks

by

 

charts completed and running tasks for a distributed computing job for each worker

taskMap( job, varargin )
function varargout = taskMap( job, varargin )
%taskMap: Create visual timing report for distributed tasks
%
%   taskMap(job) creates a chart showing how the completed and
%   running tasks for the specified job are allocated between workers on a
%   cluster over time. If the job supplied is still running then the chart
%   will be updated every few seconds. When the job is finished the
%   function will return. Press CTRL-C or close the window to stop the live
%   updating.
%
%   taskMap(job,param,value,...) also specifies one or more optional
%   parameter values. Available parameters that can be set are:
%    'RefreshPeriod'   interval (in seconds) between updates: default 5
%    'FigureSize'      size (in pixels) of the window: default [1000 400]
%
%   Examples:
%   >> jm = findResource( 'scheduler', 'type', 'jobmanager' );
%   >> job = jm.createJob( 'Name', 'Example job' );
%   >> for ii=1:100, job.createTask( @() pause(10+10*rand(1)), 0 ); end;
%   >> job.submit();
%   >> taskMap( job, 'RefreshPeriod', 10 );
%
%   See also: findResource
%             createJob
%             createTask
%             submit

%   Original version ("plottasks") by Russell Goyder.
%   Revisions by David Sampson and Ben Tordoff.

%   Copyright 2005-2010 The MathWorks Ltd
%   $Revision: 35 $Date: 2010-04-27$

% Check inputs
error( nargchk( 1, inf, nargin ) );


% Parse options
refreshPeriod = 5;
figSize = [1000 400];
if nargin > 1
    params = varargin(1:2:end);
    values = varargin(2:2:end);
    if numel( params ) ~= numel( values )
        error( 'taskMap:BadSyntax', 'Optional parameters must be specified as parameter/value pairs' );
    end
    for ii=1:numel( params )
        switch upper(params{ii})
            case 'REFRESHPERIOD'
                refreshPeriod = values{ii};
            case 'FIGURESIZE'
                figSize = values{ii};
            otherwise
                error( 'taskMap:BadParameter', 'Optional parameter ''%s'' is not supported', params{ii} );
        end
    end
end

jobName = job.Name;

f = figure( 'Color', 'w', 'Name', jobName, ...
    'PaperPositionMode', 'auto', ...
    'InvertHardcopy', 'off' );
cbax = axes( 'parent', f, 'OuterPosition', [0.92 0 0.05 1] ); % Colorbar
ax = axes('parent',f); % Main axes

% Resize the figure

pos = get(f,'Position');
delta = figSize - pos(1,3:4);
set( f, 'Position', pos(1,:) + [-delta/2 delta] );
drawnow();

tms = [];
iter = 0;
max_iters = inf;
while ~strcmp( job.State, 'finished' ) && ishandle( ax ) && iter < max_iters
    iter = iter + 1;
    tms = iReplot( ax, cbax, job );
    pause( refreshPeriod )
end

% Plot a final time
if ishandle( ax )
    tms = iReplot( ax, cbax, job );
end

% Move the final tick to be completion time. If the completion time is
% quite far from the final tick we add one in, otherwise move the existing
% one.
xtick = get( ax, 'XTick' );
xtickSpacing = mean( diff( xtick ) );
parallelTime = max( tms(:,2) );
if parallelTime - xtick(end) > 0.4*xtickSpacing
    % Add one
    xtick(end+1) = parallelTime;
else
    % Move the last tick
    xtick(end) = parallelTime;
end
set( ax, 'XTick', xtick );


% perhaps return handle to the figure
if nargout > 0
    varargout{1} = f;
end


%-------------------------------------------------------------------------%
function tms = iReplot( ax, cbax, job )

% If the plot is dead, return
if ~ishandle( ax )
    return;
end

% Check the job run-time
jobStartTime = datenum(job.StartTime,'ddd mmm dd HH:MM:SS zzz yyyy');
if strcmp( job.State, 'finished' )
    % Set job end time to be the finish time
    jobMaxTime = datenum(job.FinishTime,'ddd mmm dd HH:MM:SS zzz yyyy');
else
    % Still running, so use current time as end time
    javaCurrentTime = java.util.Date();
    jobMaxTime = datenum( char( javaCurrentTime.toString() ), ...
        'ddd mmm dd HH:MM:SS zzz yyyy' );
end

% get list of workers used for this job
tasks = job.Tasks;
nTasks = length( tasks );
allWorkerNames = cell( nTasks, 1 );
keep = true( nTasks, 1 );
maxNameLength = 30;
for ii = 1:nTasks
    worker = tasks(ii).Worker;
    if isempty( worker )
        allWorkerNames{ii} = '';
        keep(ii) = false;
    else
        if length( worker.Name ) + length( worker.Hostname ) > maxNameLength
            % Too long, so use host plus last bit of worker
            spare = maxNameLength - 4 - length( worker.Hostname );
            if spare > 0
                allWorkerNames{ii} = sprintf( '%s:...%s', worker.Hostname, worker.Name(end-spare+1:end) );
            else
                allWorkerNames{ii} = worker.Hostname;
            end
        else
            allWorkerNames{ii} = sprintf( '%s:%s', worker.Hostname, worker.Name );
        end
    end
end
tasks = tasks(keep);
allWorkerNames = allWorkerNames(keep);
nTasks = sum( keep );
workerNames = flipud( unique( allWorkerNames ) );

% for each task, get start and end times
tms = nan( nTasks, 2 );
for ii = 1:nTasks,
    
    st = tasks(ii).StartTime;
    ft = tasks(ii).FinishTime;
    
    % covert to datenum
    if ~isempty( st )
        tms(ii,1) = datenum(st,'ddd mmm dd HH:MM:SS zzz yyyy');
    end
    if ~isempty( ft )
        tms(ii,2) = datenum(ft,'ddd mmm dd HH:MM:SS zzz yyyy');
    end
end

% Convert job time into a relative time
jobMaxTime = (jobMaxTime - jobStartTime)*60*60*24;

% Convert tasks into relative time too
tms = (tms - jobStartTime)*60*60*24;

% Work out the maximum time and set all missing times to this
taskMaxTime = nanmax( tms(:) );
maxTime = max( taskMaxTime, jobMaxTime );
runningTasks = isnan( tms(:,2) );
tms( isnan( tms ) ) = maxTime;


taskDurations = tms(:,2) - tms(:,1);
if all( runningTasks )
    minTaskDuration = min( taskDurations );
else
    minTaskDuration = min( taskDurations(~runningTasks) );
end
maxTaskDuration = max( taskDurations );


% set up figure
height = 0.4;
set( ax, ...
    'YTick', 1:length(workerNames), ...
    'YLimMode', 'manual', ...
    'YLim', [0 length(workerNames)+1], ...
    'YTickLabel', workerNames, ...
    'XLim', [0 max(1,maxTime)], ...
    'ZLim', [0 1.1*height], ...
    'Color', 0.9*[1 1 1], ...
    'Box', 'on' );
cla(ax);

% Create the colormap
col1 = [0.2 1 0.2];
col2 = [1 0.2 0.2];
numColours = 128;
cmap = zeros( numColours, 3 );
for ii=1:numel(col1)
    cmap(:,ii) = linspace( col1(ii), col2(ii), numColours );
end
colormap( ax, cmap )
minColLim = minTaskDuration;
maxColLim = max(maxTaskDuration,1.5*minTaskDuration);
if minColLim == maxColLim
    set( ax, 'CLim', [minColLim minColLim+1] )
else
    set( ax, 'CLim', [minColLim maxColLim] )
end

% create patches
barHalfWidth = 0.4;
ps = zeros( nTasks );
for ii = 1:nTasks,
    
    % find which worker and generate y coords
    wInd = find( strcmp( workerNames, allWorkerNames{ii} ) );
    y0 = wInd-barHalfWidth;
    y1 = wInd+barHalfWidth;
    
    % generate x coords based on times
    x0 = tms(ii,1);
    x1 = tms(ii,2);
    
    % Create the block object
    if x1 > x0
        color = taskDurations(ii);
        ps(ii) = createBevelledSurface( ax, x0, x1, y0, y1, height, color );
        set( ps(ii), 'Tag', sprintf( 'PlotTasks:Task%d:Surface', ii ) );
        
        % Draw a rectangle in the same place to make a black border
        line( 'Parent', ax, ...
            'XData', [x0 x1 x1 x0 x0], ...
            'YData', [y0 y0 y1 y1 y0], ...
            'ZData', 1.05*height*[1 1 1 1 1], ...
            'Color', 'k', ...
            'LineStyle', '-', ...
            'Tag', sprintf( 'PlotTasks:Task%d:Border', ii ) );
        
        % If space, add the label
        text( 0.5*(x0+x1), 0.5*(y0+y1), 1.05*height, sprintf( '%d', ii ), ...
            'Parent', ax, ...
            'Color', 'k', ...
            'HorizontalAlignment', 'Center', ...
            'VerticalAlignment', 'Middle', ...
            'Tag', sprintf( 'PlotTasks:Task%d:Text', ii ) );
    end
    
end

% Draw the colorbar
cla( cbax )
patch( 'Parent', cbax, ...
    'XData', [0 1 1 0], ...
    'YData', [0 0 1 1], ...
    'FaceVertexCData', [0 0 1 1]', ...
    'CDataMapping','scaled', ...
    'FaceColor','interp');
line( 'Parent', cbax, ...
    'XData', [0 1 1 0 0], ...
    'YData', [0 0 1 1 0], ...
    'Clipping', 'off', ...
    'Color', 'k' );
colormap( cbax, cmap );
axis( cbax, 'off' );

% Add the min, max and central labels
text( 0.5, 0, sprintf( '%d', round(minColLim) ), ...
    'Parent', cbax, ...
    'Color', 'k', ...
    'HorizontalAlignment', 'Center', ...
    'VerticalAlignment', 'Bottom', ...
    'Tag', 'PlotTasks:Colorbar:MinText' );
text( 0.5, 1, sprintf( '%d', round(maxColLim) ), ...
    'Parent', cbax, ...
    'Color', 'k', ...
    'HorizontalAlignment', 'Center', ...
    'VerticalAlignment', 'Top', ...
    'Tag', 'PlotTasks:Colorbar:MaxText' );
text( 0.5, 0.5, 'Task Duration (s)', ...
    'Parent', cbax, ...
    'Color', 'k', ...
    'FontWeight', 'Bold', ...
    'HorizontalAlignment', 'Center', ...
    'VerticalAlignment', 'Middle', ...
    'Rotation', 90, ...
    'Tag', 'PlotTasks:Colorbar:TimeText' );

% Add some lighting
lighting( ax, 'gouraud' );
set( ancestor( ax, 'figure' ), 'Renderer', 'ZBuffer' );
l = findall( ax, 'Tag', 'PlotTasks:Light' );
if isempty( l )
    l = light( 'Parent', ax, ...
        'Style', 'infinite', ...
        'Tag', 'PlotTasks:Light' );
end
lightangle( l, -160, 20 );

% get serial time
serialTime = sum( tms(:,2) - tms(:,1) );

% get parallel time
parallelTime = max( tms(:,2) );

% get speedup
speedupFactor = round( serialTime / parallelTime * 10 ) / 10;

% do some labelling
xlabel( ax, 'Time (s)', 'FontWeight', 'Bold' );
nTasks = length( job.Tasks );
t = title( ax, sprintf( '%d tasks. Serial time: %ds. Parallel time: %ds. \\color{blue}Speed-up: %1.1f', ...
    nTasks, round( serialTime ), round( parallelTime ), speedupFactor ) );
set( t, 'interpreter', 'tex', ...
    'FontWeight', 'Bold' );
set( ax, 'xgrid', 'on' );



%-------------------------------------------------------------------------%
function s = createBevelledSurface( ax, x0, x1, y0, y1, height, color )
% Create a rectangular "brick" with some slight bevelling on the edges

% Calculate the visual aspect ratio (this is not quite the same as returned
% by data-aspect ratio)
axPos = getpixelposition( ax );
xlim = get( ax, 'XLim' );
ylim = get( ax, 'YLim' );
aspect = [diff(xlim)/axPos(3) diff(ylim)/axPos(4) 1/axPos(4)];%get( ax, 'DataAspectRatio' );

% The bevel is based on the depth (Y size) so that it looks the same in X
% and Y directions.
width = x1 - x0;
depth = y1 - y0;
bevel = 0.2*[depth*aspect(1)/aspect(2) depth height*aspect(3)/aspect(2)];

% Set some markers moving from the outside of the brick to the inside and
% the corresponding heights at each of these points
bevelling = [0 0.05 0.15 0.3 0.5 0.75 1];
heights = [0 height-bevel(3)*fliplr(bevelling) height height];

% Create the X and Y vectors
bevelx = bevel(1)*[0 bevelling 2 inf];
bevely = bevel(2)*[0 bevelling 2 inf];

% Build the surface as a series of horizontal (X/Y) slices stacked on top
% of each other.
n = numel( heights );
X = zeros( n, 5 );
Y = zeros( size( X ) );
Z = zeros( size( X ) );
for ii=1:n
    [X(ii,:),Y(ii,:),Z(ii,:)] = iMakeSlice( width, depth, heights(ii), bevelx(ii), bevely(ii) );
end

% Draw the surface and set its colour
s = surface( x0+X, y0+Y, Z, ...
    'FaceColor', 'flat', ...
    'EdgeColor', 'none', ...
    'CData', color*ones(size(Z)), ...
    'AmbientStrength', 0.6, ...
    'DiffuseStrength', 0.6, ...
    'SpecularStrength', 0.2, ...
    'SpecularExponent', 5, ...
    'Parent', ax );


%-------------------------------------------------------------------------%
function [x,y,z] = iMakeSlice( width, depth, height, bevelx, bevely )
% Create one slice of the surface

if bevelx>=width/2
    % fully bevelled
    x = width/2 * ones(1,5);
else
    x = [bevelx width-bevelx width-bevelx bevelx bevelx];
end

if bevely>=depth/2
    % fully bevelled
    y = depth/2 * ones(1,5);
else
    y = [bevely bevely depth-bevely depth-bevely bevely];
end

z = height * ones(1,5);

Contact us