.. _generating-workflow-benchmarks-label: WfBench: Workflow Benchmarks ============================ **WfBench** generates realistic workflow benchmarks that you can actually run on your infrastructure. It produces tasks with tunable performance characteristics (CPU, GPU, memory, and I/O usage) and realistic dependency structures derived from production workflows — so you can stress-test and compare workflow systems, schedulers, and platforms under controlled, repeatable conditions. Benchmark generation is twofold: first, a specification is produced in the :ref:`json-format-label`; then, that specification is translated into executable benchmark code for a target workflow system. Generating Workflow Benchmark Specifications -------------------------------------------- The :class:`~wfcommons.wfbench.bench.WorkflowBenchmark` class uses workflow recipes (as described in :ref:`workflow-recipe-generator-label`) for generating workflow benchmarks with an arbitrary number of tasks:: import pathlib from wfcommons import BlastRecipe from wfcommons.wfbench import WorkflowBenchmark # create a workflow benchmark object to generate specifications based on a recipe benchmark = WorkflowBenchmark(recipe=BlastRecipe, num_tasks=500) # generate a specification based on performance characteristics path = benchmark.create_benchmark(pathlib.Path("/tmp/"), cpu_work=100, data=10, percent_cpu=0.6) In the example above, the workflow benchmark generator first invokes the WfChef recipe to generate a task graph. Once the task graph has been generated, each task is set to be an instance of the workflow task benchmark. The main parameters of :meth:`~wfcommons.wfbench.bench.WorkflowBenchmark.create_benchmark` are: - :code:`cpu_work`: CPU work per workflow task (either a single value for all tasks, or a dictionary of values per task type). - :code:`percent_cpu`: the fraction of the computation's instructions that correspond to non-memory operations. - :code:`data`: individual data volumes for each task, in a way that is coherent with respect to task data dependencies (in the form of a dictionary of input size files per workflow task type). Alternatively, a total data footprint (in MB) can be defined, i.e., the sum of the sizes of all data files read/written by workflow tasks, in which case uniform I/O volumes are computed for each workflow task benchmark. - :code:`gpu_work`: GPU work per workflow task, for benchmarking GPU nodes (single value or per-task-type dictionary). - :code:`mem`: maximum memory consumption per task (in MB). - :code:`time`: time limit for running each task (in seconds). - :code:`num_chunks`: number of chunks used for pipelining I/O and computation within each task execution. - :code:`lock_files_folder`: folder for lock files used to implement CPU affinity when the workflow system does not provide it (see the Pegasus warning below). At execution time, each benchmark task runs the :code:`wfbench` executable (installed with this package), which performs the specified CPU/GPU work, memory, and I/O behavior. .. warning:: WfBench leverages :code:`stress-ng` (https://github.com/ColinIanKing/stress-ng) to execute memory-intensive threads. Ensure :code:`stress-ng` is installed on all worker nodes. Generate from synthetic workflow instances ++++++++++++++++++++++++++++++++++++++++++ WfCommons also allows you to convert synthetic workflow instances into benchmarks directly. The generated benchmark will have exactly the same structure as the synthetic workflow instance:: import pathlib from wfcommons import BlastRecipe from wfcommons.wfbench import WorkflowBenchmark # create a synthetic workflow instance with 500 tasks or use one that you already have workflow = BlastRecipe.from_num_tasks(500).build_workflow() # create a workflow benchmark object to generate specifications based on a recipe benchmark = WorkflowBenchmark(recipe=BlastRecipe, num_tasks=500) # generate a specification based on performance characteristics and the structure of the synthetic workflow instance path = benchmark.create_benchmark_from_synthetic_workflow(pathlib.Path("/tmp/"), workflow, cpu_work=100, percent_cpu=0.6) This is useful when you want to generate a benchmark with a specific structure or when you want benchmarks with the more detailed structure provided by WfChef workflow generation. Translating Specifications into Benchmark Code ---------------------------------------------- WfCommons provides a collection of translators that turn benchmark specifications into runnable workflow code. All translators inherit from :class:`~wfcommons.wfbench.translator.abstract_translator.Translator` and accept either a :class:`~wfcommons.common.workflow.Workflow` object or a path to a benchmark specification in :ref:`json-format-label`. Supported translators (alphabetical) ++++++++++++++++++++++++++++++++++++ - Airflow - Bash - CWL - Dask - Makeflow - Nextflow - Parsl - Pegasus - PyCOMPSs - Snakemake - StreamFlow - Swift/T - TaskVine Airflow +++++++ `Apache Airflow `_ is a platform for authoring, scheduling, and monitoring workflows as code. Use the Airflow translator to produce DAGs that can be executed by an Airflow scheduler:: import pathlib from wfcommons import BlastRecipe from wfcommons.wfbench import WorkflowBenchmark, AirflowTranslator benchmark = WorkflowBenchmark(recipe=BlastRecipe, num_tasks=200) benchmark.create_benchmark(pathlib.Path("/tmp/"), cpu_work=100, data=10, percent_cpu=0.6) translator = AirflowTranslator(benchmark.workflow) translator.translate(output_folder=pathlib.Path("./airflow-wf/")) Bash ++++ The Bash translator generates a simple, runnable shell workflow for quick local validation and debugging:: import pathlib from wfcommons import BlastRecipe from wfcommons.wfbench import WorkflowBenchmark, BashTranslator benchmark = WorkflowBenchmark(recipe=BlastRecipe, num_tasks=100) benchmark.create_benchmark(pathlib.Path("/tmp/"), cpu_work=50, data=5, percent_cpu=0.7) translator = BashTranslator(benchmark.workflow) translator.translate(output_folder=pathlib.Path("./bash-wf/")) CWL +++ `CWL `_ is a community standard for describing command-line tools and workflows. The CWL translator emits portable CWL definitions:: import pathlib from wfcommons import BlastRecipe from wfcommons.wfbench import WorkflowBenchmark, CWLTranslator benchmark = WorkflowBenchmark(recipe=BlastRecipe, num_tasks=150) benchmark.create_benchmark(pathlib.Path("/tmp/"), cpu_work=80, data=8, percent_cpu=0.6) translator = CWLTranslator(benchmark.workflow) translator.translate(output_folder=pathlib.Path("./cwl-wf/")) Dask ++++ `Dask `_ is an open-source library for parallel computing in Python. It supports local execution, HPC schedulers, and cloud environments:: import pathlib from wfcommons import BlastRecipe from wfcommons.wfbench import WorkflowBenchmark, DaskTranslator benchmark = WorkflowBenchmark(recipe=BlastRecipe, num_tasks=500) benchmark.create_benchmark(pathlib.Path("/tmp/"), cpu_work=100, data=10, percent_cpu=0.6) translator = DaskTranslator(benchmark.workflow) translator.translate(output_folder=pathlib.Path("./dask-wf/")) Makeflow ++++++++ `Makeflow `_ targets large, DAG-shaped workflows on clusters, grids, and clouds. The translator emits Makeflow workflows:: import pathlib from wfcommons import BlastRecipe from wfcommons.wfbench import WorkflowBenchmark, MakeflowTranslator benchmark = WorkflowBenchmark(recipe=BlastRecipe, num_tasks=200) benchmark.create_benchmark(pathlib.Path("/tmp/"), cpu_work=100, data=10, percent_cpu=0.6) translator = MakeflowTranslator(benchmark.workflow) translator.translate(output_folder=pathlib.Path("./makeflow-wf/")) Nextflow ++++++++ `Nextflow `_ enables portable, reproducible workflows across local, HPC, and cloud environments:: import pathlib from wfcommons import BlastRecipe from wfcommons.wfbench import WorkflowBenchmark, NextflowTranslator benchmark = WorkflowBenchmark(recipe=BlastRecipe, num_tasks=500) benchmark.create_benchmark(pathlib.Path("/tmp/"), cpu_work=100, data=10, percent_cpu=0.6) translator = NextflowTranslator( benchmark.workflow, use_subworkflows=False, max_tasks_per_subworkflow=1000, ) translator.translate(output_folder=pathlib.Path("./nextflow-wf/")) If you want to split large workflows across multiple Nextflow module files, enable subworkflows and set the maximum number of tasks per module. This produces a ``modules/`` directory plus a top-level ``workflow.nf`` that includes and runs the modules sequentially:: translator = NextflowTranslator( benchmark.workflow, use_subworkflows=True, max_tasks_per_subworkflow=250, ) translator.translate(output_folder=pathlib.Path("./nextflow-wf/")) .. warning:: Nextflow does not support tasks with iterations (tasks that depend on another instance of the same abstract task). Translation fails for workflows that include iterations. .. note:: If you plan to run Nextflow on an HPC system using Slurm, we **strongly recommend** using the `HyperQueue `_ executor. HyperQueue efficiently distributes workflow tasks across all allocated compute nodes, improving scalability and resource utilization. The :class:`~wfcommons.wfbench.translator.nextflow.NextflowTranslator` class includes functionality to automatically generate a Slurm script template for running the workflow on HPC systems. Parsl +++++ `Parsl `_ is a parallel scripting library for Python. The translator emits a Parsl workflow suitable for local or distributed execution:: import pathlib from wfcommons import BlastRecipe from wfcommons.wfbench import WorkflowBenchmark, ParslTranslator benchmark = WorkflowBenchmark(recipe=BlastRecipe, num_tasks=200) benchmark.create_benchmark(pathlib.Path("/tmp/"), cpu_work=100, data=10, percent_cpu=0.6) translator = ParslTranslator(benchmark.workflow) translator.translate(output_folder=pathlib.Path("./parsl-wf/")) Pegasus +++++++ `Pegasus `_ orchestrates complex scientific workflows on clusters, grids, and clouds by mapping tasks onto distributed resources:: import pathlib from wfcommons import BlastRecipe from wfcommons.wfbench import WorkflowBenchmark, PegasusTranslator benchmark = WorkflowBenchmark(recipe=BlastRecipe, num_tasks=500) benchmark.create_benchmark(pathlib.Path("/tmp/"), cpu_work=100, data=10, percent_cpu=0.6) translator = PegasusTranslator(benchmark.workflow) translator.translate(output_folder=pathlib.Path("./pegasus-wf/")) .. warning:: Pegasus uses `HTCondor `_ to orchestrate tasks. By default, HTCondor does not implement CPU affinity for program threads. To enable CPU affinity, specify :code:`lock_files_folder` when using :meth:`~wfcommons.wfbench.bench.WorkflowBenchmark.create_benchmark`. PyCOMPSs ++++++++ `PyCOMPSs `_ is a programming model and runtime for parallel Python applications on distributed infrastructures:: import pathlib from wfcommons import CyclesRecipe from wfcommons.wfbench import WorkflowBenchmark, PyCompssTranslator benchmark = WorkflowBenchmark(recipe=CyclesRecipe, num_tasks=200) benchmark.create_benchmark(pathlib.Path("/tmp/"), cpu_work=500, data=1000, percent_cpu=0.8) translator = PyCompssTranslator(benchmark.workflow) translator.translate(output_folder=pathlib.Path("./pycompss-wf/")) Snakemake +++++++++ `Snakemake `_ is a workflow system to create data analysis workflows using a human readable, Python-based language:: import pathlib from wfcommons import BlastRecipe from wfcommons.wfbench import WorkflowBenchmark, SnakemakeTranslator benchmark = WorkflowBenchmark(recipe=BlastRecipe, num_tasks=500) benchmark.create_benchmark(pathlib.Path("/tmp/"), cpu_work=100, data=10, percent_cpu=1.0) translator = SnakemakeTranslator(benchmark.workflow) translator.translate(output_folder=pathlib.Path("./snakemake/")) StreamFlow ++++++++++ `StreamFlow `_ is a container-native workflow management system that executes CWL workflows on hybrid infrastructures. The StreamFlow translator produces a CWL workflow (via the CWL translator) ready to be executed with StreamFlow:: import pathlib from wfcommons import BlastRecipe from wfcommons.wfbench import WorkflowBenchmark, StreamflowTranslator benchmark = WorkflowBenchmark(recipe=BlastRecipe, num_tasks=150) benchmark.create_benchmark(pathlib.Path("/tmp/"), cpu_work=80, data=8, percent_cpu=0.6) translator = StreamflowTranslator(benchmark.workflow) translator.translate(output_folder=pathlib.Path("./streamflow-wf/")) Swift/T +++++++ `Swift/T `_ is a workflow system for HPC environments, designed to scale to large task graphs:: import pathlib from wfcommons import BlastRecipe from wfcommons.wfbench import WorkflowBenchmark, SwiftTTranslator benchmark = WorkflowBenchmark(recipe=BlastRecipe, num_tasks=500) benchmark.create_benchmark(pathlib.Path("/tmp/"), cpu_work=100, data=10, percent_cpu=1.0) translator = SwiftTTranslator(benchmark.workflow) translator.translate(output_folder=pathlib.Path("./swift-t-wf/")) TaskVine ++++++++ `TaskVine `_ is a task scheduler for data-intensive dynamic workflows across HPC clusters, GPU clusters, and clouds:: import pathlib from wfcommons import BlastRecipe from wfcommons.wfbench import WorkflowBenchmark, TaskVineTranslator benchmark = WorkflowBenchmark(recipe=BlastRecipe, num_tasks=500) benchmark.create_benchmark(save_dir=pathlib.Path("/tmp/"), cpu_work=100, data=10, percent_cpu=1.0) translator = TaskVineTranslator(benchmark.workflow) translator.translate(output_folder=pathlib.Path("./taskvine-wf/")) WfBench will generate a folder containing the TaskVine workflow :code:`taskvine_workflow.py`, workflow input data (:code:`./taskvine-wf/data/`), workflow binaries (:code:`./taskvine-wf/bin/`), and the Poncho package specification (:code:`./taskvine-wf/taskvine_poncho.json`). .. warning:: This TaskVine workflow requires :code:`stress-ng` to be installed and accessible in the system's :code:`$PATH` where the manager runs.