-
Notifications
You must be signed in to change notification settings - Fork 1
/
qsubcompile.m
187 lines (171 loc) · 7.4 KB
/
qsubcompile.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
function [fcomp] = qsubcompile(fname, varargin)
% QSUBCOMPILE compiles your function into an standalone executable that can easily
% be distributed on a cluster by QSUBCELLFUN. Running a compiled version of your
% function does not take any additional MATLAB licenses. Note that it does require
% that the corresponding MATLAB run-time environment (MCR) is installed on your
% cluster.
%
% Use as
% compiledfun = qsubcompile(fname)
% argout = qsubcellfun(compiledfun, argin, ...)
% or
% compiledfun = qsubcompile(fname)
% jobid = qsubfeval(compiledfun, argin, ...)
% argout = qsubget(jobid)
%
% Optional input arguments should be specified in key-value pairs
% and can include
% batchid = string that is used for the compiled application filename
% and to identify the jobs in the queue, the default is
% automatically determined and looks like user_host_pid_batch.
% toolbox = string or cell-array with strings, additional Mathworks
% toolboxes to include (see below).
% executable = string with the name of a previous compiled executable
% to start, which usually takes the form "run_xxx.sh". This
% implies that compilation does not have to be done.
% numthreads = number of threads, can be 1 or inf (default = 1)
%
% When executing a single batch of jobs using QSUBCELLFUN, you can also
% compile your function on the fly with the compile flag like this
% argout = qsubcellfun(fname, argin, ..., 'compile', 'yes')
% Using this syntax, the compiled function will be automatically cleaned
% up immediately after execution.
%
% If you need to include additional functions that are not automatically
% detected as dependencies by the MATLAB compiler, e.g. because using
% constructs like feval(sprintf(...)), you can specify fname as a
% cell-array. For example
% compiledfun = qsubcompile({@ft_definetrial, @trialfun_custom})
%
% If you need to include Mathworks toolboxes that are not automatically
% detected as dependencies by the MATLAB compiler, you can specify them
% like this
% compiledfun = qsubcompile(fname, 'toolbox', {'signal', 'image', 'stats'})
%
% A common problem for compilation is caused by the use of addpath in
% your startup.m file. Please change your startup.m file into
% if ~isdeployed
% % here goes the original content of your startup file
% % ...
% end
%
% If you want to execute the same function multiple times with different input
% arguments, you only have to compile it once. The name of the executable can be
% specified as input parameter, and the specified function within the executable
% can be re-execured. An example is specyfying the executable as run_fieldtrip.sh,
% which is a compiled version of the complete FieldTrip toolbox.
%
% See also QSUBCELLFUN, QSUBFEVAL, MCC, ISDEPLOYED
% -----------------------------------------------------------------------
% Copyright (C) 2012-2016, Robert Oostenveld
%
% This program is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program. If not, see <http://www.gnu.org/licenses/
%
% $Id$
% -----------------------------------------------------------------------
% Note that the user-function will be wrapped into qsubexec, which takes care of
% the loading and saving of the input and output arguments.
% It is possible for the user to have multiple MATLAB sessions running at the same
% time (on the same or different computers) and to start multiple instances of
% qsubcellfun. To ensure that those do not interfere with each others, each batch
% of jobs (i.e. instance of qsubcellfun) should get a unique identifier that is
% used in the filename of the temporary mat files.
% get the optional input arguments
batch = ft_getopt(varargin, 'batch', getbatch()); % this is a number that is automatically incremented
batchid = ft_getopt(varargin, 'batchid', generatebatchid(batch)); % this is a string like user_host_pid_batch
toolbox = ft_getopt(varargin, 'toolbox', {});
executable = ft_getopt(varargin, 'executable');
numthreads = ft_getopt(varargin, 'numthreads', 1); % our experience is that in many cases it is better to have multiple jobs on a single machine instead of a single multi-threaded job
if ischar(toolbox)
% this should be a cell-array with strings
toolbox = {toolbox};
end
% some temporary filse are made during compilation, these flags determine
% whether they can be cleaned up afterwards
hasreadme = exist('./readme.txt', 'file');
hasmcclog = exist('./mccExcludedFiles.log', 'file');
if iscell(fname)
fdeps = fname(2:end); % this remains a cell-array
fname = fname{1}; % this is a handle or string
else
fdeps = {};
end
if isa(fname, 'function_handle')
% convert the function handle back into a string (e.g. @plus should be 'plus')
fname = func2str(fname);
end
for i=1:length(fdeps)
if isa(fdeps{i}, 'function_handle')
fdeps{i} = func2str(fdeps{i});
end
end
if numthreads==1
% pass the singleCompThread option to the MCR
runtimeopt = '-nodisplay,-singleCompThread';
elseif numthreads>0 && isinf(numthreads)
% the default behavior of the MCR is to start as many computational threads as cores available
runtimeopt = '-nodisplay';
else
error('unsupported number of threads (%d)', numthreads);
end
if ~isempty(toolbox)
% each toolbox should be added to the mcc command line as -p <full_path>
toolboxopt = cell(1,length(toolbox));
for i=1:length(toolbox)
% find the directory where the toolbox is to be found
toolboxpath = fileparts(which(fullfile(toolbox{i}, 'Contents.m')));
if isempty(toolboxpath)
error('the Mathworks toolbox "%s" could not be found', toolbox{i});
else
fprintf('including %s\n', toolboxpath);
end
toolboxopt{2*(i-1)+1} = '-p';
toolboxopt{2*(i-1)+2} = toolboxpath;
end
else
toolboxopt = {};
end
if ~isempty(executable)
% compilation is not needed, just wrap the user-specified executable and the function name
% into the output structure for use by qsubcellfun or qsubfexec
[p, f, x] = fileparts(executable);
if isempty(p) || p(1)~='/'
p = fullfile(pwd, p);
end
% precisely locate the executable, including the full path
executable = fullfile(p, [f x]);
if ~exist(executable, 'file')
error('executable "%s" does not exist', executable);
end
fprintf('using "%s"\n', executable);
else
% try to compile into a stand-allone application
fprintf('compiling %s into %s\n', fname, batchid);
% ensure that cellfun is included, it might be needed for stacked jobs
mcc('-N', '-R', runtimeopt, '-o', batchid, toolboxopt{:}, '-m', 'qsubexec', 'cellfun', fname, fdeps{:});
fprintf('finished compiling\n');
executable = fullfile(pwd, sprintf('run_%s.sh', batchid));
end
if ~hasreadme
delete('./readme.txt');
end
if ~hasmcclog
delete('./mccExcludedFiles.log');
end
% remember all details
fcomp.fname = fname;
fcomp.fdeps = fdeps;
fcomp.batch = batch;
fcomp.batchid = batchid;
fcomp.executable = executable;