Repository: common-workflow-language/cwltool

Issue:
Performance Improvements
# Overview

Cwltool currently could benefit from significant performance improvements through changes in the way workflows and tooling are processed. Suggestions for this based on profiling from @stain 

# Flag for skipping add_schemas

For workflows with complex schemas, it takes a huge amount of time for cwltool to gather, process and check them. Profiles can be seen below before and after removal:
* [with add_schemas](https://gist.github.com/stain/d35b9f4451443cad8c748b84099650a8) 482.372s
* [without add_schemas](https://gist.github.com/stain/158853fd46c659ac368dd06224f982bf) 101.739s

This was tested by adding a simple return statement at the start of the function. This option could benefit a lot of users and would be an easy win

# Multicore file loading

A large part of the remaining time taken, as would be expected, is in downloading and parsing.

Currently cwltool spends about 0.6s per file loaded remotely. As in the above traces of a total time of 86s, 36s is http fetching. These files could be loaded in parallel on a multicore machine and yaml parsing a different document could be done at the same time as downloading another.

With many split tooling files or subworkflows this will make a particularly large impact.



Discussion:
Thank you very much @MarkRobbo 

I wonder how many of the conformance tests break with `add_schemas` skipped..
IIRC the uses of schema loading are:

1. To use RDFS/OWL reasoning about file formats
2. Establish an expanded vocabulary that recognizes foreign properties (making "unrecognized foreign property" warnings more meaningful because they are more likely to be legitimate errors). 

These are both "nice to have" behaviors that are not essential to core CWL execution.
Would it make sense then to add a `--skip-schemas` ?
@stain I think `--skip-schemas` is worth exploring, yes

Resolving patch:
diff --git a/cwltool/load_tool.py b/cwltool/load_tool.py
--- a/cwltool/load_tool.py
+++ b/cwltool/load_tool.py
@@ -133,7 +133,8 @@ def validate_document(document_loader,  # type: Loader
                       enable_dev=False,  # type: bool
                       strict=True,  # type: bool
                       preprocess_only=False,  # type: bool
-                      fetcher_constructor=None
+                      fetcher_constructor=None,
+                      skip_schemas=None
                       # type: Callable[[Dict[unicode, unicode], requests.sessions.Session], Fetcher]
                       ):
     # type: (...) -> Tuple[Loader, Names, Union[Dict[Text, Any], List[Dict[Text, Any]]], Dict[Text, Any], Text]
@@ -180,9 +181,10 @@ def validate_document(document_loader,  # type: Loader
         raise avsc_names
 
     processobj = None  # type: Union[CommentedMap, CommentedSeq, unicode]
+
     document_loader = Loader(sch_document_loader.ctx, schemagraph=sch_document_loader.graph,
                              idx=document_loader.idx, cache=sch_document_loader.cache,
-                             fetcher_constructor=fetcher_constructor)
+                             fetcher_constructor=fetcher_constructor, skip_schemas=skip_schemas)
 
     _add_blank_ids(workflowobj)
 
diff --git a/cwltool/main.py b/cwltool/main.py
--- a/cwltool/main.py
+++ b/cwltool/main.py
@@ -141,6 +141,9 @@ def arg_parser():  # type: () -> argparse.ArgumentParser
     exgroup.add_argument("--non-strict", action="store_false", help="Lenient validation (ignore unrecognized fields)",
                          default=True, dest="strict")
 
+    parser.add_argument("--skip-schemas", action="store_true",
+            help="Skip loading of schemas", default=True, dest="skip_schemas")
+
     exgroup = parser.add_mutually_exclusive_group()
     exgroup.add_argument("--verbose", action="store_true", help="Default logging")
     exgroup.add_argument("--quiet", action="store_true", help="Only print warnings and errors.")
@@ -634,6 +637,7 @@ def main(argsl=None,  # type: List[str]
                      'enable_dev': False,
                      'enable_ext': False,
                      'strict': True,
+                     'skip_schemas': False,
                      'rdf_serializer': None,
                      'basedir': None,
                      'tool_help': False,
@@ -701,7 +705,8 @@ def main(argsl=None,  # type: List[str]
                 = validate_document(document_loader, workflowobj, uri,
                                     enable_dev=args.enable_dev, strict=args.strict,
                                     preprocess_only=args.print_pre or args.pack,
-                                    fetcher_constructor=fetcher_constructor)
+                                    fetcher_constructor=fetcher_constructor,
+                                    skip_schemas=args.skip_schemas)
 
             if args.pack:
                 stdout.write(print_pack(document_loader, processobj, uri, metadata))
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -57,7 +57,7 @@
           'ruamel.yaml >= 0.12.4',
           'rdflib >= 4.2.2, < 4.3.0',
           'shellescape >= 3.4.1, < 3.5',
-          'schema-salad >= 2.4.20170308171942, < 3',
+          'schema-salad >= 2.5.20170623152720, < 3',
           'typing >= 3.5.2, < 3.6',
           'six >= 1.8.0',
       ],


Test patch: