Merge remote-tracking branch 'MajewskiKrzysztof/keep_client_delete_method' into 18655...
[arvados.git] / doc / user / cwl / cwl-style.html.textile.liquid
1 ---
2 layout: default
3 navsection: userguide
4 title: Guidelines for Writing High-Performance Portable Workflows
5 ...
6 {% comment %}
7 Copyright (C) The Arvados Authors. All rights reserved.
8
9 SPDX-License-Identifier: CC-BY-SA-3.0
10 {% endcomment %}
11
12 h2(#performance). Performance
13
14 To get the best perfomance from your workflows, be aware of the following Arvados features, behaviors, and best practices.
15
16 Does your application support NVIDIA GPU acceleration?  Use "cwltool:CUDARequirement":cwl-extensions.html#CUDARequirement to request nodes with GPUs.
17
18 If you have a sequence of short-running steps (less than 1-2 minutes each), use the Arvados extension "arv:RunInSingleContainer":cwl-extensions.html#RunInSingleContainer to avoid scheduling and data transfer overhead by running all the steps together in the same container on the same node.  To use this feature, @cwltool@ must be installed in the container image.  Example:
19
20 {% codeblock as yaml %}
21 class: Workflow
22 cwlVersion: v1.0
23 $namespaces:
24   arv: "http://arvados.org/cwl#"
25 inputs:
26   file: File
27 outputs: []
28 requirements:
29   SubworkflowFeatureRequirement: {}
30 steps:
31   subworkflow-with-short-steps:
32     in:
33       file: file
34     out: [out]
35     # This hint indicates that the subworkflow should be bundled and
36     # run in a single container, instead of the normal behavior, which
37     # is to run each step in a separate container.  This greatly
38     # reduces overhead if you have a series of short jobs, without
39     # requiring any changes the CWL definition of the sub workflow.
40     hints:
41       - class: arv:RunInSingleContainer
42     run: subworkflow-with-short-steps.cwl
43 {% endcodeblock %}
44
45 Avoid declaring @InlineJavascriptRequirement@ or @ShellCommandRequirement@ unless you specifically need them.  Don't include them "just in case" because they change the default behavior and may add extra overhead.
46
47 When combining a parameter value with a string, such as adding a filename extension, write @$(inputs.file.basename).ext@ instead of @$(inputs.file.basename + 'ext')@.  The first form is evaluated as a simple text substitution, the second form (using the @+@ operator) is evaluated as an arbitrary Javascript expression and requires that you declare @InlineJavascriptRequirement@.
48
49 Use @ExpressionTool@ to efficiently rearrange input files between steps of a Workflow.  For example, the following expression accepts a directory containing files paired by @_R1_@ and @_R2_@ and produces an array of Directories containing each pair.
50
51 {% codeblock as yaml %}
52 class: ExpressionTool
53 cwlVersion: v1.0
54 inputs:
55   inputdir: Directory
56 outputs:
57   out: Directory[]
58 requirements:
59   InlineJavascriptRequirement: {}
60 expression: |
61   ${
62     var samples = {};
63     for (var i = 0; i < inputs.inputdir.listing.length; i++) {
64       var file = inputs.inputdir.listing[i];
65       var groups = file.basename.match(/^(.+)(_R[12]_)(.+)$/);
66       if (groups) {
67         if (!samples[groups[1]]) {
68           samples[groups[1]] = [];
69         }
70         samples[groups[1]].push(file);
71       }
72     }
73     var dirs = [];
74     for (var key in samples) {
75       dirs.push({"class": "Directory",
76                  "basename": key,
77                  "listing": [samples[key]]});
78     }
79     return {"out": dirs};
80   }
81 {% endcodeblock %}
82
83 Available compute nodes types vary over time and across different cloud providers, so try to limit the RAM requirement to what the program actually needs.  However, if you need to target a specific compute node type, see this discussion on "calculating RAM request and choosing instance type for containers.":{{site.baseurl}}/api/execution.html#RAM
84
85 Instead of scattering separate steps, prefer to scatter over a subworkflow.
86
87 With the following pattern, @step1@ has to wait for all samples to complete before @step2@ can start computing on any samples.  This means a single long-running sample can prevent the rest of the workflow from moving on:
88
89 {% codeblock as yaml %}
90 cwlVersion: v1.0
91 class: Workflow
92 inputs:
93   inp: File
94 steps:
95   step1:
96     in: {inp: inp}
97     scatter: inp
98     out: [out]
99     run: tool1.cwl
100   step2:
101     in: {inp: step1/inp}
102     scatter: inp
103     out: [out]
104     run: tool2.cwl
105   step3:
106     in: {inp: step2/inp}
107     scatter: inp
108     out: [out]
109     run: tool3.cwl
110 {% endcodeblock %}
111
112 Instead, scatter over a subworkflow.  In this pattern, a sample can proceed to @step2@ as soon as @step1@ is done, independently of any other samples.
113 Example: (note, the subworkflow can also be put in a separate file)
114
115 {% codeblock as yaml %}
116 cwlVersion: v1.0
117 class: Workflow
118 steps:
119   step1:
120     in: {inp: inp}
121     scatter: inp
122     out: [out]
123     run:
124       class: Workflow
125       inputs:
126         inp: File
127       outputs:
128         out:
129           type: File
130           outputSource: step3/out
131       steps:
132         step1:
133           in: {inp: inp}
134           out: [out]
135           run: tool1.cwl
136         step2:
137           in: {inp: step1/inp}
138           out: [out]
139           run: tool2.cwl
140         step3:
141           in: {inp: step2/inp}
142           out: [out]
143           run: tool3.cwl
144 {% endcodeblock %}
145
146
147 h2. Portability
148
149 To write workflows that are easy to modify and portable across CWL runners (in the event you need to share your workflow with others), there are several best practices to follow:
150
151 Workflows should always provide @DockerRequirement@ in the @hints@ or @requirements@ section.
152
153 Build a reusable library of components.  Share tool wrappers and subworkflows between projects.  Make use of and contribute to "community maintained workflows and tools":https://github.com/common-workflow-library and tool registries such as "Dockstore":http://dockstore.org .
154
155 CommandLineTools wrapping custom scripts should represent the script as an input parameter with the script file as a default value.  Use @secondaryFiles@ for scripts that consist of multiple files.  For example:
156
157 {% codeblock as yaml %}
158 cwlVersion: v1.0
159 class: CommandLineTool
160 baseCommand: python
161 inputs:
162   script:
163     type: File
164     inputBinding: {position: 1}
165     default:
166       class: File
167       location: bclfastq.py
168       secondaryFiles:
169         - class: File
170           location: helper1.py
171         - class: File
172           location: helper2.py
173   inputfile:
174     type: File
175     inputBinding: {position: 2}
176 outputs:
177   out:
178     type: File
179     outputBinding:
180       glob: "*.fastq"
181 {% endcodeblock %}
182
183 You can get the designated temporary directory using @$(runtime.tmpdir)@ in your CWL file, or from the @$TMPDIR@ environment variable in your script.
184
185 Similarly, you can get the designated output directory using $(runtime.outdir), or from the @HOME@ environment variable in your script.
186
187 Avoid specifying resource requirements in CommandLineTool.  Prefer to specify them in the workflow.  You can provide a default resource requirement in the top level @hints@ section, and individual steps can override it with their own resource requirement.
188
189 {% codeblock as yaml %}
190 cwlVersion: v1.0
191 class: Workflow
192 inputs:
193   inp: File
194 hints:
195   ResourceRequirement:
196     ramMin: 1000
197     coresMin: 1
198     tmpdirMin: 45000
199 steps:
200   step1:
201     in: {inp: inp}
202     out: [out]
203     run: tool1.cwl
204   step2:
205     in: {inp: step1/inp}
206     out: [out]
207     run: tool2.cwl
208     hints:
209       ResourceRequirement:
210         ramMin: 2000
211         coresMin: 2
212         tmpdirMin: 90000
213 {% endcodeblock %}