Blame view

src/doc/queue.dox 46.4 KB
8dcb6dfcb   Yannick Estève   first commit
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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
  // doc/queue.dox
  
  
  // Copyright 2009-2011 Microsoft Corporation
  //                2013 Johns Hopkins University (author: Daniel Povey)
  
  // See ../../COPYING for clarification regarding multiple authors
  //
  // Licensed under the Apache License, Version 2.0 (the "License");
  // you may not use this file except in compliance with the License.
  // You may obtain a copy of the License at
  
  //  http://www.apache.org/licenses/LICENSE-2.0
  
  // THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
  // WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
  // MERCHANTABLITY OR NON-INFRINGEMENT.
  // See the Apache 2 License for the specific language governing permissions and
  // limitations under the License.
  
  namespace kaldi {
  
  /**
    \page queue Parallelization in Kaldi
  
   \section parallelization_intro Introduction
  
   Kaldi is designed to work best with software such as Sun GridEngine or other software
   that works on a similar principle; and if multiple machines are to work together
   in a cluster then they need access to a shared file system such as one based on NFS.
   However, Kaldi can easily be configured to run on a single machine.
  
   If you look at a top-level example script like <tt>egs/wsj/s5/run.sh</tt>, you'll see commands like
  \verbatim
   steps/train_sat.sh  --cmd "$train_cmd" \
     4200 40000 data/train_si284 data/lang exp/tri3b_ali_si284 exp/tri4a
  \endverbatim
   At the top of the <tt>run.sh</tt> script you'll see it sourcing a file called <tt>cmd.sh</tt>:
  \verbatim
  . ./cmd.sh
  \endverbatim
  and in <tt>cmd.sh</tt> you'll see the following variable being set:
  \verbatim
  export train_cmd="queue.pl -l arch=*64"
  \endverbatim
  You'll change this variable if you don't have GridEngine or if your queue is configured
  differently from CLSP\@JHU.   To run everything locally on a single machine you can
  set <tt>export train_cmd=run.pl</tt>.
  
  In <tt>steps/train_sat.sh</tt> the varible <tt>cmd</tt> is set to the argument
  to the <tt>--cmd</tt> option, i.e. to <tt>queue.pl -l arch=*64</tt> in this case,
  and in the script you'll see commands like the following:
  \verbatim
        $cmd JOB=1:$nj $dir/log/fmllr.$x.JOB.log \
          ali-to-post "ark:gunzip -c $dir/ali.JOB.gz|" ark:-  \| \
          weight-silence-post $silence_weight $silphonelist $dir/$x.mdl ark:- ark:- \| \
          gmm-est-fmllr --fmllr-update-type=$fmllr_update_type \
          --spk2utt=ark:$sdata/JOB/spk2utt $dir/$x.mdl \
          "$feats" ark:- ark:$dir/tmp_trans.JOB || exit 1;
  \endverbatim
  What's going on is that the command <tt>$cmd</tt> (e.g. <tt>queue.pl</tt> or <tt>run.pl</tt>)
  is being executed; it is responsible for spawning the jobs and waiting until they are done,
  and returning with nonzero status if something went wrong.  The basic usage of these
  commands (and there are others called <tt>slurm.pl</tt> and <tt>ssh.pl</tt>) is like this:
  \verbatim
   queue.pl <options> <log-file> <command>
  \endverbatim
  and the simplest possible example of using one of these scripts is:
  \verbatim
   run.pl foo.log echo hello world
  \endverbatim
  (we're using <tt>run.pl</tt> for the example because it will run on any system, it doesn't
  require GridEngine).  It's possible to run a one-dimensional array of jobs, and an example
  is:
  \verbatim
   run.pl JOB=1:10 foo.JOB.log echo hello world number JOB
  \endverbatim
  and these programs will replace any instance of JOB in the command line with a number
  within that range, so make sure that your working directory doesn't contain the string
  JOB, or bad things may happen.  You can even submit jobs with pipes and redirection by
  suitable use of quoting or escaping:
  \verbatim
   run.pl JOB=1:10 foo.JOB.log echo "hello world number JOB" \| head -n 1 \> output.JOB
  \endverbatim
  In this case, the command that actually gets executed will be something like:
  \verbatim
  echo "hello world number JOB" | head -n 1 > output.JOB
  \endverbatim
  If you want to see what's actually getting executed, you can look in a file like
  <tt>foo.1.log</tt>, where you'll see the following:
  \verbatim
  # echo "hello world number 1" | head -n 1 > output.1
  # Started at Sat Jan  3 17:44:20 PST 2015
  #
  # Accounting: time=0 threads=1
  # Ended (code 0) at Sat Jan  3 17:44:20 PST 2015, elapsed time 0 seconds
  \endverbatim
  
  
  \section parallelization_common Common interface of parallelization tools
  
  In this section we discuss the commonalities of the parallelization tools.  They
  are designed to all be interchangeable on the command line, so that a script
  that is tested for one of these parallelization tools will work for any; you can
  switch over to using another by setting the <tt>$cmd</tt> variable to a
  different value.
  
  The basic usage of these tools is, as we said,
  \verbatim
   queue.pl <options> <log-file> <command>
  \endverbatim
  and what we are about to say also holds for <tt>run.pl</tt>, <tt>ssh.pl</tt> and <tt>slurm.pl</tt>.
  
  <tt><options></tt> may include some or all of the following:
  <ul>
    <li> A job range specifier (e.g. JOB=1:10).  The name is uppercase by convention only, and may include underscores.
         The starting index must be 1 or more; this is a GridEngine limitation.
    <li> Anything that looks as if it would be accepted by GridEngine as an option to <tt>qsub</tt>.
         For example, <tt>-l arch=*64*</tt>, or <tt>-l mem_free=6G,ram_free=6G</tt>, or <tt>-pe smp 6</tt>.
         For compatibility, scripts other than <tt>queue.pl</tt> will ignore such options.
    <li> New-style options like <tt>--mem 10G</tt> (see below).
  </ul>
  
  <tt><log-file></tt> is just a filename, which for array jobs must contain the identifier of
   the array (e.g. <tt>exp/foo/log/process_data.JOB.log</tt>).
  
  <tt><command></tt> can basically be anything, including symbols that would
  be interpreted by the shell, but of course <tt>queue.pl</tt> can't process
  something if it gets interpreted by <tt>bash</tt> first.   For instance, this is WRONG:
  \verbatim
   queue.pl test.log  echo foo | awk 's/f/F/';
  \endverbatim
  because <tt>queue.pl</tt> won't even see the arguments after the pipe symbol, but will get its
  standard output piped into the awk command.  Instead you should write
  \verbatim
   queue.pl test.log  echo foo \| awk 's/f/F/';
  \endverbatim
  You need to escape or quote the pipe symbol, and also things like ";" and ">".
  If one of the arguments in the <tt><command></tt> contains a space, then
  queue.pl will assume you quoted it for a reason, and will quote it for you when
  it gets passed to bash.  It quotes using single quotes by default, but if the
  string itself contains single quotes then it uses double quotes instead.  This
  usually does what we want.  The <tt>PATH</tt> variable from the shell that
  you executed </tt>queue.pl</tt> from will be passed through to the scripts
  that get executed, and just to be certain you get everything you need,
  the file <tt>./path.sh</tt> will also be sourced.  The commands will be executed
  with bash.
  
  \subsection parallelization_common_new New-style options (unified interface)
  
  When we originally wrote Kaldi, we made the example scripts pass in options like
  <tt>-l ram_free=6G,mem_free=6G</tt> to <tt>queue.pl</tt>, when we needed to
  specify things like memory requirements.  Because the scripts
  like <tt>steps/train_sat.sh</tt> can't make assumptions about how GridEngine is
  configured or whether we are using GridEngine at all, such options had to be
  passed in from the very outer level of the scripts, which is awkward.  We have more recently
  defined a "new-style interface" to the parallelization scripts, such that they
  all accept the following types of options (examples shown):
  \verbatim
     --config conf/queue_mod.conf
     --mem 10G
     --num-threads 6
     --max-jobs-run 10
     --gpu 1
  \endverbatim
  The config file specifies how to convert new-style options into a form that
  GridEngine (or your grid software of choice) can interpret.  Currently
  only <tt>queue.pl</tt> actually interprets these options; the other scripts
  ignore them.  Our plan is to gradually modify the scripts in <tt>steps/</tt> to
  make use of the new-style options, where necessary, and to use <tt>queue.pl</tt>
  for other varieties of grid software through use of the config files (where possible).
  
  <tt>queue.pl</tt> will read <tt>conf/queue.conf</tt> if it exists; otherwise it
  will default to a particular config file that we define in the code.  The config
  file specifies how to convert the "new-style" options into options that
  GridEngine or similar software can interpret.  The following example show the
  behavior that the default config file specifies:
  <table>
   <tr> <th> New-style option </th>  <th>  Converted form (for GridEngine) </th> <th> Comment </th> </tr>
   <tr> <td> <tt>--mem 10G</tt></td>  <td> <tt>-l mem_free=10G,ram_free=10G</tt></td> <td></td> </tr>
   <tr> <td> <tt>--max-jobs-run 10</tt></td> <td> <tt>-tc 10</tt></td> <td> (We use this for jobs that cause too much I/O). </td></tr>
   <tr> <td> <tt>--num-threads 6</tt></td> <td> <tt>-pe smp 6</tt></td> <td></td>(general case) </tr>
   <tr> <td> <tt>--num-threads 1</tt></td> <td> <tt>(no extra options)</tt></td> <td>(special case)</td> </tr>
   <tr> <td> <tt>--gpu 1</tt></td> <td> <tt>-q g.q -l gpu=1 </tt></td> <td>(general case)</td> </tr>
   <tr> <td> <tt>--gpu 0</tt></td> <td> (no extra options)  </td> <td>(special case for gpu=0)</td> </tr>
  </table>
  It's also possible to add extra options with this general format, i.e. options
  that look like
  <tt>--foo-bar</tt> and take one argument.  The default configuration tabulated above works for the CLSP grid
  but may not work everywhere, because GridEngine is very configurable.  Thefore you may
  have to create a config file <tt>conf/queue.conf</tt> and edit it to work with your grid.
  The following configuration file is the one that <tt>queue.pl</tt> defaults to if <tt>conf/queue.conf</tt>
  does not exist and the <tt>--config</tt> option is not specified, and may be used as a starting
  point for your own config file:
  \verbatim
  # Default configuration
  command qsub -v PATH -cwd -S /bin/bash -j y -l arch=*64*
  option mem=* -l mem_free=$0,ram_free=$0
  option mem=0          # Do not add anything to qsub_opts
  option num_threads=* -pe smp $0
  option num_threads=1  # Do not add anything to qsub_opts
  option max_jobs_run=* -tc $0
  default gpu=0
  option gpu=0
  option gpu=* -l gpu=$0 -q g.q
  \endverbatim
  The line beginning with
  <tt>command</tt> specifies the unchanging part of the command line, and you can
  modify this to get it to use grid software other than GridEngine, or to specify
  options that you always want.  The lines beginning with
  <tt>option</tt> specify how to transform the input options such as <tt>--mem</tt>.
  Lines beginning with something like <tt>"option mem=*"</tt> handle the general case
  (the <tt>$0</tt> gets replaced with the actual argument to the option), while
  lines like <tt>"option gpu=0"</tt> allow you to specify special behavior for special
  cases of the argument, so in this case the option <tt>--gpu 0</tt> is configured
  to produce no extra options to <tt>qsub</tt> at all.  The line <tt>"default gpu=0"</tt> specifies
  that if you don't give the <tt>--gpu</tt> option at all, <tt>queue.pl</tt> should act like
  you specified <tt>--gpu 0</tt>.  In this particular configuration we could
  have omitted the line <tt>default gpu=0</tt>, because in any case the
  effect is to produce no extra command line options. We previously had it
  configured with a line: <tt>"option gpu=0 -q all.q"</tt>, so there was a time when the line
  <tt>"default gpu=0"</tt> used to make a difference.
  
  (Note: most of the time if you are configuring GridEngine yourself you will want a queue.conf
  that is the same as the one above missing the "-q g.q" option).
  
  The mapping from what the config-file specifies to what appears on the command-line
  of qsub sometimes has to be tweaked slightly in the perl code: for instance, we made it
  so that the <tt>--max-jobs-run</tt> option is ignored for non-array jobs.
  
   \subsection parallelization_common_new_example Example of configuring grid software with new-style options
  
  
  We'd like to give an example of how the config file can be used in a real
  situation.  We had a problem where, due to a bug in an outdated version of the
  CUDA toolkit that we had installed on the grid, some of our neural net training
  runs were crashing on our K20 GPU cards  but not the K10s.  We created a config
  file  <tt>conf/no_k20.conf</tt> which was as the configuration file above (search in
  the text above for <tt># Default configuration</tt>, but with the following
  lines added:
  \verbatim
  default allow_k20=true
  option allow_k20=true
  option allow_k20=false -l 'hostname=!g01*&!g02*&!b06*'
  \endverbatim
  We then set the relevant <tt>$cmd</tt> variable to the value
  <tt>queue.pl -config conf/no_k20.conf --allow-k20 false</tt>.
  Note that a simpler way to have done this would have been
  to simply edit the <tt>command</tt> line in the config file to read
  \verbatim
  command qsub -v PATH -cwd -S /bin/bash -j y -l arch=*64* -l 'hostname=!g01*&!g02*&!b06*'
  \endverbatim
  and if we had done that, it would not have been necessary to add <tt>--allow-k20 false</tt>.
  
  
  \section parallelization_specific Parallelization using specific scripts
  
  In this section we explain the things that are specific to individual
  parallelization scripts.
  
   \subsection parallelization_specific_queue Parallelization using queue.pl
  
   <tt>queue.pl</tt> is the normal, recommended way to parallelize.  It was originally
   designed for use with GridEngine, but now that we have introduced the "new-style options"
   we believe it can be configured for use with other parallelization engines, such as Tork
   or slurm.  If you develop config files that work for such engines, please
   contact the maintainers of Kaldi.  It may also be necessary to make further changes to <tt>queue.pl</tt>
   to properly support other engines, since some parts of the command line are currently
   constructed in <tt>queue.pl</tt> in a way that is not configurable from the command line:
   e.g., adding <tt>-o foo/bar/q/jobname.log</tt> to direct output from <tt>qsub</tt> itself
   to a separate log-file; and for array jobs, adding options like <tt>-t 1:40</tt> to the command
   line.  The scripts that we ask <tt>qsub</tt> to run also make use of the variable
   <tt>$SGE_TASK_ID</tt>, which SGE sets to the job index for array jobs.  Our plan is to extend
   the config-file mechanism as necessary to accommodate whatever changes are needed to support
   other grid software, within reason.
  
   Since we have explained the behavior of <tt>queue.pl</tt> at length above, we aren't going
   to provide many further details in this section, but please see below the section
   \ref parallelization_gridengine.
  
  \subsection parallelization_specific_run Parallelization using run.pl
  
   <tt>run.pl</tt> is a fall-back option in case the user
   does not have GridEngine installed.  This script is very simple; it runs all
   the jobs you request on the local machine, and it does so in parallel if you
   use a job range specifier like <tt>JOB=1:10</tt>.  in parallel on the local
   machine.  It doesn't try to keep track of how many CPUs are
   available or how much memory your machine has.  Therefore if you
   use <tt>run.pl</tt> to run scripts that were designed to run
   with <tt>queue.pl</tt> on a larger grid, you may end up exhausting the memory
   of your machine or overloading it with jobs.  We recommend to study the script
   you are running, and being particularly careful with decoding scripts that run
   in the background (with <tt>&</tt>) and with scripts that use a large number of
   jobs, e.g. <tt>--nj 50</tt>.  Generally speaking you can reduce the value of the <tt>--nj</tt>
   option without affecting the outcome, but there are some situations where
   the <tt>--nj</tt> options given to multiple scripts must match, or
   a later stage will crash.
  
   <tt>run.pl</tt> will ignore all options given to it except for the job-range specifier.
  
  \subsection parallelization_specific_ssh Parallelization using ssh.pl
  
   <tt>ssh.pl</tt> is a poor man's <tt>queue.pl</tt>, for use in case
   you have a small cluster of several machines but don't want the trouble of setting
   up GridEngine.  Like <tt>run.pl</tt>, it doesn't attempt to keep track of
   CPUs or memory; it works like <tt>run.pl</tt> except that it distributes the
   jobs across multiple machines.
   You have to create a file <tt>.queue/machines</tt>
   (where <tt>.queue</tt> is a subdirectory of the directory you are running the script from),
   where each line contains the name of a machine.  It needs to be possible to ssh to each
   of these machines without a password, i.e. you have to set up your ssh keys.
  
  
   \subsection parallelization_specific_slurm Parallelization using slurm.pl
  
   <tt>slurm.pl</tt> was written to accomodate the <tt>slurm</tt> grid management tool,
   which operates on similar principles to GridEngine.  It has not been tested very
   recently.  Probably it is now possible to set up <tt>queue.pl</tt> to use <tt>slurm</tt>
   using a suitable configuration file, which would make <tt>slurm.pl</tt> unnecessary.
  
  
  \section parallelization_gridengine Setting up GridEngine for use with Kaldi
  
   Sun GridEngine (SGE) is the open-source grid management tool that we (the
   maintainers of Kaldi) have most experience with.  Oracle now maintains SGE and
   has started calling it Oracle GridEngine.  The version that is in use at CLSP\@JHU is
   6.2u5; SGE is old and fairly stable so the precise version
   number is not too critical.  There are various open-source alternatives to SGE
   and various forks of it, but our instructions here relate to the main-line version
   which is currently maintained by Oracle.
  
   In this section we explain how to install and configure GridEngine on an
   arbitrary cluster.  If you have a cluster in Amazon's EC2 cloud and you want
   something that can take care of spinning up new nodes, you might want to look
   at MIT's StarCluster project, although we (the maintainers of Kaldi) have also
   created a project called "kluster" on Sourceforge that provides some scripts
   and documentation for the same purpose.  StarCluster was not very stable at the
   time we developed kluster, but we believe it's improved since then.
  
  
    \subsection parallelization_gridengine_installing Installing GridEngine
  
    To start with, you probably want to get a basic installation of GridEngine
    working.  In GridEngine, the queue management software runs on the "master",
    and a different set of programs runs on all the nodes.  The master can also be
    a node in the queue.  There's also a concept of a "shadow master" which is
    like a backup for the master, in case the master dies, but we won't address
    that here (probably it's just a question of installing the <tt>gridengine-master</tt>
    package on another node and setting the master to be another node, but we're not sure).
  
    Last time we checked, installing GridEngine from source was a huge pain.  Your
    life will be much easier if your distribution of Linux has a GridEngine
    package in its repositories, and choose your distribution wisely because not
    all distributions have such a package.  We'll discuss how to do this with
    Debian, because that's what we're most experienced with.
  
    To install GridEngine on the master, you'll run (on your chosen master node):
  \verbatim
    sudo apt-get install gridengine-master gridengine-client
  \endverbatim
   Select "yes" for automatic configuration.
   It will ask you for the "cell name", which you can leave as "default", and it
   will ask for the name of the "master", which you should set to the hostname of
   your chosen master.  Typically this should be the fully qualified domain name (FQDN)
   of the master, but I believe anything that resolves via hostname lookup to the
   master node should work.  Note that GridEngine is sometimes picky about about
   hostname lookup and reverse DNS lookup matching up, and GridEngine problems can
   sometimes be traced to this.  Also be aware that doing "apt-get remove" of these
   packages and reinstalling them won't give you a blank slate because Debian
   sometimes remembers your selections; this can be a pain.
  
   It will make your life easier if you add yourself as manager, so do:
  \verbatim
   sudo qconf -am <your-user-id>
  \endverbatim
   Here "am" means add manager; "dm" would mean delete manager and "sm" would mean
   show all managers.  To see the available options, do <tt>qconf -help</tt>.
  
   To install GridEngine on the normal nodes, you'll run
  \verbatim
    sudo apt-get install gridengine-client gridengine-exec
  \endverbatim
   The "cell name" should be left as "default", and the "master" should be the name of
   the master node that you previously installed.
   You can run this on the master too if the master is to run jobs also.
  
   Typing <tt>qstat</tt> and <tt>qhost -q</tt> will let you know whether things are working.
   The following is what it looks like when things are working fine (we tested this
   in the Google cloud):
  \verbatim
  dpovey_gmail_com@instance-1:~$ qstat
  dpovey_gmail_com@instance-1:~$ qhost -q
  HOSTNAME                ARCH         NCPU  LOAD  MEMTOT  MEMUSE  SWAPTO  SWAPUS
  -------------------------------------------------------------------------------
  global                  -               -     -       -       -       -       -
  instance-1.c.analytical-rig-638.internal lx26-amd64      1  0.07    3.6G  133.9M     0.0     0.0
  \endverbatim
   We don't have a fully working setup yet, we still need to configure it; we're just checking
   that the client can reach the master.  At this point, any errors likely relate to
   DNS lookup, reverse DNS lookup, or your /etc/hostname or /etc/hosts files; GridEngine
   doesn't like it when these things are inconsistent.  If you need to change the name of
   the master from what you told the installer, you may be able to do so by editing the file
  \verbatim
  /var/lib/gridengine/default/common/act_qmaster
  \endverbatim
   (at least, this is where it's located in Debian Wheezy).
  
    \subsection parallelization_gridengine_configuring Configuring GridEngine
  
   First let's make sure that a queue is defined.  GridEngine doesn't define any queues by
   default.  We'll set up a queue called <tt>all.q</tt>.  Make sure the shell variable <tt>EDITOR</tt>
   is set to your favorite shell (e.g. <tt>vim</tt> or <tt>emacs</tt>), and type as follows; and this should
   work from master or client.
  \verbatim
   qconf -aq
  \endverbatim
    This will bring up an editor.  Edit the line
  \verbatim
  qname               template
  \endverbatim
  so it says
  \verbatim
  qname               all.q
  \endverbatim
  Also change the field <tt>shell</tt> from <tt>/bin/csh</tt>
  to <tt>/bin/bash</tt>; this is a better default, although it shouldn't affect
  Kaldi.  Quitting the editor will save the changes,  although if you made syntax
  errors, <tt>qconf</tt> will reject your edits.  Later on we'll make more
  changes to this queue by typing <tt>qconf -mq all.q</tt>.
  
   GridEngine stores some global configuration values, not connected with any queue,
   which can be viewed with <tt>qconf -sconf</tt>.  We'll edit them using <tt>qconf -mconf</tt>.
   There is a line that reads
  \verbatim
  administrator_mail           root
  \endverbatim
  and if you have sending emails working from your machine (i.e. you can
  type <tt>mail foobar\@gmail.com</tt> and the mail gets delivered), then you can
  change <tt>root</tt> to an email address where you want to receive
  notifications if things go wrong.  Be advised that due to anti-spam measures,
  sending emails from the cloud is painful from EC2 and close to impossible
  from Google's cloud offering, so it may be best just to leave this field the
  way it is and make do without email notifications.
  You may also want to run <tt>qconf -msconf</tt> and edit it so that
  the schedule_interval is:
  \verbatim
  schedule_interval                 0:0:5
  \endverbatim (the default is <tt>00:00:15</tt>), which will
  give a slightly faster turnaround time for submitting jobs.
  
  GridEngine has the concept of "resources" which can be requested or specified by
  your jobs, and these can be viewed using <tt>qconf -sc</tt>.  Modify them
  using <tt>qconf -mc</tt>.  Modify the <tt>mem_free</tt> line to change the default
  memory requirement from 0 to 1G, and to make it consumable, i.e.:
  \verbatim
  #name               shortcut    type        relop requestable consumable default  urgency
  #------------------------------------------------------------------------------------------
  <snip>
  mem_free            mf         MEMORY      <=    YES         YES         1G        0
  \endverbatim
   and also add the following two new lines; it doesn't matter where in the file you add them.
  \verbatim
  #name               shortcut    type        relop requestable consumable default  urgency
  #------------------------------------------------------------------------------------------
  <snip>
  gpu                 g           INT         <=    YES         YES        0        10000
  ram_free            ram_free    MEMORY      <=    YES         JOB        1G       0
  \endverbatim
   You'll only need the "gpu" field if you add GPUs to your grid; the ram_free is a field
   that we find useful in managing the memory of the machines, as the inbuilt field
   <tt>mem_free</tt> doesn't seem to work quite right for our purposes.  Later on
   when we add hosts to the grid, we'll use the command <tt>qconf -me <some-hostname></tt> to
   edit the <tt>complex_values</tt> field to read something like:
  \verbatim
   complex_values        ram_free=112G,gpu=2
  \endverbatim
   (for a machine with 112G of physical memory and 2 GPUs).  If we want to submit
   a job that needs 10G of memory, we'll specify <tt>-l mem_free=10G,ram_free=10G</tt> as an
   option to <tt>qsub</tt>; the <tt>mem_free</tt> requirement makes sure the machine has that much free
   memory at the time the job starts, and the <tt>ram_free</tt> requirement makes sure we
   don't submit a lot of jobs requiring a lot of memory, all to the same host.
   We tried, as an alternative to adding the <tt>ram_free</tt> resource,
   using <tt>qconf -mc</tt> to edit the <tt>consumable</tt> field of the inbuilt <tt>mem_free</tt> resource to say
   <tt>YES</tt>, to make GridEngine keep track of memory requests; but this did not
   seem to work as desired.  Note that
   both <tt>ram_free</tt> and <tt>gpu</tt> are names that we chose ourselves; they have no
   special meaning to GridEngine, while some of the inbuilt resources such as <tt>mem_free</tt>
   do have special meanings.  The string <tt>JOB</tt> in the <tt>consumable</tt> entry for
   <tt>ram_free</tt> means that the <tt>ram_free</tt> resource is specified per job rather than
   per thread; this is more convenient for Kaldi scripts.
  
   We are not saying anything here about the queue "g.q" which is required for GPUs
   in the default queue.pl behavior.  That was introduced at Hopkins in order to avoid
   the situation where GPUs are blocked because all CPU slots are used.  It's really not
   necessary in general; the easiest way to get things working without it is to create a file
   <tt>conf/queue.conf</tt> that's the same as the default one (search above) except
   without the "-q g.q" option.
  
   Next you have to have to add the parallel environment called <tt>smp</tt> to GridEngine.
   This is a kind of tradition in GridEngine setups, but it's not built-in.  It's a
   simple parallel environment where GridEngine doesn't really do anything, it just reserves you
   a certain number of slots, so if you do <tt>qsub -pe smp 10 <your-script></tt> you will
   get 10 CPU slots reserved; this can be useful for multi-threaded or multi-process jobs.
   Do <tt>qconf -ap smp</tt>, and edit the <tt>slots</tt> field to say 9999, so it reads:
  \verbatim
  pe_name            smp
  slots              9999
  ...
  \endverbatim
   Then do <tt>qconf -mq all.q</tt>, and edit the <tt>pe_list</tt> field by adding <tt>smp</tt>, so
   it reads:
  \verbatim
  pe_list               make smp
  \endverbatim
   This enables the <tt>smp</tt> parallelization method in the queue <tt>all.q</tt>.
  
  
   \subsection parallelization_gridengine_configuring_advanced Configuring GridEngine (advanced)
  
   In this section we just want to make a note of some things that might be helpful, but which
   aren't necessary just to get things running.
   In the CLSP cluster, we edited the <tt>prolog</tt> field in <tt>qconf -mq all.q</tt>
   so that it says
  \verbatim
  prolog                /var/lib/gridengine/default/common/prolog.sh
  \endverbatim
   (the default was <tt>NONE</tt>),
   and the script <tt>/var/lib/gridengine/default/common/prolog.sh</tt>,
   which we copied to that location on each individual node in the cluster,
   reads as follows.  Its only purpose is to wait a short time if the job script can't be
   accessed, to give NFS some time to sync in case the scripts were written very
   recently and haven't yet propagated across the grid:
  \verbatim
  #!/bin/bash
  
  function test_ok {
    if [ ! -z "$JOB_SCRIPT" ] && [ "$JOB_SCRIPT" != QLOGIN ] && [ "$JOB_SCRIPT" != QRLOGIN ]; then
      if [ ! -f "$JOB_SCRIPT" ]; then
         echo "$0: warning: no such file $JOB_SCRIPT, will wait" 1>&2
         return 1;
      fi
    fi
    if [ ! -z "$SGE_STDERR_PATH" ]; then
      if [ ! -d "`dirname $SGE_STDERR_PATH`" ]; then
        echo "$0: warning: no such directory $JOB_SCRIPT, will wait." 1>&2
        return 1;
      fi
    fi
    return 0;
  }
  
  if ! test_ok; then
    sleep 2;
    if ! test_ok; then
       sleep 4;
       if ! test_ok; then
          sleep 8;
       fi
    fi
  fi
  
  exit 0;
  \endverbatim
  This script waits at most 14 seconds, which is enough because we configured <tt>acdirmax=8</tt>
  in our NFS options as the maximum wait before refreshing a cached directory (see \ref parallelization_grid_stable_nfs below).
  
  We also edited the queue with <tt>qconf -mq all.q</tt> to change
  <tt>rerun</tt> from FALSE to TRUE, i.e. to say:
  \verbatim
  rerun                 TRUE
  \endverbatim
  This means that when jobs fail, they get in a status that shows up in the output of
  <tt>qstat</tt> as <tt>Eqw</tt>, with the <tt>E</tt> indicating error, and you can ask the
  queue to reschedule them by clearing the error status with <tt>qmod -cj <numeric-job-id></tt>
  (or if you don't want to rerun them, you can delete them with <tt>qmod -dj <numeric-job-id></tt>).
  Setting the queue to allow reruns can avoid the hassle of rerunning scripts from the
  start when things break due to NFS problems.
  
  Something else we did in the CLSP queue is to edit the following fields, which by default read:
  \verbatim
  rlogin_daemon                /usr/sbin/sshd -i
  rlogin_command               /usr/bin/ssh
  qlogin_daemon                /usr/sbin/sshd -i
  qlogin_command               /usr/share/gridengine/qlogin-wrapper
  rsh_daemon                   /usr/sbin/sshd -i
  rsh_command                  /usr/bin/ssh
  \endverbatim
  to read instead:
  \verbatim
  qlogin_command               builtin
  qlogin_daemon                builtin
  rlogin_command               builtin
  rlogin_daemon                builtin
  rsh_command                  builtin
  rsh_daemon                   builtin
  \endverbatim
  This was to solve a problem whose nature we can no longer recall, but it's something you might want to try it if
  commands like <tt>qlogin</tt> and <tt>qrsh</tt> don't work.
  
  \subsection parallelization_gridengine_configuring_adding Configuring GridEngine (adding nodes)
  
   In this section we address what you do when you add nodes to the queue.
   As mentioned above, you can install GridEngine on nodes by doing
  \verbatim
    sudo apt-get install gridengine-client gridengine-exec
  \endverbatim
   and you need to specify <tt>default</tt> as the cluster name, and the name of your master
   node as the master (probably using the FQDN of the master is safest here, but if you are on
   a local network, just the last part of the name may also work).
  
   But that doesn't mean your machine is fully in the queue.  GridEngine has separate notions
   of hosts being administrative hosts, execution hosts and submit hosts.  All your machines
   should be all three.  You can view which machines have these three roles using the commands
   <tt>qconf -sh</tt>, <tt>qconf -sel</tt>, and <tt>qconf -ss</tt> respectively.   You can
   add your machine as an administrative or submit host with the commands:
  \verbatim
   qconf -ah <your-fqdn>
   qconf -as <your-fqdn>
  \endverbatim
   and you can add your host as an execution host with the command
  \verbatim
   qconf -ae <your-fqdn>
  \endverbatim
   which brings up an editor; you can put in the ram_free and possibly GPU fields in here, e.g.
  \verbatim
  complex_values    ram_free=112G,gpu=1
  \endverbatim
  You'll notice is a slight asymmetry between the commands <tt>qconf -sh</tt>
  and <tt>qconf -ss</tt> on the one hand, and <tt>qconf -sel</tt> on the other.
  The <tt>"l"</tt> in the latter command means show the list.   The difference is that
  administrative and submit host lists are just lists of hosts, whereas
  qconf stores a bunch of information about the execution hosts, so it views it as a different
  type of data structure.
  You can view the information about a particular host with <tt>qconf -se <some-hostname></tt>,
  add a new host with <tt>qconf -ae <some-hostname</tt>, and
  modify with <tt>qconf -me <some-hostname></tt>.  This is a general pattern in GridEngine:
  for things like queues that have a bunch of information in them, you can show the full list
  by typing a command ending in "l" like <tt>qconf -sql</tt>, and the corresponding "add"
  (<tt>"a"</tt>) and "modify" (<tt>"m"</tt>) commands accept arguments.
  
  It's not enough to tell GridEngine that a node is an execution host; you have to also add it to the queue,
  and tell the queue how many slots to allocate for that node.  First figure out how many CPUs (or virtual CPUs)
  your machine has, by doing:
  \verbatim
  grep proc /proc/cpuinfo | wc -l
  \endverbatim
  Suppose this is 48.  You can choose a number a little smaller than this, say 40, and use that for the
  number of slots.  Edit the queue using <tt>qconf -mq all.q</tt>, add your machine
  to the hostlist, and set the number of slots.  It should look like this:
  \verbatim
  qname                 all.q
  hostlist              gridnode1.research.acme.com,gridnode2.research.acme.com
  <snip>
  slots                 30,[gridnode1.research.acme.com=48],[gridnode1.research.acme.com=48]
  <snip>
  \endverbatim
  In the <tt>slots</tt> field, the <tt>30</tt> at the beginning is a default value; for any
  nodes with that number of slots you can save yourself some time and avoid adding the node's
  name to the <tt>slots</tt> field.
  There is an alternative way to set up the <tt>hostlist</tt> field.  GridEngine has the concept of host groups,
  so you could do <tt>qconf -ahgrp \@allhosts</tt> to add a group of hosts, and edit it using
  <tt>qconf -mhgrp \@allhosts</tt> to add your new nodes.  The configuration of
  <tt>all.q</tt> could then just read:
  \verbatim
  hostlist            @allhosts
  \endverbatim
  It's your choice.  For simple queues, it's probably fine to just put the host list in the <tt>all.q</tt>
  configuration.
  
  A useful command to list the hosts that GridEngine knows about, and what queues they
  are in, is <tt>qhost -q</tt>.  For example:
  \verbatim
  # qhost -q
  HOSTNAME                ARCH         NCPU  LOAD  MEMTOT  MEMUSE  SWAPTO  SWAPUS
  -------------------------------------------------------------------------------
  global                  -               -     -       -       -       -       -
  a01.clsp.jhu.edu        lx26-amd64     24 12.46  126.2G   11.3G   86.6G  213.7M
     all.q                BIP   0/6/20
  a02.clsp.jhu.edu        lx26-amd64     24 16.84  126.2G   12.4G   51.3G  164.5M
     all.q                BIP   0/18/20
  <snip>
  \endverbatim
  If you see the letter <tt>"E"</tt> in the place where the example above shows <tt>"BIP"</tt>,
  it means the node is in the error state.  Other letters you don't want to see in that position are
  <tt>"a"</tt> for alarm (a generic indicator of badness) and <tt>"u"</tt> for unreachable.
  <tt>"d"</tt> means a node has been disabled by an administrator.
  Nodes sometimes get in the error (<tt>"E"</tt>) state when GridEngine had
  trouble running a job, which is often due to NFS or automount problems.
  You can clear the error by doing something like
  \verbatim
  qmod -c all.q@a01
  \endverbatim
  but of course if the node has serious problems, it might be wise to fix them first.
  It's sometimes also useful to enable and disable nodes in the queue by doing
  \verbatim
  qmod -d all.q@a01
  \endverbatim
  to disable a node, and <tt>qmod -e all.q\@a01</tt> to enable it again.
  
  A common symptom of GridEngine problems is jobs waiting when you think nodes
  are free.  The easiest way to debug this is to look for the job-id in the output
  of <tt>qstat</tt>, and then to do <tt>qstat -j <job-id></tt> and look for
  the reasons why the job is not running.
  
  You can view all jobs from all users by running
  \verbatim
  qstat -u '*'
  \endverbatim
  
  \section parallelization_grid_stable Keeping your grid stable
  
  In this section we have some general notes on how to ensure stability in
  a compute cluster of the kind useful for Kaldi.
  
  \subsection parallelization_grid_stable_oom Keeping your grid stable (OOM)
  
  One of the major causes of crashes in compute clusters is memory exhaustion.
  The default OOM-killer in Linux is not very good, so if you exhaust memory, it
  may end up killing an important system process, which tends to cause
  hard-to-diagnose instability.  Even if nothing is killed, <tt>malloc()</tt> may
  start failing when called from processes on the system; and very few programs
  deal with this gracefully.  In the CLSP grid we wrote our own version of an OOM
  killer, which we run as root, and we wrote the corresponding init scripts.  When
  our OOM killer detects memory overload, it kills the largest process of whichever
  non-system user is using the most memory.  This is usually the right thing to
  do.  These scripts have been made public as part of the <tt>kluster</tt>
  project, and you can get them as shown below if you want to add them to your
  system.  The following commands will only work as-is if you have LSB-style init
  scripts, which is the case in Debian
  <tt>wheezy</tt>.  The next Debian release, <tt>jessie</tt>, won't have init scripts at all and
  will use <tt>systemd</tt> instead (the so-called "systemd controversy").  If someone can figure out how to do the
  following in <tt>systemd</tt>, please let us know.
  Type
  \verbatim
  sudo bash
  \endverbatim
  and then as root, do:
  \verbatim
  apt-get install -y subversion
  svn cat https://svn.code.sf.net/p/kluster/code/trunk/scripts/sbin/mem-killer.pl > /sbin/mem-killer.pl
  chmod +x /sbin/mem-killer.pl
  cd /etc/init.d
  svn cat https://svn.code.sf.net/p/kluster/code/trunk/scripts/etc/init.d/mem-killer >mem-killer
  chmod +x mem-killer
  update-rc.d mem-killer defaults
  service mem-killer start
  \endverbatim
  <tt>mem-killer.pl</tt> is capable of sending email to the administrators and to the person
  whose jobs was killed if something is wrong, but this will only work if your
  system can send mail, and if the user has put their email somewhere in the
  "office" field of their gecos information, using chfn (or ypchfn if using NIS).
  Again, if you're running in the cloud, it's best to forget about anything email-related,
  as it's too hard to get working.
  
  \subsection parallelization_grid_stable_nfs Keeping your grid stable (NFS)
  
  We aren't going to give a complete run-down of how to install NFS here, but we
  want to mention some potential issues, and explain some options that work well.
  Firstly, NFS is not the only option for a shared filesystem; there are some newer
  distributed file systems available too, but we don't have much experience with them.
  
  NFS can perform quite badly if the options are wrong.  Below are the options we use.
  We show it as if we're grepping it from /etc/fstab; this isn't actually how we do it
  (we actually use NIS and automount), but the following way is simpler:
  \verbatim
  # grep a05 /etc/fstab
  a05:/mnt/data /export/a05 nfs rw,vers=3,rsize=8192,wsize=8192,acdirmin=5,acdirmax=8,hard,proto=tcp 0 0
  \endverbatim
  The option "vers=3" means we use NFS version 3, which is stateless.  We tried using version 4,
  a supposedly more advanced "stateful" protocol, but we got a lot of crashes.
  
  The <tt>acdirmin=5</tt> and <tt>acdirmin=8</tt> options are the minimum and maximum times that NFS
  waits before re-reading cached directory information; the defaults are 30 and 60 seconds respectively.
  This is important for Kaldi scripts, because the files that we execute on GridEngine are written
  only shortly before we run the scripts, so with default NFS options they may not yet be visible
  on the execution host at the time they are needed.  Above we showed our script <tt>/var/lib/gridengine/default/common/prolog.sh</tt>
  which waits up to 14 seconds for the script to appear.  It's significant that 14 > 8, i.e. that the
  number of seconds the prolog script will wait for is greater than the maximum directory caching period for NFS.
  
  The <tt>hard</tt> option is also important; it means that if the server is busy, the client will wait
  for it to succeed rather than reporting an error (e.g. <tt>fopen</tt> returning error status).  If you
  specify <tt>soft</tt>, Kaldi may crash.  <tt>hard</tt> is the default so could be omitted.
  
  The <tt>proto=tcp</tt> option is also the default on Debian currently; the
  alternative is <tt>proto=udp</tt>.  The TCP protocol is important for stability
  when local networks may get congested; we have found this through experience.
  
  The <tt>rsize=8192,wsize=8192</tt> are packet sizes; they are supposedly
  important in the performance of NFS.  Kaldi reads and writes are generally contiguous and
  don't tend to seek much within files, so large packet size is probably
  suitable.
  
  Another thing you might want to tune that's NFS related is the number of threads in the server.
  You can see this as follows:
  \verbatim
  /$ head /etc/default/nfs-kernel-server
  # Number of servers to start up
  RPCNFSDCOUNT=64
  \endverbatim
  To change it you edit that file and then restart the service (<tt>service nfs-kernel-server restart</tt>, on Debian).
  Apparently it is not good for this to be less than the number of clients that might simultaneously access your
  server (although 64 is the upper limit of what I've seen people recommend to set this to).  Apparently one way to
  tell if you have too few threads is too look at the  <tt>retrans</tt> count in your NFS stats:
  \verbatim
  nfsstat -rc
  Client rpc stats:
  calls      retrans    authrefrsh
  434831612   6807461    434979729
  \endverbatim
  The stats above have a largish number of <tt>retrans</tt>, and apparently in the ideal case it should be zero.
  We had the number of NFS threads set to a lower number (24) for most of the time that machine was up, which
  was less than the potential number of clients, so it's not surprising that we had a high amount of <tt>retrans</tt>.
  What seems to happen is that if a large number of clients are actively using the server, especially writing
  large amounts of data simultaneously, they can tie up all the server threads and then when another client tries
  to do something, it fails and in the client logs you'll see something like <tt>nfs: server a02 not responding, still trying</tt>.
  This can sometimes be associated with crashed jobs, and if you use automount on your setup, it can sometimes
  cause jobs that are not even accessing that server to crash or be delayed (automount has a brain-dead, single-threaded
  design, so failure of one mount request can hang up all other automount reqeuests).
  
  \subsection parallelization_grid_stable_misc Keeping your grid stable (general issues)
  
  In this section we have some general observations about how to configure
  and manage a compute grid.
  
  In CLSP we use a lot of NFS hosts, not just one or two; in fact, most of our
  nodes also export data via NFS.  If you do this you should use
  our <tt>mem-killer.pl</tt> or a similar script, or you will get instability due
  to memory exhaustion when users make mistakes.
  Having a large number of file
  servers is a particularly good idea for queues that are shared by many people,
  because it's inevitable that people will overload file servers, and if there are
  only one or two file servers, the queue will end up being in a degraded state
  for much of the time.  The network bandwidth of each individual file server at
  CLSP@JHU is quite slow: for cost reasons we still use 1G ethernet, but all
  connected to each other with an expensive Cisco router so that there is no
  global bottleneck.  This means that each file server gets overloaded quite
  easily; but because there are so many individual file servers, this generally
  only inconveniences the person who is overloading it and maybe one or two
  others.
  
  When we do detect that a particular file server is loaded (generally either by
  noticing slowness, or by seeing errors like <tt>nfs: server a15 not
  responding</tt> in the output of <tt>dmesg</tt>), we try to track down why this
  is happening.  Without exception this is due to bad user behavior, i.e. users
  running too many I/O-heavy jobs.  Usually through a combination of the output
  of <tt>iftop</tt> and the output of <tt>qstat</tt>, we can figure out which user
  is causing the problem.  In order to keep the queue stable it's necessary to get
  users to correct their behavior and to limit the number of I/O heavy jobs they
  run, by sending them emails and asking them to modify their setups.  If we
  didn't contact users in this way the queue would be unusable, because users
  would persist in their bad habits.
  
  Many other groups have a lot of file servers, like us, but still have all their
  traffic going to one file server because they buy the file servers one at a time
  and they always allocate space on the most recently bought one.  This is just
  stupid.  In CLSP we avoid the administrator hassle of having to allocate space,
  by having all the NFS servers world-writable at the top level, and by instructing
  people to make directories with their userid on them, on a server of their choice.
  We set up scripts to notify the administrators by email when any of the file
  servers gets 95% full, and to let us know which directories contain a lot of
  data.  We can then ask the users concerned to delete some data, or we delete it
  ourselves if the users are gone and if we feel it's appropriate.  We also set up
  scripts to work out, queue-wide, which users are using the most space, and to
  send them emails informing them where they are consuming the most space.  The
  administrators will also ask the users to clean up, particularly in extreme cases
  (e.g. when a junior student is using a huge amount of space for no obvious
  reason).  Space should never really be a problem, because it's almost always the
  case that the disk is 95% full of junk that no-one cares about or even
  remembers.  It's simply a matter of finding a way to ask those responsible to
  clean up, and making their life easy by telling them where the bulk of their
  data is.
  
  Another useful thing is to locate home directories on a server that is not also
  used for experiments; this ensures that users can always get a prompt, even when
  other users are being stupid.  It also makes the backup policy easier: we back
  up the home directories but not the NFS volumes used for experimental work, and
  we make clear to users that those volumes are not backed up (of course, students
  will still fail to back up their important data, and will sometimes lose it as a
  result).  The ban on running experiments in home directories needs to be
  enforced; we frequently have to tell users to stop parallel jobs with data in
  their home directories.  This is the most frequent cause of grid-wide problems.
  
  
  */
  
  }