From 9db7e920fc09212efc3b4531bd233762bc56f139 Mon Sep 17 00:00:00 2001 From: Ian Lumsden Date: Thu, 4 Apr 2024 13:44:41 -0400 Subject: [PATCH 01/35] Updates Dockerfile.spawn and requirements.txt to handle the newest version of DYAD and DLIO --- .../JupyterNotebook/docker/Dockerfile.spawn | 65 ++++++++++--------- .../JupyterNotebook/requirements.txt | 2 + 2 files changed, 37 insertions(+), 30 deletions(-) diff --git a/2024-RIKEN-AWS/JupyterNotebook/docker/Dockerfile.spawn b/2024-RIKEN-AWS/JupyterNotebook/docker/Dockerfile.spawn index a9c61b4..f886028 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/docker/Dockerfile.spawn +++ b/2024-RIKEN-AWS/JupyterNotebook/docker/Dockerfile.spawn @@ -11,24 +11,24 @@ ENV NB_USER=jovyan \ HOME=/home/jovyan RUN adduser \ - --disabled-password \ - --gecos "Default user" \ - --uid ${NB_UID} \ - --home ${HOME} \ - --force-badname \ - ${NB_USER} + --disabled-password \ + --gecos "Default user" \ + --uid ${NB_UID} \ + --home ${HOME} \ + --force-badname \ + ${NB_USER} RUN apt-get update \ - && apt-get upgrade -y \ - && apt-get install -y --no-install-recommends \ - ca-certificates \ - dnsutils \ - iputils-ping \ - python3-pip \ - tini \ - # requirement for nbgitpuller - git \ - && rm -rf /var/lib/apt/lists/* + && apt-get upgrade -y \ + && apt-get install -y --no-install-recommends \ + ca-certificates \ + dnsutils \ + iputils-ping \ + python3-pip \ + tini \ + # requirement for nbgitpuller + git \ + && rm -rf /var/lib/apt/lists/* COPY ./requirements.txt ./requirements.txt RUN ln -s /usr/bin/python3 /usr/bin/python && \ @@ -43,28 +43,29 @@ RUN git clone https://github.com/openucx/ucx.git \ && git checkout v1.13.1 \ && ./autogen.sh \ && ./configure --disable-optimizations --enable-logging --enable-debug --disable-assertions --enable-mt --disable-params-check \ - --without-go --without-java --disable-cma --without-cuda --without-gdrcopy --without-verbs --without-knem --without-rmdacm \ - --without-rocm --without-xpmem --without-fuse3 --without-ugni --prefix=/usr CC=$(which gcc) CXX=$(which g++) \ + --without-go --without-java --disable-cma --without-cuda --without-gdrcopy --without-verbs --without-knem --without-rmdacm \ + --without-rocm --without-xpmem --without-fuse3 --without-ugni --prefix=/usr CC=$(which gcc) CXX=$(which g++) \ && make -j \ && sudo make install \ && cd .. \ && rm -rf ucx -RUN git clone https://github.com/TauferLab/dyad.git \ +RUN git clone https://github.com/flux-framework/dyad.git \ && cd dyad \ - && git checkout ucx \ - && cp -r docs/demos/ecp_feb_2023 .. \ - && ./autogen.sh \ - && ./configure --prefix=/usr CC=$(which gcc) CXX=$(which g++) --enable-dyad-debug \ + && git checkout tutorial-riken-2024 \ + && cp -r tests/integration/dlio_benchmark /home/jovyan \ + && mkdir build \ + && cd build \ + && cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo .. \ && make -j \ && sudo make install \ - && cd .. \ - && rm -rf dyad - -RUN mv ecp_feb_2023 /opt/dyad_demo \ - && cd /opt/dyad_demo \ - && CC=$(which gcc) CXX=$(which g++) make all \ - && cd .. + && cd ../pydyad \ + && python3 -m pip install . \ + && cd ../.. \ + && rm -rf dyad \ + && rm /home/jovyan/dlio_benchmark/*.sh \ + && rm -r /home/jovyan/dlio_benchmark/perf_analysis \ + && mv /home/jovyan/dlio_benchmark /home/jovyan/dlio_extensions # This adds the flux-tree command, which is provided in flux-sched source # but not installed alongside production flux-core @@ -101,6 +102,10 @@ CMD ["flux", "start", "--test-size=4", "jupyter", "lab"] # This won't be available in K8s, but will be for a single container build COPY ./tutorial /home/jovyan/flux-tutorial-2024 +USER root +RUN mv /home/jovyan/dlio_extensions /home/jovyan/flux-tutorial-2024 +USER ${NB_USER} + # Previous command for non-kubernetes # CMD PATH=$HOME/.local/bin:$PATH \ # flux start --test-size=4 /home/fluxuser/.local/bin/jupyterhub-singleuser diff --git a/2024-RIKEN-AWS/JupyterNotebook/requirements.txt b/2024-RIKEN-AWS/JupyterNotebook/requirements.txt index 0d9d99e..b746861 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/requirements.txt +++ b/2024-RIKEN-AWS/JupyterNotebook/requirements.txt @@ -337,3 +337,5 @@ webencodings==0.5.1 # tinycss2 websocket-client==1.6.1 # via jupyter-server +# Used for the DYAD notebook +dlio_benchmark @ git+https://github.com/argonne-lcf/dlio_benchmark.git From a4c730b24ce0a61953b7b32b80e7eca2d9003d10 Mon Sep 17 00:00:00 2001 From: Ian Lumsden Date: Thu, 4 Apr 2024 16:36:10 -0400 Subject: [PATCH 02/35] Moves copy/move of tutorial materials into the root user block --- .../JupyterNotebook/docker/Dockerfile.spawn | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/2024-RIKEN-AWS/JupyterNotebook/docker/Dockerfile.spawn b/2024-RIKEN-AWS/JupyterNotebook/docker/Dockerfile.spawn index f886028..c426188 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/docker/Dockerfile.spawn +++ b/2024-RIKEN-AWS/JupyterNotebook/docker/Dockerfile.spawn @@ -50,6 +50,9 @@ RUN git clone https://github.com/openucx/ucx.git \ && cd .. \ && rm -rf ucx +# This won't be available in K8s, but will be for a single container build +COPY ./tutorial /home/jovyan/flux-tutorial-2024 + RUN git clone https://github.com/flux-framework/dyad.git \ && cd dyad \ && git checkout tutorial-riken-2024 \ @@ -65,7 +68,8 @@ RUN git clone https://github.com/flux-framework/dyad.git \ && rm -rf dyad \ && rm /home/jovyan/dlio_benchmark/*.sh \ && rm -r /home/jovyan/dlio_benchmark/perf_analysis \ - && mv /home/jovyan/dlio_benchmark /home/jovyan/dlio_extensions + && mv /home/jovyan/dlio_benchmark /home/jovyan/dlio_extensions \ + && mv /home/jovyan/dlio_extensions /home/jovyan/flux-tutorial-2024 # This adds the flux-tree command, which is provided in flux-sched source # but not installed alongside production flux-core @@ -99,13 +103,6 @@ COPY ./docker/entrypoint.sh /entrypoint.sh COPY ./docker/start.sh /start.sh CMD ["flux", "start", "--test-size=4", "jupyter", "lab"] -# This won't be available in K8s, but will be for a single container build -COPY ./tutorial /home/jovyan/flux-tutorial-2024 - -USER root -RUN mv /home/jovyan/dlio_extensions /home/jovyan/flux-tutorial-2024 -USER ${NB_USER} - # Previous command for non-kubernetes # CMD PATH=$HOME/.local/bin:$PATH \ # flux start --test-size=4 /home/fluxuser/.local/bin/jupyterhub-singleuser From 4adc40d7519d592b467eac23c04f421383089874 Mon Sep 17 00:00:00 2001 From: Ian Lumsden Date: Fri, 5 Apr 2024 17:32:58 -0400 Subject: [PATCH 03/35] Adds intro, tutorial code cells, and extra dependencies to DYAD portion of the tutorial --- .../JupyterNotebook/requirements.txt | 1 + .../tutorial/notebook/dyad_dlio.ipynb | 345 ++++++++++++++++++ .../tutorial/notebook/flux.ipynb | 2 +- .../tutorial/notebook/img/dyad_design.png | Bin 0 -> 184051 bytes 4 files changed, 347 insertions(+), 1 deletion(-) create mode 100644 2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/dyad_dlio.ipynb create mode 100644 2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/img/dyad_design.png diff --git a/2024-RIKEN-AWS/JupyterNotebook/requirements.txt b/2024-RIKEN-AWS/JupyterNotebook/requirements.txt index b746861..41d2fbe 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/requirements.txt +++ b/2024-RIKEN-AWS/JupyterNotebook/requirements.txt @@ -339,3 +339,4 @@ websocket-client==1.6.1 # via jupyter-server # Used for the DYAD notebook dlio_benchmark @ git+https://github.com/argonne-lcf/dlio_benchmark.git +Pygments diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/dyad_dlio.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/dyad_dlio.ipynb new file mode 100644 index 0000000..042898e --- /dev/null +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/dyad_dlio.ipynb @@ -0,0 +1,345 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "dd3e912b-3428-4bc7-88bd-97686406b75a", + "metadata": { + "tags": [] + }, + "source": [ + "# Welcome to the DYAD component of the Flux tutorial\n" + ] + }, + { + "cell_type": "markdown", + "id": "4db0555f", + "metadata": {}, + "source": [ + "> What is DYAD? πŸ€”οΈ\n", + "\n", + "DYAD is a locality-aware, write-once, read-many file cache that runs on top of local NVMe and other burst buffer-style technologies (e.g., El Capitan Rabbit nodes). It is designed to accelerate large, distributed workloads, such as distributed Deep Learning (DL) training and scientific computing workflows, on HPC systems. Unlike similar tools (e.g., DataSpaces and UnifyFS), which tend to optimize for write performance, DYAD aims to provide good write **and read** performance. To optimize read performance, DYAD uses a locality-aware \"Hierarchical Data Locator,\" which prioritizes node-local metadata and data retrieval to minimize the amount of network communications. When moving data from another node, DYAD also uses a streaming RPC over RDMA protocol, which uses preallocated buffers and connection caching to maximize network bandwidth. This process is shown in the figure below:\n", + "\n", + "![DYAD Reading Process](img/dyad_design.png)\n", + "\n", + "DYAD uses several services provided by Flux (key-value store, remote proceedure call, broker modules) to orchestrate data movement between nodes. It also uses UCX to move data." + ] + }, + { + "cell_type": "markdown", + "id": "badb9753", + "metadata": {}, + "source": [ + "> I'm ready! How do I do this tutorial? 😁️\n", + "\n", + "The process for running this tutorial is the same as `flux.ipynb`. To step through examples in this notebook \n", + "you need to execute cells. To run a cell, press Shift+Enter on your keyboard. If you prefer, you can also paste \n", + "the shell commands in the JupyterLab terminal and execute them there." + ] + }, + { + "cell_type": "markdown", + "id": "c0a9e0f9", + "metadata": {}, + "source": [ + "# Accelerating Distributed Deep Learning (DL) Training with DYAD\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "be8da082", + "metadata": {}, + "source": [ + "## Show code" + ] + }, + { + "cell_type": "markdown", + "id": "4f018bbc", + "metadata": {}, + "source": [ + "[data loader](../dlio_extensions/dyad_torch_data_loader.py)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c92da400", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import sys\n", + "import inspect\n", + "from pygments import highlight\n", + "from pygments.lexers import PythonLexer\n", + "from pygments.formatters import HtmlFormatter\n", + "from IPython.display import display, HTML\n", + "\n", + "sys.path.insert(0, os.path.abspath(\"../dlio_extensions/dyad_torch_data_loader.py\"))\n", + "\n", + "from dyad_torch_data_loader import DYADTorchDataset" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "27e463c0", + "metadata": {}, + "outputs": [], + "source": [ + "display(HTML(highlight(inspect.getsource(DYADTorchDataset.worker_init), PythonLexer(), HtmlFormatter(full=True))))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ab755b0a", + "metadata": {}, + "outputs": [], + "source": [ + "display(HTML(highlight(inspect.getsource(DYADTorchDataset.__getitem__), PythonLexer(), HtmlFormatter(full=True))))" + ] + }, + { + "cell_type": "markdown", + "id": "fefd9ae3", + "metadata": {}, + "source": [ + "## Configure DLIO and DYAD" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "92881a8f", + "metadata": {}, + "outputs": [], + "source": [ + "kvs_namespace = \"dyad\"\n", + "initial_data_directory = \"/tmp/dlio_data\"\n", + "managed_directory = \"/tmp/dyad_data\"\n", + "workers_per_node = 8" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "68be24ed", + "metadata": {}, + "outputs": [], + "source": [ + "dyad_install_prefix = \"/usr\"\n", + "num_nodes = !flux hostlist -c\n", + "dlio_extensions_dir = !$HOME/flux-tutorial-2024/dlio_extensions\n", + "dtl_mode = \"UCX\"\n", + "workload = \"dyad_unet3d_small\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8ce527f2", + "metadata": {}, + "outputs": [], + "source": [ + "env_lines = [\n", + " f\"DYAD_KVS_NAMESPACE={kvs_namespace}\\n\",\n", + " f\"DYAD_DTL_MODE={dtl_mode}\\n\",\n", + " f\"DYAD_PATH={managed_directory}\\n\",\n", + " f\"PYTHONPATH={dlio_extensions_dir}:$PYTHONPATH\\n\",\n", + " \"DLIO_PROFILER_ENABLE=0\\n\",\n", + " \"DLIO_PROFILER_INC_METADATA=1\\n\",\n", + " \"DLIO_PROFILER_LOG_LEVEL=ERROR\\n\",\n", + " \"DLIO_PROFILER_BIND_SIGNALS=0\\n\",\n", + " \"HDF5_USE_FILE_LOCKING=0\\n\",\n", + "]\n", + "with open(\"dlio_env.txt\", \"w\") as f:\n", + " for el in env_lines:\n", + " f.write(el)" + ] + }, + { + "cell_type": "markdown", + "id": "398e110f", + "metadata": {}, + "source": [ + "## Create Flux KVS Namespace and start DYAD service" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bf132600", + "metadata": {}, + "outputs": [], + "source": [ + "!flux kvs namespace create {kvs_namespace}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3220ef03", + "metadata": {}, + "outputs": [], + "source": [ + "!flux exec -r all flux module load {dyad_install_prefix}/lib/dyad.so --mode={dtl_mode} {managed_directory}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4750013c", + "metadata": {}, + "outputs": [], + "source": [ + "!flux module list" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3322e350", + "metadata": {}, + "outputs": [], + "source": [ + "!flux kvs namespace list" + ] + }, + { + "cell_type": "markdown", + "id": "c0dfe655", + "metadata": {}, + "source": [ + "## Generate Data for Unet3D" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2dd03ec1", + "metadata": {}, + "outputs": [], + "source": [ + "!flux run -N {num_nodes} --tasks-per-node=1 mkdir -p {managed_directory} \n", + "!flux run -N {num_nodes} --tasks-per-node=1 rm -r {managed_directory}/* " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d4e5d30e", + "metadata": {}, + "outputs": [], + "source": [ + "!flux run -N {num_nodes} -o cpu-affinity=off --tasks-per-node={workers_per_node} --env-file=dlio_env.txt \\\n", + " dlio_benchmark --config-dir={dlio_extensions_dir}/configs workload={workload} \\\n", + " ++workload.dataset.data_folder={initial_data_directory} ++workload.workflow.generate_data=True \\\n", + " ++workload.workflow.train=False" + ] + }, + { + "cell_type": "markdown", + "id": "3f14ffdd", + "metadata": {}, + "source": [ + "## Run \"training\" through DLIO" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3437a068", + "metadata": {}, + "outputs": [], + "source": [ + "!flux run -N {num_nodes} -o cpu-affinity=on --tasks-per-node={workers_per_node} --env-file=dlio_env.txt \\\n", + " dlio_benchmark --config-dir={dlio_extensions_dir}/configs workload={workload} \\\n", + " ++workload.dataset.data_folder={initial_data_directory} ++workload.workflow.generate_data=False \\\n", + " ++workload.workflow.train=True" + ] + }, + { + "cell_type": "markdown", + "id": "573ce232", + "metadata": {}, + "source": [ + "## Shutdown the DYAD service and cleanup" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "755251df", + "metadata": {}, + "outputs": [], + "source": [ + "!flux kvs namespace remove {kvs_namespace}\n", + "!flux exec -r all flux module remove dyad" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2bf50c8e", + "metadata": {}, + "outputs": [], + "source": [ + "!flux module list" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e50c926e", + "metadata": {}, + "outputs": [], + "source": [ + "!flux kvs namespace list" + ] + }, + { + "cell_type": "markdown", + "id": "81d7d87f-1e09-42c8-b165-8902551f6847", + "metadata": {}, + "source": [ + "# This concludes the notebook tutorial for DYAD.\n", + "\n", + "If you are interested in learning more about DYAD, check out our [ReadTheDocs page](https://dyad.readthedocs.io/en/latest/), our [GitHub repository](https://github.com/flux-framework/dyad), and our published/presented works:\n", + "* [eScience 2022 Short Paper](https://dyad.readthedocs.io/en/latest/_downloads/27090817b034a89b76e5538e148fea9e/ShortPaper_2022_eScience_LLNL.pdf)\n", + "* [SC 2023 ACM Student Research Competition Extended Abstract](https://github.com/flux-framework/dyad/blob/main/docs/_static/ExtendedAbstract_2023_SC_ACM_SRC_DYAD.pdf)\n", + "* [IPDPS 2024 HiCOMB Workshop Paper](https://github.com/flux-framework/dyad/blob/main/docs/_static/Paper_2024_IPDPS_HiCOMB_DYAD.pdf)\n", + "\n", + "If you are interested in working with us, please reach out to Jae-Seung Yeom (yeom2@llnl.gov), Hariharan Devarajan (hariharandev1@llnl.gov), or Ian Lumsden (ilumsden@vols.utk.edu)." + ] + }, + { + "cell_type": "markdown", + "id": "d16426a9", + "metadata": {}, + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/flux.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/flux.ipynb index 9fc4c85..49d956d 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/flux.ipynb +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/flux.ipynb @@ -1623,7 +1623,7 @@ "source": [ "![https://flux-framework.org/flux-operator/_static/images/flux-operator.png](https://flux-framework.org/flux-operator/_static/images/flux-operator.png)\n", "\n", - "> See you next year! πŸ‘‹οΈπŸ˜ŽοΈ" + "" ] } ], diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/img/dyad_design.png b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/img/dyad_design.png new file mode 100644 index 0000000000000000000000000000000000000000..88bc6492add2ec407a75de196177a20553b40b4a GIT binary patch literal 184051 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

MEjSpDZ4q5{BIgXMWA$^>MW=g~9 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 z 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 literal 0 HcmV?d00001 From 41bb2189c2ad9e7a10978c2829d94597859b7e08 Mon Sep 17 00:00:00 2001 From: Ian Lumsden Date: Fri, 5 Apr 2024 17:35:25 -0400 Subject: [PATCH 04/35] Adds cleanup for the data generation directory in DYAD portion of tutorial --- .../tutorial/notebook/dyad_dlio.ipynb | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/dyad_dlio.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/dyad_dlio.ipynb index 042898e..1803e35 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/dyad_dlio.ipynb +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/dyad_dlio.ipynb @@ -223,7 +223,9 @@ "outputs": [], "source": [ "!flux run -N {num_nodes} --tasks-per-node=1 mkdir -p {managed_directory} \n", - "!flux run -N {num_nodes} --tasks-per-node=1 rm -r {managed_directory}/* " + "!flux run -N {num_nodes} --tasks-per-node=1 rm -r {managed_directory}/* \n", + "!flux run -N {num_nodes} --tasks-per-node=1 mkdir -p {initial_data_directory} \n", + "!flux run -N {num_nodes} --tasks-per-node=1 rm -r {initial_data_directory}/* " ] }, { @@ -299,6 +301,19 @@ "!flux kvs namespace list" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "2b99e3b4", + "metadata": {}, + "outputs": [], + "source": [ + "!flux run -N {num_nodes} --tasks-per-node=1 mkdir -p {managed_directory} \n", + "!flux run -N {num_nodes} --tasks-per-node=1 rm -r {managed_directory}/* \n", + "!flux run -N {num_nodes} --tasks-per-node=1 mkdir -p {initial_data_directory} \n", + "!flux run -N {num_nodes} --tasks-per-node=1 rm -r {initial_data_directory}/* " + ] + }, { "cell_type": "markdown", "id": "81d7d87f-1e09-42c8-b165-8902551f6847", From c4e3f8c9ac44fb5021d2da9399d5199a839f933b Mon Sep 17 00:00:00 2001 From: Ian Lumsden Date: Wed, 10 Apr 2024 12:40:21 -0400 Subject: [PATCH 05/35] Current progress and debugging --- .../JupyterNotebook/docker/Dockerfile.spawn | 64 ++++-- .../JupyterNotebook/requirements.txt | 3 - .../JupyterNotebook/requirements_venv.txt | 9 + .../tutorial/notebook/dyad_dlio.ipynb | 183 ++++++++++++++---- Dockerfile | 54 ++++++ 5 files changed, 261 insertions(+), 52 deletions(-) create mode 100644 2024-RIKEN-AWS/JupyterNotebook/requirements_venv.txt create mode 100644 Dockerfile diff --git a/2024-RIKEN-AWS/JupyterNotebook/docker/Dockerfile.spawn b/2024-RIKEN-AWS/JupyterNotebook/docker/Dockerfile.spawn index c426188..78454d5 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/docker/Dockerfile.spawn +++ b/2024-RIKEN-AWS/JupyterNotebook/docker/Dockerfile.spawn @@ -1,4 +1,4 @@ -FROM fluxrm/flux-sched:jammy +FROM fluxrm/flux-sched:focal # Based off of https://github.com/jupyterhub/zero-to-jupyterhub-k8s/tree/main/images/singleuser-sample # Local usage @@ -8,7 +8,8 @@ USER root ENV NB_USER=jovyan \ NB_UID=1000 \ - HOME=/home/jovyan + HOME=/home/jovyan \ + VENV_DIR=/home/jovyan/.flux_tutorial_venv RUN adduser \ --disabled-password \ @@ -19,22 +20,28 @@ RUN adduser \ ${NB_USER} RUN apt-get update \ - && apt-get upgrade -y \ + # && apt-get upgrade -y \ && apt-get install -y --no-install-recommends \ + gcc-10 \ + g++-10 \ ca-certificates \ dnsutils \ iputils-ping \ + python3.9 \ + python3.9-dev \ python3-pip \ + python3-venv \ + openmpi-bin \ + openmpi-common \ + libopenmpi-dev \ + liblz4-dev \ tini \ # requirement for nbgitpuller git \ && rm -rf /var/lib/apt/lists/* -COPY ./requirements.txt ./requirements.txt -RUN ln -s /usr/bin/python3 /usr/bin/python && \ - python -m pip install -r requirements.txt && \ - python -m pip install ipython==7.34.0 && \ - python -m IPython kernel install +ENV CC=gcc-10 \ + CXX=g++-10 # This is code to install DYAD # This was added to the RADIUSS 2023 tutorials on AWS @@ -50,20 +57,40 @@ RUN git clone https://github.com/openucx/ucx.git \ && cd .. \ && rm -rf ucx +RUN python3 -m venv $VENV_DIR +ENV VENV_SOURCE=". ${VENV_DIR}/bin/activate" + +COPY ./requirements.txt ./requirements.txt +COPY ./requirements_venv.txt ./requirements_venv.txt +RUN ln -s /usr/bin/python3 /usr/bin/python && \ + python -m pip install -r requirements.txt && \ + python -m pip install ipython==7.34.0 && \ + # python -m pip install ipykernel && \ + # python -m ipykernel install --prefix=${VENV_DIR} --name 'dyad_venv' --display-name 'DYAD Venv' && \ + python -m IPython kernel install + +RUN $VENV_SOURCE && \ + pip install -r requirements_venv.txt + +RUN chmod 777 -R $VENV_DIR && \ + mkdir -p $HOME/.local/share && \ + chmod 777 -R $HOME/.local/share + # This won't be available in K8s, but will be for a single container build COPY ./tutorial /home/jovyan/flux-tutorial-2024 -RUN git clone https://github.com/flux-framework/dyad.git \ +RUN $VENV_SOURCE \ + && git clone https://github.com/flux-framework/dyad.git \ && cd dyad \ && git checkout tutorial-riken-2024 \ && cp -r tests/integration/dlio_benchmark /home/jovyan \ && mkdir build \ && cd build \ - && cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo .. \ - && make -j \ - && sudo make install \ + && cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DDYAD_ENABLE_UCX_DATA=ON .. \ + && sudo make install -j \ && cd ../pydyad \ - && python3 -m pip install . \ + && python3 -m build --wheel . \ + && pip install $(ls ./dist/*.whl | head -1) \ && cd ../.. \ && rm -rf dyad \ && rm /home/jovyan/dlio_benchmark/*.sh \ @@ -84,11 +111,22 @@ RUN python3 -m pip install jupyter_app_launcher && \ COPY ./docker/jupyter-launcher.yaml /usr/local/share/jupyter/lab/jupyter_app_launcher/config.yaml ENV JUPYTER_APP_LAUNCHER_PATH /usr/local/share/jupyter/lab/jupyter_app_launcher +# Give jovyan user permissions to tutorial materials +RUN chmod -R 777 ~/flux-tutorial-2024 + +RUN apt-get update && \ + apt-get install -y gdb gdbserver tmux + # No permission errors here USER ${NB_USER} WORKDIR $HOME COPY ./docker/flux-icon.png $HOME/flux-icon.png +RUN ${VENV_SOURCE} && \ + pip install --upgrade --force-reinstall cffi && \ + python3 -m ipykernel install --user --name 'dyad_venv' --display-name 'DYAD Venv' && \ + jupyter kernelspec list + # note that previous examples are added via git volume in config.yaml ENV SHELL=/usr/bin/bash ENV FLUX_URI_RESOLVE_LOCAL=t diff --git a/2024-RIKEN-AWS/JupyterNotebook/requirements.txt b/2024-RIKEN-AWS/JupyterNotebook/requirements.txt index 41d2fbe..0d9d99e 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/requirements.txt +++ b/2024-RIKEN-AWS/JupyterNotebook/requirements.txt @@ -337,6 +337,3 @@ webencodings==0.5.1 # tinycss2 websocket-client==1.6.1 # via jupyter-server -# Used for the DYAD notebook -dlio_benchmark @ git+https://github.com/argonne-lcf/dlio_benchmark.git -Pygments diff --git a/2024-RIKEN-AWS/JupyterNotebook/requirements_venv.txt b/2024-RIKEN-AWS/JupyterNotebook/requirements_venv.txt new file mode 100644 index 0000000..01cea55 --- /dev/null +++ b/2024-RIKEN-AWS/JupyterNotebook/requirements_venv.txt @@ -0,0 +1,9 @@ +# Used for the DYAD notebook +Pygments +build +ipykernel +jsonschema +cffi +ply +pyyaml +dlio_benchmark @ git+https://github.com/argonne-lcf/dlio_benchmark.git \ No newline at end of file diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/dyad_dlio.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/dyad_dlio.ipynb index 1803e35..e4ae5ce 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/dyad_dlio.ipynb +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/dyad_dlio.ipynb @@ -17,7 +17,7 @@ "source": [ "> What is DYAD? πŸ€”οΈ\n", "\n", - "DYAD is a locality-aware, write-once, read-many file cache that runs on top of local NVMe and other burst buffer-style technologies (e.g., El Capitan Rabbit nodes). It is designed to accelerate large, distributed workloads, such as distributed Deep Learning (DL) training and scientific computing workflows, on HPC systems. Unlike similar tools (e.g., DataSpaces and UnifyFS), which tend to optimize for write performance, DYAD aims to provide good write **and read** performance. To optimize read performance, DYAD uses a locality-aware \"Hierarchical Data Locator,\" which prioritizes node-local metadata and data retrieval to minimize the amount of network communications. When moving data from another node, DYAD also uses a streaming RPC over RDMA protocol, which uses preallocated buffers and connection caching to maximize network bandwidth. This process is shown in the figure below:\n", + "DYAD is a transparent, locality-aware, write-once, read-many file cache that runs on top of local NVMe and other burst buffer-style technologies (e.g., El Capitan Rabbit nodes). It is designed to accelerate large, distributed workloads, such as distributed Deep Learning (DL) training and scientific computing workflows, on HPC systems. It is also designed be transparent, which allows users to leverage DYAD with little to no code refactoring. Unlike similar tools (e.g., DataSpaces and UnifyFS), which tend to optimize for write performance, DYAD aims to provide good write **and read** performance. To optimize read performance, DYAD uses a locality-aware \"Hierarchical Data Locator,\" which prioritizes node-local metadata and data retrieval to minimize the amount of network communications. When moving data from another node, DYAD also uses a streaming RPC over RDMA protocol, which uses preallocated buffers and connection caching to maximize network bandwidth. This process is shown in the figure below:\n", "\n", "![DYAD Reading Process](img/dyad_design.png)\n", "\n", @@ -42,7 +42,8 @@ "metadata": {}, "source": [ "# Accelerating Distributed Deep Learning (DL) Training with DYAD\n", - "\n" + "\n", + "Description of distributed DL (from DLIO paper)" ] }, { @@ -50,15 +51,17 @@ "id": "be8da082", "metadata": {}, "source": [ - "## Show code" - ] - }, - { - "cell_type": "markdown", - "id": "4f018bbc", - "metadata": {}, - "source": [ - "[data loader](../dlio_extensions/dyad_torch_data_loader.py)" + "## Leveraging DYAD in PyTorch\n", + "\n", + "When using custom datasets or custom techniques/tools to read a dataset from storage, PyTorch requires the creation of `Dataset` and `DataLoader` classes. To use DYAD in PyTorch-based distributed DL training, we have implemented several of these classes. They can all be found in the [dlio_extensions](../dlio_extensions) directory.\n", + "\n", + "The specific classes used in this tutorial are `DYADTorchDataset` and `DyadTorchDataLoader`, which can both be found [here](../dlio_extensions/dyad_torch_data_loader.py). The `DYADTorchDataset` class contains all the DYAD-specific code. The `DyadTorchDataLoader` class is a basic `DataLoader` designed to read individual samples from the `DYADTorchDataset` class.\n", + "\n", + "In the following code cells, we show the DYAD-specific code in `DYADTorchDataset`. As you will see, this code is very similar to standard Python file I/O. As a result, this code serves as an example of DYAD's transparency.\n", + "\n", + "

\n", + "Note: due to several aspects of PyTorch's design (described below), DYAD cannot be used as transparently as normal. Normally, in Python, users would just have to replace the built-in `open` function for DYAD's `dyad_open`. As a result, this use case should be considered the *worst case* for DYAD's transparency.\n", + "
" ] }, { @@ -76,11 +79,21 @@ "from pygments.formatters import HtmlFormatter\n", "from IPython.display import display, HTML\n", "\n", - "sys.path.insert(0, os.path.abspath(\"../dlio_extensions/dyad_torch_data_loader.py\"))\n", + "sys.path.insert(0, os.path.abspath(\"../dlio_extensions/\"))\n", "\n", "from dyad_torch_data_loader import DYADTorchDataset" ] }, + { + "cell_type": "markdown", + "id": "8007ad75", + "metadata": {}, + "source": [ + "This first block of code shows the `DYADTorchDataset.worker_init` function. This function is called to initialize the I/O processes that are spawned by the `DyadTorchDataLoader` class. As a result, this function contains two parts: (1) the initialization of PyTorch internals and utilities (e.g., a logger) and (2) the initialization of DYAD.\n", + "\n", + "Normally, DYAD is configured using environment variables, and, as a result, DYAD's initialization can be hidden from users. However, due to PyTorch's complexity and challenges in correctly propagating environment variables through PyTorch's dynamic process spawning, DYAD's transparent, environment variable-based initialization cannot be used in `DYADTorchDataset`. Instead, we manually initialize and configure DYAD using `Dyad.init()`." + ] + }, { "cell_type": "code", "execution_count": null, @@ -91,6 +104,14 @@ "display(HTML(highlight(inspect.getsource(DYADTorchDataset.worker_init), PythonLexer(), HtmlFormatter(full=True))))" ] }, + { + "cell_type": "markdown", + "id": "32a146e6", + "metadata": {}, + "source": [ + "This second block of code shows the `DYADTorchDataset.__getitem__` function. This function is called by `DyadTorchDataLoader` to read individual samples for a batch from disk. With other `Dataset` classes, this function would simply identify the file containing the requested sample and read that sample from remote storage (e.g., Lustre) using Python's built-in `open` function. On the other hand, `DYADTorchDataset` does four things. First, it identifies the file containing the requested sample. Second, it uses DYAD's `get_metadata` function to check if that file has already been cached into DYAD. Third, if the file has already been cached, it will retrieve the sample using DYAD's `dyad_open` function. This function retrieves the sample from a different node, if needed, and then makes that sample available through an interface equivalent to Python's built-in `open` function. Finally, if the file has **not** been cached, it will read the sample from remote storage (e.g., Lustre) and cache the sample into DYAD for more efficient future reading." + ] + }, { "cell_type": "code", "execution_count": null, @@ -106,36 +127,82 @@ "id": "fefd9ae3", "metadata": {}, "source": [ - "## Configure DLIO and DYAD" + "## Configure DLIO and DYAD\n", + "\n", + "Now that we've seen how DYAD is integrated into PyTorch, we will start configuring DYAD and DLIO." + ] + }, + { + "cell_type": "markdown", + "id": "731d52a3", + "metadata": {}, + "source": [ + "First, we will configure DYAD. DYAD requires three settings for configuration:\n", + "1. A namespace in the Flux key-value store, which DYAD will use for metadata management\n", + "2. A \"managed directory,\" which DYAD will use to determine the files that should be tracked\n", + "3. A data transport layer (DTL) mode, which DYAD will use to select the underlying networking library for data transfer " ] }, { "cell_type": "code", "execution_count": null, - "id": "92881a8f", + "id": "21abe5ee", "metadata": {}, "outputs": [], "source": [ "kvs_namespace = \"dyad\"\n", - "initial_data_directory = \"/tmp/dlio_data\"\n", "managed_directory = \"/tmp/dyad_data\"\n", - "workers_per_node = 8" + "dtl_mode = \"UCX\" # We currently only support UCX, so do not change this" + ] + }, + { + "cell_type": "markdown", + "id": "6e32bc27", + "metadata": {}, + "source": [ + "Next, we will configure DLIO. DLIO requires several configuration settings. However, for this tutorial, the only one that should be set is the initial data directory, or the directory where the dataset initially resides at the start of training. When running DLIO, the `DYADTorchDataset` class will dynamically copy files from this directory into DYAD's managed directory." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b5e3438f", + "metadata": {}, + "outputs": [], + "source": [ + "initial_data_directory = \"/tmp/dlio_data\"" + ] + }, + { + "cell_type": "markdown", + "id": "d979369c", + "metadata": {}, + "source": [ + "Finally, we will set the remaining configurations for DLIO. These should not be edited because they depend on the directory structure and configuration of this tutorial." ] }, { "cell_type": "code", "execution_count": null, - "id": "68be24ed", + "id": "92881a8f", "metadata": {}, "outputs": [], "source": [ - "dyad_install_prefix = \"/usr\"\n", + "workers_per_node = 8\n", + "dyad_install_prefix = \"/usr/local\"\n", "num_nodes = !flux hostlist -c\n", "dlio_extensions_dir = !$HOME/flux-tutorial-2024/dlio_extensions\n", - "dtl_mode = \"UCX\"\n", "workload = \"dyad_unet3d_small\"" ] }, + { + "cell_type": "markdown", + "id": "801719eb", + "metadata": {}, + "source": [ + "To properly set the environment variables needed for running DLIO with DYAD, we will create an environment file that is compatible with the `--env-file` flag of `flux submit`." + ] + }, { "cell_type": "code", "execution_count": null, @@ -143,20 +210,16 @@ "metadata": {}, "outputs": [], "source": [ - "env_lines = [\n", - " f\"DYAD_KVS_NAMESPACE={kvs_namespace}\\n\",\n", - " f\"DYAD_DTL_MODE={dtl_mode}\\n\",\n", - " f\"DYAD_PATH={managed_directory}\\n\",\n", - " f\"PYTHONPATH={dlio_extensions_dir}:$PYTHONPATH\\n\",\n", - " \"DLIO_PROFILER_ENABLE=0\\n\",\n", - " \"DLIO_PROFILER_INC_METADATA=1\\n\",\n", - " \"DLIO_PROFILER_LOG_LEVEL=ERROR\\n\",\n", - " \"DLIO_PROFILER_BIND_SIGNALS=0\\n\",\n", - " \"HDF5_USE_FILE_LOCKING=0\\n\",\n", - "]\n", + "env_file = \"\"\"\n", + "DYAD_KVS_NAMESPACE={kvs_namespace}\n", + "DYAD_DTL_MODE={dtl_mode}\n", + "DYAD_PATH={managed_directory}\n", + "PYTHONPATH={dlio_extensions_dir}:$PYTHONPATH\n", + "DLIO_PROFILER_ENABLE=0\n", + "\"\"\"\n", + "\n", "with open(\"dlio_env.txt\", \"w\") as f:\n", - " for el in env_lines:\n", - " f.write(el)" + " f.write(env_file)" ] }, { @@ -164,7 +227,9 @@ "id": "398e110f", "metadata": {}, "source": [ - "## Create Flux KVS Namespace and start DYAD service" + "## Create Flux KVS Namespace and start DYAD service\n", + "\n", + "Next, we will start the DYAD service. This involves two steps. First, we need to create a namespace withing the Flux key-value store. This namespace will be used by DYAD to store metadata about cached files. This metadata is then used by DYAD's Hierarchical Data Locator to locate files." ] }, { @@ -177,6 +242,14 @@ "!flux kvs namespace create {kvs_namespace}" ] }, + { + "cell_type": "markdown", + "id": "723cbeaf", + "metadata": {}, + "source": [ + "After creating the key-value store namespace, we will start the DYAD service. The DYAD service is implemented as a Flux broker module. This allows us to use Flux for service deployment and application-to-service communication. To start the DYAD service, we use the `flux module load` command. We run that command through `flux exec -r all` to deploy the service across all Flux brokers." + ] + }, { "cell_type": "code", "execution_count": null, @@ -187,6 +260,14 @@ "!flux exec -r all flux module load {dyad_install_prefix}/lib/dyad.so --mode={dtl_mode} {managed_directory}" ] }, + { + "cell_type": "markdown", + "id": "f95e0145", + "metadata": {}, + "source": [ + "Finally, we can check that the service and key-value store namespace were successfully created with the cells below." + ] + }, { "cell_type": "code", "execution_count": null, @@ -212,7 +293,9 @@ "id": "c0dfe655", "metadata": {}, "source": [ - "## Generate Data for Unet3D" + "## Generate Data for Unet3D\n", + "\n", + "Before running DLIO, we need to obtain data for the unet3d use case. Instead of downloading the full dataset, we will use DLIO to generate a smaller, synthetic version of the dataset for this tutorial." ] }, { @@ -246,7 +329,9 @@ "id": "3f14ffdd", "metadata": {}, "source": [ - "## Run \"training\" through DLIO" + "## Run \"training\" through DLIO\n", + "\n", + "Now, we will run DLIO using the command below. As DLIO runs, it will print out logging statements showing how long sample reading is taking. At the end, DLIO will print out a performance summary of the run." ] }, { @@ -267,7 +352,9 @@ "id": "573ce232", "metadata": {}, "source": [ - "## Shutdown the DYAD service and cleanup" + "## Shutdown the DYAD service and cleanup\n", + "\n", + "Now that we are done running DLIO, we need to shutdown the DYAD service and remove the key-value store namespace used by DYAD. This can be done with the two Flux commands below." ] }, { @@ -281,6 +368,14 @@ "!flux exec -r all flux module remove dyad" ] }, + { + "cell_type": "markdown", + "id": "cbd52626", + "metadata": {}, + "source": [ + "The following cells show that the DYAD service has been removed and that the namespace has been removed from the key-value store." + ] + }, { "cell_type": "code", "execution_count": null, @@ -301,6 +396,14 @@ "!flux kvs namespace list" ] }, + { + "cell_type": "markdown", + "id": "607cb1d2", + "metadata": {}, + "source": [ + "Finally, we need to remove all the files we generated while running DLIO. We use `flux run` to ensure that any node-local files are deleted." + ] + }, { "cell_type": "code", "execution_count": null, @@ -314,6 +417,14 @@ "!flux run -N {num_nodes} --tasks-per-node=1 rm -r {initial_data_directory}/* " ] }, + { + "cell_type": "markdown", + "id": "68ea2fe7", + "metadata": {}, + "source": [ + "# Full Scale Results" + ] + }, { "cell_type": "markdown", "id": "81d7d87f-1e09-42c8-b165-8902551f6847", diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ee6ba63 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,54 @@ +FROM fluxrm/flux-sched:focal + +# Based off of https://github.com/jupyterhub/zero-to-jupyterhub-k8s/tree/main/images/singleuser-sample +# Local usage +# docker run -p 8888:8888 -v $(pwd):/home/jovyan/work test + +USER root + +ENV NB_USER=jovyan \ + NB_UID=1000 \ + HOME=/home/jovyan \ + VENV_DIR=/home/jovyan/.flux_tutorial_venv + +RUN adduser \ + --disabled-password \ + --gecos "Default user" \ + --uid ${NB_UID} \ + --home ${HOME} \ + --force-badname \ + ${NB_USER} + +RUN apt-get update \ + # && apt-get upgrade -y \ + && apt-get install -y --no-install-recommends \ + gcc-10 \ + g++-10 \ + ca-certificates \ + dnsutils \ + iputils-ping \ + python3.9 \ + python3.9-dev \ + python3-pip \ + python3-venv \ + openmpi-bin \ + openmpi-common \ + libopenmpi-dev \ + liblz4-dev \ + tini \ + # requirement for nbgitpuller + git \ + && rm -rf /var/lib/apt/lists/* + +ENV CC=gcc-10 \ + CXX=g++-10 + +RUN pip install git+https://github.com/argonne-lcf/dlio_benchmark.git + +RUN pip install dlio-profiler-py +ENV DLIO_PROFILER_ENABLE=0 +# RUN dlio_benchmark workload=unet3d_a100 ++workload.dataset.data_folder=/root/data ++workload.workflow.generate_data=True ++workload.workflow.train=False ++workload.dataset.record_length=4096 ++workload.dataset.record_length_stdev=0 ++workload.dataset.record_length_resize=0 ++workload.reader.batch_size=1 ++workload.dataset.num_files_train=16 ++workload.reader.read_threads=1 + +# RUN dlio_benchmark workload=unet3d_a100 ++workload.dataset.data_folder=/root/data ++workload.workflow.generate_data=False ++workload.workflow.train=True ++workload.dataset.record_length=4096 ++workload.dataset.record_length_stdev=0 ++workload.dataset.record_length_resize=0 ++workload.reader.batch_size=1 ++workload.dataset.num_files_train=16 ++workload.reader.read_threads=1] + +ENTRYPOINT [ "/bin/bash" ] \ No newline at end of file From 223afdc07c36b55cd34995c6aa4515cd5a05618e Mon Sep 17 00:00:00 2001 From: Hariharan Devarajan Date: Wed, 10 Apr 2024 11:27:20 -0700 Subject: [PATCH 06/35] DLIO version of the Docker file. --- 2024-RIKEN-AWS/JupyterNotebook/Dockerfile.dl | 140 +++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 2024-RIKEN-AWS/JupyterNotebook/Dockerfile.dl diff --git a/2024-RIKEN-AWS/JupyterNotebook/Dockerfile.dl b/2024-RIKEN-AWS/JupyterNotebook/Dockerfile.dl new file mode 100644 index 0000000..c6b2d4c --- /dev/null +++ b/2024-RIKEN-AWS/JupyterNotebook/Dockerfile.dl @@ -0,0 +1,140 @@ +FROM fluxrm/flux-sched:focal + +# Based off of https://github.com/jupyterhub/zero-to-jupyterhub-k8s/tree/main/images/singleuser-sample +# Local usage +# docker run -p 8888:8888 -v $(pwd):/home/jovyan/work test + +USER root + +ENV NB_USER=jovyan \ + NB_UID=1000 \ + HOME=/home/jovyan \ + VENV_DIR=/home/jovyan/.flux_tutorial_venv + +RUN adduser \ + --disabled-password \ + --gecos "Default user" \ + --uid ${NB_UID} \ + --home ${HOME} \ + --force-badname \ + ${NB_USER} + +RUN apt-get update \ + # && apt-get upgrade -y \ + && apt-get install -y --no-install-recommends \ + gcc-10 \ + g++-10 \ + ca-certificates \ + dnsutils \ + iputils-ping \ + python3.9 \ + python3.9-dev \ + python3-pip \ + python3-venv \ + openmpi-bin \ + openmpi-common \ + libopenmpi-dev \ + liblz4-dev \ + tini \ + # requirement for nbgitpuller + git +#&& rm -rf /var/lib/apt/lists/* + +COPY ./requirements_venv.txt ./requirements_venv.txt +RUN pip install -r requirements_venv.txt + +ENV DLIO_PROFILER_ENABLE=0 +RUN dlio_benchmark workload=unet3d_a100 ++workload.dataset.data_folder=/root/data ++workload.workflow.generate_data=True ++workload.workflow.train=False ++workload.dataset.record_length=4096 ++workload.dataset.record_length_stdev=0 ++workload.dataset.record_length_resize=0 ++workload.reader.batch_size=1 ++workload.dataset.num_files_train=16 ++workload.reader.read_threads=1 + +RUN dlio_benchmark workload=unet3d_a100 ++workload.dataset.data_folder=/root/data ++workload.workflow.generate_data=False ++workload.workflow.train=True ++workload.dataset.record_length=4096 ++workload.dataset.record_length_stdev=0 ++workload.dataset.record_length_resize=0 ++workload.reader.batch_size=1 ++workload.dataset.num_files_train=16 ++workload.reader.read_threads=1 + +COPY ./requirements.txt ./requirements.txt +RUN pip install -r requirements.txt && \ + pip install ipython==7.34.0 && \ + python3 -m IPython kernel install + +COPY ./tutorial /home/jovyan/flux-tutorial-2024 + +# This is code to install DYAD +# This was added to the RADIUSS 2023 tutorials on AWS +RUN git clone https://github.com/openucx/ucx.git \ + && cd ucx \ + && git checkout v1.13.1 \ + && ./autogen.sh \ + && ./configure --disable-optimizations --enable-logging --enable-debug --disable-assertions --enable-mt --disable-params-check \ + --without-go --without-java --disable-cma --without-cuda --without-gdrcopy --without-verbs --without-knem --without-rmdacm \ + --without-rocm --without-xpmem --without-fuse3 --without-ugni --prefix=/usr CC=$(which gcc) CXX=$(which g++) \ + && make -j \ + && sudo make install \ + && cd .. \ + && rm -rf ucx + +RUN $VENV_SOURCE \ + && git clone https://github.com/flux-framework/dyad.git \ + && cd dyad \ + && git checkout tutorial-riken-2024 \ + && cp -r tests/integration/dlio_benchmark /home/jovyan \ + && mkdir build \ + && cd build \ + && cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DDYAD_ENABLE_UCX_DATA=ON .. \ + && sudo make install -j \ + && cd ../pydyad \ + && python3 -m build --wheel . \ + && pip install $(ls ./dist/*.whl | head -1) \ + && cd ../.. \ + && rm -rf dyad \ + && rm /home/jovyan/dlio_benchmark/*.sh \ + && rm -r /home/jovyan/dlio_benchmark/perf_analysis \ + && mv /home/jovyan/dlio_benchmark /home/jovyan/dlio_extensions \ + && mv /home/jovyan/dlio_extensions /home/jovyan/flux-tutorial-2024 + +ENV DLIO_PROFILER_ENABLE=0 + +RUN dlio_benchmark workload=unet3d_a100 ++workload.dataset.data_folder=/root/data ++workload.workflow.generate_data=False ++workload.workflow.train=True ++workload.dataset.record_length=4096 ++workload.dataset.record_length_stdev=0 ++workload.dataset.record_length_resize=0 ++workload.reader.batch_size=1 ++workload.dataset.num_files_train=16 ++workload.reader.read_threads=1 + + +# This adds the flux-tree command, which is provided in flux-sched source +# but not installed alongside production flux-core +COPY ./flux-tree/* /usr/libexec/flux/cmd/ +RUN chmod +x /usr/libexec/flux/cmd/flux-tree* + +# This customizes the launcher UI +# https://jupyter-app-launcher.readthedocs.io/en/latest/usage.html +RUN python3 -m pip install jupyter_app_launcher && \ + python3 -m pip install --upgrade jupyter-server && \ + mkdir -p /usr/local/share/jupyter/lab/jupyter_app_launcher +COPY ./docker/jupyter-launcher.yaml /usr/local/share/jupyter/lab/jupyter_app_launcher/config.yaml +ENV JUPYTER_APP_LAUNCHER_PATH /usr/local/share/jupyter/lab/jupyter_app_launcher + +# Give jovyan user permissions to tutorial materials +RUN chmod -R 777 ~/flux-tutorial-2024 + +RUN echo "dummy" +WORKDIR $HOME +COPY ./docker/flux-icon.png $HOME/flux-icon.png + +RUN ${VENV_SOURCE} && \ + pip install --upgrade --force-reinstall cffi && \ + python3 -m ipykernel install --user --name 'dyad_venv' --display-name 'DYAD Venv' && \ + jupyter kernelspec list + +# note that previous examples are added via git volume in config.yaml +ENV SHELL=/usr/bin/bash +ENV FLUX_URI_RESOLVE_LOCAL=t + +EXPOSE 8888 +ENTRYPOINT ["tini", "--"] + +# This is for JupyterHub +COPY ./docker/entrypoint.sh /entrypoint.sh + +# This is for a local start +COPY ./docker/start.sh /start.sh + +USER ${NB_USER} + +RUN dlio_benchmark workload=unet3d_a100 ++workload.dataset.data_folder=${HOME}/data ++workload.workflow.generate_data=True ++workload.workflow.train=False ++workload.dataset.record_length=4096 ++workload.dataset.record_length_stdev=0 ++workload.dataset.record_length_resize=0 ++workload.reader.batch_size=1 ++workload.dataset.num_files_train=16 ++workload.reader.read_threads=1 + +RUN dlio_benchmark workload=unet3d_a100 ++workload.dataset.data_folder=${HOME}/data ++workload.workflow.generate_data=False ++workload.workflow.train=True ++workload.dataset.record_length=4096 ++workload.dataset.record_length_stdev=0 ++workload.dataset.record_length_resize=0 ++workload.reader.batch_size=1 ++workload.dataset.num_files_train=16 ++workload.reader.read_threads=1 + +CMD ["flux", "start", "--test-size=4", "jupyter", "lab"] \ No newline at end of file From fe854e1953755f4f89cdc4349bea81b3a8049ffa Mon Sep 17 00:00:00 2001 From: Ian Lumsden Date: Wed, 10 Apr 2024 14:46:14 -0400 Subject: [PATCH 07/35] Starts to break the tutorial down into modules --- .../tutorial/notebook/01_flux_tutorial.ipynb | 91 +++ .../notebook/02_flux_scheduling.ipynb | 518 ++++++++++++++++++ .../tutorial/notebook/03_flux_framework.ipynb | 0 .../05_flux_tutorial_conclusions.ipynb | 50 ++ .../tutorial/notebook/hello-batch.sh | 8 +- .../tutorial/notebook/sleep_batch.sh | 3 + 6 files changed, 666 insertions(+), 4 deletions(-) create mode 100644 2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/01_flux_tutorial.ipynb create mode 100644 2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb create mode 100644 2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/03_flux_framework.ipynb create mode 100644 2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/05_flux_tutorial_conclusions.ipynb diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/01_flux_tutorial.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/01_flux_tutorial.ipynb new file mode 100644 index 0000000..efd46ae --- /dev/null +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/01_flux_tutorial.ipynb @@ -0,0 +1,91 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Welcome to the Flux Tutorial\n", + "\n", + "## What is Flux Framework? πŸ€”οΈ\n", + " \n", + "Flux is a flexible framework for resource management, built for your site. The framework consists of a suite of projects, tools, services, and libraries which may be used to build site-custom resource managers for High Performance Computing centers. Flux is a next-generation resource manager and scheduler with many transformative capabilities like hierarchical scheduling and resource management (you can think of it as \"fractal scheduling\") and directed-graph based resource representations.\n", + "\n", + "To provide some brief, added background on Flux and a bit more motivation for our tutorial, start by watching our YouTube video:\n", + "\n", + "\n", + "\n", + "## I'm ready! How do I do this tutorial? 😁️\n", + "\n", + "This tutorial is split into several notebooks:\n", + "* [01_flux_tutorial.ipynb](./01_flux_tutorial.ipynb) (this notebook)\n", + "* [02_flux_scheduling.ipynb](./02_flux_scheduling.ipynb)\n", + "* [03_flux_framework.ipynb](./03_flux_framework.ipynb)\n", + "* [dyad_dlio.ipynb](./dyad_dlio.ipynb)\n", + "* [05_flux_tutorial_conclusions.ipynb](./05_flux_tutorial_conclusions.ipynb)\n", + "\n", + "To go through this tutorial, you need to go through these notebooks in the order above. To step through examples in each notebook you need to execute cells. To run a cell, press Shift+Enter on your keyboard. If you prefer, you can also paste the shell commands in the JupyterLab terminal and execute them there.\n", + "\n", + "Let's get started!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Getting started with Flux\n", + "\n", + "The main unit of interaction with Flux is the **Flux instance**. A Flux instance consists of a collection of **Flux brokers**. These brokers are connected together and deploy a fully functional set of services which manage compute resources under its domain with the capability to launch jobs on those resources. A Flux instance may be running as the default resource manager on a cluster, a job in a resource manager such as Slurm, LSF, or Flux itself, or as a test instance launched locally.\n", + "\n", + "When run as a job in another resource manager, Flux is started like an MPI program, e.g., under Slurm we might run `srun [OPTIONS] flux start [SCRIPT]`. Flux is unique in that a test instance which mimics a multi-node instance can be started locally with simply `flux start --test-size=N`. This offers users to a way to learn and test interfaces and commands without access to an HPC cluster.\n", + "\n", + "To start a Flux session with 4 brokers in your container, run:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!flux start --test-size=4 flux getattr size" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The output indicates the number of brokers started successfully.\n", + "\n", + "# This concludes this notebook.\n", + "\n", + "Next, we will see how to use Flux for both traditional batch scheduling and hierarchical scheduling. To continue, open [02_flux_scheduling.ipynb](./02_flux_scheduling.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb new file mode 100644 index 0000000..73802ed --- /dev/null +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb @@ -0,0 +1,518 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Traditional and Hierarchical Schedulinng using Flux\n", + "\n", + "As mentioned in the intro video, Flux provides powerful and advanced scheduling capabilities that are important for Exascale systems like El Capitan. In this notebook, we will show how to:\n", + "1. Use Flux for traditional batch scheduling similar to what is provided by other schedulers like Slurm\n", + "2. Use Flux for hierarchical scheduling to achieve greater scheduling throughput" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Traditional batch scheduling with Flux\n", + "\n", + "In traditional batch scheduling (e.g., what Slurm provides), users send requests for resources and jobs to a centralized service (i.e., the scheduler), which stores the requests in a queue and fulfills them as possible.\n", + "\n", + "![img/single-submit.png](img/single-submit.png)\n", + "\n", + "To allow for this type of scheduling, schedulers like Slurm provide 3 main operations:\n", + "1. Submitting jobs\n", + "2. Running distributed applications within a job\n", + "3. Querying the status of jobs or canceling running jobs\n", + "\n", + "Although Flux's real benefits come from other types of scheduling (e.g., hierarchical and graph-based), Flux is still capable of handling these traditional scheduling operations. In this section, we will cover how to use Flux to perform these traditional batch scheduling operations. We will cover these operations in the order shown in the table below:\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
OperationSlurmFlux
Submit a jobsbatchflux batch
Submit an interactive jobsallocflux alloc
Run distributed application and wait for completionsrunflux run
Run distrubted application and don't wait for completionN/Aflux submit
Query the status of jobssqueue/scontrol show job job_idflux jobs/flux job info job_id
Cancel running jobsscancelflux cancel
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Submitting jobs\n", + "\n", + "Similar to Slurm's `sbatch`, users submit non-interactive, batch script-based jobs using `flux batch`. To see how `flux batch` works, let's start by looking at the batch script `sleep_batch.sh`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from IPython.display import Code\n", + "Code(filename='sleep_batch.sh', language='bash')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Similar to a Slurm batch script, a Flux batch script consists of two main sections:\n", + "1. A set of Flux directives defining the arguments that should be passed to `flux batch`\n", + "2. The commands defining the job\n", + "\n", + "In `sleep_batch.sh`, there are 3 directives:\n", + "1. `#FLUX: --nodes=2`: tells Flux to create an allocation of 2 nodes for this job\n", + "2. `#FLUX: --nslots=2`: tells Flux to reserve 2 slots total for this job\n", + "3. `#FLUX: --cores-per-slot=1`: tells Flux to reserve 1 core per slot for this job\n", + "\n", + "The rest of this batch script contains several `echo` commands follwed by 2 `flux run` commands that will sleep for 30 seconds each.\n", + "\n", + "Let's try to run our batch job with `flux batch`. Note that we provide an extra `--flags=waitable` flag to `flux batch`. Similar to Slurm, flags passed on the command line are added to the set of flags specified in the Flux directives. In this case, `--flags=waitable` allows us run `flux job wait` to wait for the completion of `flux batch` and see `stdout` and `stderr` on the command line." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!flux batch --flags=waitable ./sleep_batch.sh\n", + "!flux job wait" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Submitting interactive jobs" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Similar to Slurm's `salloc`, users can submit interactive jobs using `flux alloc`. When launching an interactive job, you can request resources using the same flags that you would pass to `flux batch` (e.g., `-N` for requesting a number of nodes).\n", + "\n", + "Due to Jupyter's lack of a pseudo-terminal, we cannot show `flux alloc` in this notebook. So, we will open a terminal in Jupyter. To do so, click on `FILE -> NEW -> TERMINAL`. Then, copy and paste the following commands into the terminal:\n", + "\n", + "```bash\n", + "$ flux alloc --nodes=2 --nslots=2 --cores-per-slot=1\n", + "$ ./hello-batch.sh\n", + "$ cat /tmp/hello-batch-1.out\n", + "$ cat /tmp/hello-batch-2.out\n", + "$ cat /tmp/hello-batch-3.out\n", + "$ cat /tmp/hello-batch-4.out\n", + "```\n", + "\n", + "The `hello-batch.sh` script (shown below) runs 4 `flux submit` commands that print output to the 4 files that we run `cat` on. It then runs `flux job wait --all`, which waits for all 4 `flux submit` commands to finish." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from IPython.display import Code\n", + "Code(filename='hello-batch.sh', language='bash')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Runing distributed applications with waiting for completion\n", + "\n", + "Similar to Slurm's `srun`, users can run distributed (e.g., MPI) applications and wait for completion using `flux run`. To see how `flux run` works, let's run the following command." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!flux run -n 4 --label-io --time-limit=5s --env-remove=LD_LIBRARY_PATH hostname" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This command does the following:\n", + "1. Remove `LD_LIBRARY_PATH` from the environment of each `hostname` program (specified by `--env-remove=LD_LIBRARY_PATH`)\n", + "2. Launch 4 copies of the `hostname` program and waits for all of them to complete before finishing (specified by `-n 4`)\n", + "3. Prepend the task rank to each line of `stdout` and `stderr` (specified by `--label-io`)\n", + "4. Kill the job automatically after 5 seconds (specified by `--time-limit=5s`)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Running distributed applications without waiting for completion\n", + "\n", + "Unlike Slurm, Flux provides the `flux submit` command to run distributed (e.g., MPI) applications **without** waiting for the application to complete. This allows users to easily run multiple distributed applications in parallel *under the same job*, which is important for many modern HPC applications such as workflows.\n", + "\n", + "To see how `flux submit` works, let's look at `hello-batch.sh` again:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from IPython.display import Code\n", + "Code(filename='hello-batch.sh', language='bash')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As you can see, this script runs 4 different `flux submit` commands, each of which prints a message to a different file. If this script were to use `flux run`, these commands would run one after the other. Instead, by using `flux submit` instead of `flux run`, Flux can run all of these `echo` programs in parallel (assuming there are enough resources to do so). This means the job that runs this script can (theoretically) complete **4 times faster** than it could using `flux run`.\n", + "\n", + "Because `flux submit` does not wait for jobs, batch scripts that use this command must use another approach for waiting on job completion. To help with this scenario, Flux provides the `flux job wait` command, which waits for the specified job/program (or all of them if the `--all` flag is provided) to complete. *Note that, to use `flux job wait`, you must pass the `--flags=waitable` flag to your Flux command.*\n", + "\n", + "To see `flux submit` in action, let's run `hello-batch.sh` through `flux batch`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!flux batch --flags=waitable --out /tmp/flux-batch.out -N2 ./hello-batch.sh\n", + "!flux job wait\n", + "!cat /tmp/hello-batch-1.out\n", + "!cat /tmp/hello-batch-2.out\n", + "!cat /tmp/hello-batch-3.out\n", + "!cat /tmp/hello-batch-4.out" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Flux also includes 2 more convenient options for submitting multiple copies of the same or similar jobs in parallel.\n", + "\n", + "First, there is `flux bulksubmit`. This command enqueues jobs based on a set of inputs which are substituted on the command line, similar to `xargs` and the GNU `parallel` utility. Unlike those programs, the jobs created by `flux bulksubmit` have access to the resources of an entire Flux instance instead of only the local system.\n", + "\n", + "Let's run a simple example of `flux bulksubmit` to see it in action." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!flux bulksubmit --watch --wait echo {} ::: foo bar baz" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The flags provided to `flux bulksubmit` tell it to print the output of each job to the terminal and wait for all the jobs to finish before returning.\n", + "\n", + "Second, there is the `-cc` flag to `flux submit`. This flag tells Flux to spawn multiple copies of a single command with different job IDs. Unlike `flux bulksubmit`, you cannot substitute arbitrary values into the command. Instead, when using the `-cc` flag, you can only substitute the job ID using `{cc}`.\n", + "\n", + "Let's run a simple example of `flux submit`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!flux submit --cc=1-10 --watch hostname" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Querying the status of jobs\n", + "\n", + "Similar to Slurm's `squeue`, users can check the status of all their jobs using `flux jobs`. To see what information `flux jobs` gives us, let's start a bunch of jobs." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!flux submit hostname\n", + "!flux submit -N1 -n2 sleep inf\n", + "!flux run hostname\n", + "!flux run /bin/false\n", + "!flux run -n4 --label-io --time-limit=5s --env-remove=LD_LIBRARY_PATH hostname\n", + "!flux submit --cc=1-10 --watch hostname" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To see the status of all pending, running, or completed jobs, we will run `flux jobs`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!flux jobs" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Users can also filter or expand what jobs they see by providing flags to `flux jobs`. The full list of flags can be obtained using `flux jobs --help` (for usage statement style) or `flux help jobs` (for man page style).\n", + "\n", + "Let's run the two code cells below to see information on all completed jobs and failed jobs respectively." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!flux jobs -a" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!flux jobs -f failed" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Canceling running jobs\n", + "\n", + "Similar to Slurm's `scancel`, users can kill running jobs and cancel pending jobs using `flux cancel`. This command can be used to kill/cancel individual jobs or all jobs.\n", + "\n", + "Let's run the command below to cancel the last submitted job. Note that `flux job last` gives us the ID of the most recently submitted job." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!flux cancel $(flux job last)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, let's run the `flux cancel --all` to cancel all running and pending jobs." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!flux cancel --all" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Hierarchical scheduling with Flux\n", + "\n", + "With traditional batch schedulers (e.g., Slurm), all job requests from all users are submitted to one centralized service. Note that our maximum job throughput is one job per second.\n", + "\n", + "![img/single-submit.png](img/single-submit.png)\n", + "\n", + "The throughput of this approach is limited by the scheduler's ability to process a single job. To improve throughput, Flux introduces the ability to launch multiple Flux instances within an existing Flux instance. This creates a hierarchy of Flux instances across which job requests can be distributed. For example, let's say we create a Flux instance that has control of some number of nodes. We then create 3 child instances (each with its own scheduler and queue). By scheduling across this hierarchy of instances, we get a throughput of 1x3, or 3 jobs per second.\n", + "\n", + "![img/instance-submit.png](img/instance-submit.png)\n", + "\n", + "By leveraging a hierarchy of Flux instances to achieve a divide-and-conquer approach to scheduling, we can exponentially increase throughput. The figure below (from our [learning guide](https://flux-framework.readthedocs.io/en/latest/guides/learning_guide.html#fully-hierarchical-resource-management-techniques)) shows this exponential increase in an actual experiment. We were able to submit 500 jobs/second using only a three-level hierarchy, whereas a centralized scheduler (1-Level in the figure) was only able to achieve one 1 job/second.\n", + "\n", + "![img/scaled-submit.png](img/scaled-submit.png)\n", + "\n", + "There are several ways to create hierarchies of Flux instances. In this tutorial, we will focus on 2 of them:\n", + "1. Through nested invocations of `flux batch`\n", + "2. Through the prototype `flux tree` command" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Hierarchical scheduling with flux batch\n", + "\n", + "As mentioned above, `flux batch` is the command used to submit non-interactive, batch script-based jobs to Flux. When a job submitted with `flux batch` starts running, Flux will create a new Flux instance over the resources reserved for that job. In other words, before even getting to the script that the user provides, `flux batch` creates a new child in the hierarchy of Flux instances. Since a Flux instance has the same capabilities no matter where it lies in the hierarchy, this newly created instance can schedule its resources in the same way that a system-wide Flux instance can. As a result, the newly created Flux instance can be used to perform additional `flux batch` commands over its subset of the resources.\n", + "\n", + "To show this in action, let's look at `sub_job1.sh` and `sub_job2.sh`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from IPython.display import Code\n", + "Code(filename='sub_job1.sh', language='bash')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from IPython.display import Code\n", + "Code(filename='sub_job2.sh', language='bash')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When scheduled with `flux batch`, `sub_job1.sh` will get a new Flux instance. It will then run `flux batch` again to run `sub_job2.sh`. Because the second `flux batch` command is within `sub_job1.sh`, the job request produced by the second `flux batch` command will go to the scheduler of the child Flux instance instead of the parent Flux instance.\n", + "\n", + "We can see this in action by running the cell below." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!flux batch -N1 ./sub_job1.sh" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now that we've submitted `sub_job1.sh`, we can look at the hierarchy for all the jobs we've run using `flux pstree`. Normally, this command can be used to show jobs in a Flux instance. However, since we are running in a Jupyter notebook, this command will have limited functionality. So, instead of just running the single command, we will run `flux pstree -a` to look at **all** jobs. In a more complex environment with more jobs, this command would show a deeper nesting. You can see examples of more complex outputs [here](https://flux-framework.readthedocs.io/en/latest/jobs/hierarchies.html?h=pstree#flux-pstree-command)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!flux pstree -a" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Hierarchical scheduling with flux tree\n", + "\n", + "`flux tree` is a prototype tool that allows you to easily create a hierarchy of Flux instances and submit work to different levels it. Alternatively, it can be thought of as a way to create a nested hierarchy of jobs that scale out.\n", + "\n", + "Let's run the command, look at the output, and talk about it." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!flux tree -T2x2 -J 4 -N 1 -c 4 -o ./tree.out -Q easy:fcfs hostname \n", + "! cat ./tree.out" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In the above cell, we run `flux tree` and look at the output file. The flags to `flux tree` do the following:\n", + "* `-T2x2`: spawn 2 Flux instances under the current instance and then spawn 2 more Flux instances under each of the other 2 (resulting in 4 leaf instances)\n", + "* `-N 1`: deploy this hierarchy across 1 node\n", + "* `-c 4`: deploy this hierarchy with 4 cores per node\n", + "* `-o ./tree.out`: write performance data for the hierarchy to `./tree.out`\n", + "\n", + "With these flags, `flux tree` creates the hierarchy shown in the image below, with each leaf-level instance scheduling the `hostname` program.\n", + "\n", + "IMAGE GOES HERE\n", + "\n", + "For this tutorial, we show `flux tree` with a relatively simple job (i.e., `hostname`). However, since this command accepts any valid jobspec that can be recognized by `flux submit`, it can be used to rapidly deploy much more complex scenarios, including scenarios where different programs are run on each leaf-level instance." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# This concludes this notebook.\n", + "\n", + "In this notebook, we demonstrated how to:\n", + "1. Use Flux for traditional batch scheduling similar to what is provided by other schedulers like Slurm\n", + "2. Use Flux for hierarchical scheduling to achieve greater scheduling throughput\n", + "\n", + "To continue with the tutorial, open [03_flux_framework.ipynb](./03_flux_framework.ipynb)." + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/03_flux_framework.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/03_flux_framework.ipynb new file mode 100644 index 0000000..e69de29 diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/05_flux_tutorial_conclusions.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/05_flux_tutorial_conclusions.ipynb new file mode 100644 index 0000000..e7eb8f4 --- /dev/null +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/05_flux_tutorial_conclusions.ipynb @@ -0,0 +1,50 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# This concludes the Flux tutorial! πŸ˜„οΈ\n", + "\n", + "Don't worry, you'll have more opportunities for using Flux! We hope you reach out to us on any of our [project repositories](https://flux-framework.org) and ask any questions that you have. We'd love your contribution to code, documentation, or just saying hello! πŸ‘‹οΈ If you have feedback on the tutorial, please let us know so we can improve it for next year. \n", + "\n", + "## What do I do now?\n", + "\n", + "Feel free to experiment more with Flux here, or (for more freedom) in the terminal. You can try more of the examples in the flux-workflow-examples directory one level up in the window to the left. If you're using a shared system like the one on the RADIUSS AWS tutorial please be mindful of other users and don't run compute intensive workloads. If you're running the tutorial in a job on an HPC cluster... compute away! ⚾️\n", + "\n", + "## How can I run this tutorial on my own?\n", + "\n", + "All materials for this tutorial (including other versions of the tutorial) can be found in our [Tutorials repo](https://github.com/flux-framework/Tutorials). To run this tutorial on your own, you can clone this repo, enter the directory for the version of the tutorial you want to run, and follow the instructions in that directory's README. All versions of this tutorial are designed to either be deployed to cloud (e.g., AWS) or be run locally using Docker.\n", + "\n", + "## Where can I learn to set up Flux on my own?\n", + "\n", + "If you're interested in installing Flux on your cluster, take a look at the [system instance instructions](https://flux-framework.readthedocs.io/en/latest/adminguide.html). If you are interested in running Flux on Kubernetes, check out the [Flux Operator](https://github.com/flux-framework/flux-operator). \n", + "\n", + "## How can I learn more about Flux?\n", + "\n", + "We've got lots of resources for learning about Flux!\n", + "- [https://flux-framework.org/](https://flux-framework.org/) Flux Framework portal for projects, releases, and publication.\n", + " - [Flux Documentation](https://flux-framework.readthedocs.io/en/latest/).\n", + " - [Flux Framework Cheat Sheet](https://flux-framework.org/cheat-sheet/)\n", + " - [Flux Glossary of Terms](https://flux-framework.readthedocs.io/en/latest/glossary.html)\n", + " - [Flux Comics](https://flux-framework.readthedocs.io/en/latest/comics/fluxonomicon.html) come and meet FluxBird - the pink bird who knows things!\n", + " - [Flux Learning Guide](https://flux-framework.readthedocs.io/en/latest/guides/learning_guide.html) learn about what Flux does, how it works, and real research applications \n", + " - [Getting Started with Flux and Go](https://converged-computing.github.io/flux-go/)\n", + " - [Getting Started with Flux in C](https://converged-computing.github.io/flux-c-examples/) *looking for contributors*\n", + "\n", + "And, of course, you can always reach out to us on any of our [project repositories](https://flux-framework.org) and ask any questions that you have. We'd love your contribution to code, documentation, or just saying hello!\n", + "\n", + "![https://flux-framework.org/flux-operator/_static/images/flux-operator.png](https://flux-framework.org/flux-operator/_static/images/flux-operator.png)\n", + "\n", + "" + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/hello-batch.sh b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/hello-batch.sh index cc6427b..3c39dc0 100755 --- a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/hello-batch.sh +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/hello-batch.sh @@ -1,8 +1,8 @@ #!/bin/bash -flux submit --flags=waitable -N1 --out /tmp/hello-batch-1.out echo "Hello job 1 from $(hostname) πŸ’›οΈ" -flux submit --flags=waitable -N1 --out /tmp/hello-batch-2.out echo "Hello job 2 from $(hostname) πŸ’šοΈ" -flux submit --flags=waitable -N1 --out /tmp/hello-batch-3.out echo "Hello job 3 from $(hostname) πŸ’™οΈ" -flux submit --flags=waitable -N1 --out /tmp/hello-batch-4.out echo "Hello job 4 from $(hostname) πŸ’œοΈ" +flux submit --flags=waitable -N1 --output=/tmp/hello-batch-1.out echo "Hello job 1 from $(hostname) πŸ’›οΈ" +flux submit --flags=waitable -N1 --output=/tmp/hello-batch-2.out echo "Hello job 2 from $(hostname) πŸ’šοΈ" +flux submit --flags=waitable -N1 --output=/tmp/hello-batch-3.out echo "Hello job 3 from $(hostname) πŸ’™οΈ" +flux submit --flags=waitable -N1 --output=/tmp/hello-batch-4.out echo "Hello job 4 from $(hostname) πŸ’œοΈ" # Wait for the jobs to finish flux job wait --all \ No newline at end of file diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/sleep_batch.sh b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/sleep_batch.sh index 19c1560..58496da 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/sleep_batch.sh +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/sleep_batch.sh @@ -1,4 +1,7 @@ #!/bin/bash +#FLUX: --nodes=2 +#FLUX: --nslots=2 +#FLUX: --cores-per-slot=1 echo "Starting my batch job" echo "Print the resources allocated to this batch job" From f036fa21b6a6a4980313ea52eccc20bca32d4833 Mon Sep 17 00:00:00 2001 From: Ian Lumsden Date: Wed, 10 Apr 2024 14:52:40 -0400 Subject: [PATCH 08/35] Minor text changes to 02_flux_scheduling.ipynb --- .../tutorial/notebook/02_flux_scheduling.ipynb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb index 73802ed..0010004 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb @@ -426,7 +426,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "When scheduled with `flux batch`, `sub_job1.sh` will get a new Flux instance. It will then run `flux batch` again to run `sub_job2.sh`. Because the second `flux batch` command is within `sub_job1.sh`, the job request produced by the second `flux batch` command will go to the scheduler of the child Flux instance instead of the parent Flux instance.\n", + "When scheduled with `flux batch`, `sub_job1.sh` will run in a new Flux instance. It will then run `flux batch` again to run `sub_job2.sh`. Because the second `flux batch` command is within `sub_job1.sh`, the job request produced by the second `flux batch` command will go to the scheduler of the child Flux instance instead of the parent Flux instance.\n", "\n", "We can see this in action by running the cell below." ] @@ -474,7 +474,7 @@ "outputs": [], "source": [ "!flux tree -T2x2 -J 4 -N 1 -c 4 -o ./tree.out -Q easy:fcfs hostname \n", - "! cat ./tree.out" + "!cat ./tree.out" ] }, { From 560a48ee08132205017129af4ead225352121440 Mon Sep 17 00:00:00 2001 From: Ian Lumsden Date: Wed, 10 Apr 2024 15:10:45 -0400 Subject: [PATCH 09/35] Adds a description of the scheduling policies used in flux tree example --- .../JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb | 1 + 1 file changed, 1 insertion(+) diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb index 0010004..780d529 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb @@ -486,6 +486,7 @@ "* `-N 1`: deploy this hierarchy across 1 node\n", "* `-c 4`: deploy this hierarchy with 4 cores per node\n", "* `-o ./tree.out`: write performance data for the hierarchy to `./tree.out`\n", + "* `-Q easy:fcfs`: use the EASY scheduling policy (backfilling with reservations) in the first level of the hierarchy and use the fcfs policy (first come, first served) in the second (i.e., leaf) level\n", "\n", "With these flags, `flux tree` creates the hierarchy shown in the image below, with each leaf-level instance scheduling the `hostname` program.\n", "\n", From 223861eb83dfc2641cdbda60ce333576d739d55f Mon Sep 17 00:00:00 2001 From: Ian Lumsden Date: Wed, 10 Apr 2024 15:21:13 -0400 Subject: [PATCH 10/35] Adds an image for flux tree --- .../tutorial/notebook/02_flux_scheduling.ipynb | 2 +- .../tutorial/notebook/img/flux-tree.png | Bin 0 -> 47131 bytes 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/img/flux-tree.png diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb index 780d529..6f4f2f6 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb @@ -490,7 +490,7 @@ "\n", "With these flags, `flux tree` creates the hierarchy shown in the image below, with each leaf-level instance scheduling the `hostname` program.\n", "\n", - "IMAGE GOES HERE\n", + "![img/flux-tree.png](img/flux-tree.png)\n", "\n", "For this tutorial, we show `flux tree` with a relatively simple job (i.e., `hostname`). However, since this command accepts any valid jobspec that can be recognized by `flux submit`, it can be used to rapidly deploy much more complex scenarios, including scenarios where different programs are run on each leaf-level instance." ] diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/img/flux-tree.png b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/img/flux-tree.png new file mode 100644 index 0000000000000000000000000000000000000000..a0dba825f20642e5af98425b9d5bccaa7b051e2e GIT binary patch literal 47131 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 z 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 Date: Wed, 10 Apr 2024 15:58:29 -0400 Subject: [PATCH 11/35] Adds attribution to images --- .../notebook/02_flux_scheduling.ipynb | 48 ++++++++++++++++--- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb index 6f4f2f6..2998f33 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb @@ -19,7 +19,11 @@ "\n", "In traditional batch scheduling (e.g., what Slurm provides), users send requests for resources and jobs to a centralized service (i.e., the scheduler), which stores the requests in a queue and fulfills them as possible.\n", "\n", - "![img/single-submit.png](img/single-submit.png)\n", + "
\n", + "\n", + "
\n", + "Image created by Vanessa Sochat for Flux Framework Components documentation
\n", + "
\n", "\n", "To allow for this type of scheduling, schedulers like Slurm provide 3 main operations:\n", "1. Submitting jobs\n", @@ -376,15 +380,27 @@ "\n", "With traditional batch schedulers (e.g., Slurm), all job requests from all users are submitted to one centralized service. Note that our maximum job throughput is one job per second.\n", "\n", - "![img/single-submit.png](img/single-submit.png)\n", + "
\n", + "\n", + "
\n", + "Image created by Vanessa Sochat for Flux Framework Components documentation
\n", + "
\n", "\n", "The throughput of this approach is limited by the scheduler's ability to process a single job. To improve throughput, Flux introduces the ability to launch multiple Flux instances within an existing Flux instance. This creates a hierarchy of Flux instances across which job requests can be distributed. For example, let's say we create a Flux instance that has control of some number of nodes. We then create 3 child instances (each with its own scheduler and queue). By scheduling across this hierarchy of instances, we get a throughput of 1x3, or 3 jobs per second.\n", "\n", - "![img/instance-submit.png](img/instance-submit.png)\n", + "
\n", + "\n", + "
\n", + "Image created by Vanessa Sochat for Flux Framework Components documentation
\n", + "
\n", "\n", "By leveraging a hierarchy of Flux instances to achieve a divide-and-conquer approach to scheduling, we can exponentially increase throughput. The figure below (from our [learning guide](https://flux-framework.readthedocs.io/en/latest/guides/learning_guide.html#fully-hierarchical-resource-management-techniques)) shows this exponential increase in an actual experiment. We were able to submit 500 jobs/second using only a three-level hierarchy, whereas a centralized scheduler (1-Level in the figure) was only able to achieve one 1 job/second.\n", "\n", - "![img/scaled-submit.png](img/scaled-submit.png)\n", + "
\n", + "\n", + "
\n", + "Image from Flux learning guide
\n", + "
\n", "\n", "There are several ways to create hierarchies of Flux instances. In this tutorial, we will focus on 2 of them:\n", "1. Through nested invocations of `flux batch`\n", @@ -490,7 +506,11 @@ "\n", "With these flags, `flux tree` creates the hierarchy shown in the image below, with each leaf-level instance scheduling the `hostname` program.\n", "\n", - "![img/flux-tree.png](img/flux-tree.png)\n", + "
\n", + "\n", + "
\n", + "Image created by Ian Lumsden based on images by Vanessa Sochat
\n", + "
\n", "\n", "For this tutorial, we show `flux tree` with a relatively simple job (i.e., `hostname`). However, since this command accepts any valid jobspec that can be recognized by `flux submit`, it can be used to rapidly deploy much more complex scenarios, including scenarios where different programs are run on each leaf-level instance." ] @@ -510,10 +530,24 @@ } ], "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, "language_info": { - "name": "python" + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.7" } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } From 26f6834dd9ce9a939c061c8aa6e374a5cf3cc7db Mon Sep 17 00:00:00 2001 From: Ian Lumsden Date: Wed, 10 Apr 2024 16:12:46 -0400 Subject: [PATCH 12/35] Adds module labels throughout tutorial --- .../tutorial/notebook/01_flux_tutorial.ipynb | 12 ++++++------ .../tutorial/notebook/02_flux_scheduling.ipynb | 2 +- .../notebook/05_flux_tutorial_conclusions.ipynb | 6 ++++++ 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/01_flux_tutorial.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/01_flux_tutorial.ipynb index efd46ae..f01498c 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/01_flux_tutorial.ipynb +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/01_flux_tutorial.ipynb @@ -31,11 +31,11 @@ "## I'm ready! How do I do this tutorial? 😁️\n", "\n", "This tutorial is split into several notebooks:\n", - "* [01_flux_tutorial.ipynb](./01_flux_tutorial.ipynb) (this notebook)\n", - "* [02_flux_scheduling.ipynb](./02_flux_scheduling.ipynb)\n", - "* [03_flux_framework.ipynb](./03_flux_framework.ipynb)\n", - "* [dyad_dlio.ipynb](./dyad_dlio.ipynb)\n", - "* [05_flux_tutorial_conclusions.ipynb](./05_flux_tutorial_conclusions.ipynb)\n", + "* [Module 1: Getting started with Flux](./01_flux_tutorial.ipynb) (the rest of this notebook)\n", + "* [Module 2: Traditional and hierarchical schedulinng using Flux](./02_flux_scheduling.ipynb)\n", + "* [Module 3: TBA](./03_flux_framework.ipynb)\n", + "* [Module 4: Accelerating Distributed Deep Learning (DL) Training with DYAD](./dyad_dlio.ipynb)\n", + "* [Module 5: Conclusions](./05_flux_tutorial_conclusions.ipynb)\n", "\n", "To go through this tutorial, you need to go through these notebooks in the order above. To step through examples in each notebook you need to execute cells. To run a cell, press Shift+Enter on your keyboard. If you prefer, you can also paste the shell commands in the JupyterLab terminal and execute them there.\n", "\n", @@ -46,7 +46,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Getting started with Flux\n", + "# Module 1: Getting started with Flux\n", "\n", "The main unit of interaction with Flux is the **Flux instance**. A Flux instance consists of a collection of **Flux brokers**. These brokers are connected together and deploy a fully functional set of services which manage compute resources under its domain with the capability to launch jobs on those resources. A Flux instance may be running as the default resource manager on a cluster, a job in a resource manager such as Slurm, LSF, or Flux itself, or as a test instance launched locally.\n", "\n", diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb index 2998f33..b492227 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Traditional and Hierarchical Schedulinng using Flux\n", + "# Module 2: Traditional and hierarchical schedulinng using Flux\n", "\n", "As mentioned in the intro video, Flux provides powerful and advanced scheduling capabilities that are important for Exascale systems like El Capitan. In this notebook, we will show how to:\n", "1. Use Flux for traditional batch scheduling similar to what is provided by other schedulers like Slurm\n", diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/05_flux_tutorial_conclusions.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/05_flux_tutorial_conclusions.ipynb index e7eb8f4..67d48aa 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/05_flux_tutorial_conclusions.ipynb +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/05_flux_tutorial_conclusions.ipynb @@ -4,6 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "# Moudle 5: Conclusions\n", "# This concludes the Flux tutorial! πŸ˜„οΈ\n", "\n", "Don't worry, you'll have more opportunities for using Flux! We hope you reach out to us on any of our [project repositories](https://flux-framework.org) and ask any questions that you have. We'd love your contribution to code, documentation, or just saying hello! πŸ‘‹οΈ If you have feedback on the tutorial, please let us know so we can improve it for next year. \n", @@ -38,6 +39,11 @@ "\n", "" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] } ], "metadata": { From 89980ed59ec96a3f743b4c2418778d5394e5e0eb Mon Sep 17 00:00:00 2001 From: Ian Lumsden Date: Wed, 10 Apr 2024 16:14:10 -0400 Subject: [PATCH 13/35] Adds Flux logo to other notebooks --- .../notebook/02_flux_scheduling.ipynb | 4 +++ .../tutorial/notebook/03_flux_framework.ipynb | 27 +++++++++++++++++++ .../05_flux_tutorial_conclusions.ipynb | 4 +++ 3 files changed, 35 insertions(+) diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb index b492227..dcd367c 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb @@ -4,6 +4,10 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "
\n", + "
\n", + "
\n", + "\n", "# Module 2: Traditional and hierarchical schedulinng using Flux\n", "\n", "As mentioned in the intro video, Flux provides powerful and advanced scheduling capabilities that are important for Exascale systems like El Capitan. In this notebook, we will show how to:\n", diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/03_flux_framework.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/03_flux_framework.ipynb index e69de29..27724e0 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/03_flux_framework.ipynb +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/03_flux_framework.ipynb @@ -0,0 +1,27 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + "
\n", + "\n", + "# Module 3: TBA" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/05_flux_tutorial_conclusions.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/05_flux_tutorial_conclusions.ipynb index 67d48aa..5c379a2 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/05_flux_tutorial_conclusions.ipynb +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/05_flux_tutorial_conclusions.ipynb @@ -4,6 +4,10 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "
\n", + "
\n", + "
\n", + "\n", "# Moudle 5: Conclusions\n", "# This concludes the Flux tutorial! πŸ˜„οΈ\n", "\n", From eaf4b1a936e6879ae95ce42aa8fb030dbe172c2a Mon Sep 17 00:00:00 2001 From: Ian Lumsden Date: Wed, 10 Apr 2024 21:12:12 -0400 Subject: [PATCH 14/35] Makes a couple of fixes for DLIO use case --- .../JupyterNotebook/tutorial/notebook/dyad_dlio.ipynb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/dyad_dlio.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/dyad_dlio.ipynb index e4ae5ce..1359880 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/dyad_dlio.ipynb +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/dyad_dlio.ipynb @@ -151,7 +151,7 @@ "outputs": [], "source": [ "kvs_namespace = \"dyad\"\n", - "managed_directory = \"/tmp/dyad_data\"\n", + "managed_directory = \"./dyad_data\"\n", "dtl_mode = \"UCX\" # We currently only support UCX, so do not change this" ] }, @@ -170,7 +170,7 @@ "metadata": {}, "outputs": [], "source": [ - "initial_data_directory = \"/tmp/dlio_data\"" + "initial_data_directory = \"./dlio_data\"" ] }, { @@ -190,8 +190,8 @@ "source": [ "workers_per_node = 8\n", "dyad_install_prefix = \"/usr/local\"\n", - "num_nodes = !flux hostlist -c\n", - "dlio_extensions_dir = !$HOME/flux-tutorial-2024/dlio_extensions\n", + "num_nodes = 4\n", + "dlio_extensions_dir = \"/home/jovyan/flux-tutorial-2024/dlio_extensions\"\n", "workload = \"dyad_unet3d_small\"" ] }, @@ -210,7 +210,7 @@ "metadata": {}, "outputs": [], "source": [ - "env_file = \"\"\"\n", + "env_file = f\"\"\"\n", "DYAD_KVS_NAMESPACE={kvs_namespace}\n", "DYAD_DTL_MODE={dtl_mode}\n", "DYAD_PATH={managed_directory}\n", From 50d7cd66ae81b4bc54a60c2d75b80074aa9b298c Mon Sep 17 00:00:00 2001 From: Ian Lumsden Date: Wed, 10 Apr 2024 21:49:23 -0400 Subject: [PATCH 15/35] Last few bugfixes in DYAD notebook --- .../JupyterNotebook/tutorial/notebook/dyad_dlio.ipynb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/dyad_dlio.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/dyad_dlio.ipynb index 1359880..5d7c21a 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/dyad_dlio.ipynb +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/dyad_dlio.ipynb @@ -188,11 +188,11 @@ "metadata": {}, "outputs": [], "source": [ - "workers_per_node = 8\n", + "workers_per_node = 1\n", "dyad_install_prefix = \"/usr/local\"\n", - "num_nodes = 4\n", + "num_nodes = 2\n", "dlio_extensions_dir = \"/home/jovyan/flux-tutorial-2024/dlio_extensions\"\n", - "workload = \"dyad_unet3d_small\"" + "workload = \"dyad_unet3d_demo\"" ] }, { From 0ab14fc66dc6847daabf00d59c42f190d07e0246 Mon Sep 17 00:00:00 2001 From: Ian Lumsden Date: Wed, 10 Apr 2024 22:14:41 -0400 Subject: [PATCH 16/35] Minor changes to Flux scheduling notebook to correct job waiting --- .../tutorial/notebook/02_flux_scheduling.ipynb | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb index dcd367c..eacc0f7 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb @@ -109,7 +109,7 @@ "\n", "The rest of this batch script contains several `echo` commands follwed by 2 `flux run` commands that will sleep for 30 seconds each.\n", "\n", - "Let's try to run our batch job with `flux batch`. Note that we provide an extra `--flags=waitable` flag to `flux batch`. Similar to Slurm, flags passed on the command line are added to the set of flags specified in the Flux directives. In this case, `--flags=waitable` allows us run `flux job wait` to wait for the completion of `flux batch` and see `stdout` and `stderr` on the command line." + "Let's try to run our batch job with `flux batch`. Note that we provide two extra flags to `flux batch`. Similar to Slurm, flags passed on the command line are added to the set of flags specified in the Flux directives. In this case, the `--output=kvs` and `--error=kvs` flags redirect `stdout` and `stderr` to the Flux key-value store (which will be covered in [Module 3](./03_flux_framework.ipynb)), which allows it to be tracked by the `flux watch` command." ] }, { @@ -118,8 +118,8 @@ "metadata": {}, "outputs": [], "source": [ - "!flux batch --flags=waitable ./sleep_batch.sh\n", - "!flux job wait" + "!flux batch --output=kvs --error=kvs ./sleep_batch.sh\n", + "!flux watch $(flux job last)" ] }, { @@ -227,7 +227,7 @@ "outputs": [], "source": [ "!flux batch --flags=waitable --out /tmp/flux-batch.out -N2 ./hello-batch.sh\n", - "!flux job wait\n", + "!flux job wait $(flux job last)\n", "!cat /tmp/hello-batch-1.out\n", "!cat /tmp/hello-batch-2.out\n", "!cat /tmp/hello-batch-3.out\n", @@ -294,7 +294,8 @@ "!flux run hostname\n", "!flux run /bin/false\n", "!flux run -n4 --label-io --time-limit=5s --env-remove=LD_LIBRARY_PATH hostname\n", - "!flux submit --cc=1-10 --watch hostname" + "!flux submit --cc=1-10 --watch hostname\n", + "!flux submit -N1 -n2 sleep inf" ] }, { From 13cc28c2ec4305b1e8ebd12e014b3d4ca93449f5 Mon Sep 17 00:00:00 2001 From: Ian Lumsden Date: Wed, 10 Apr 2024 22:26:12 -0400 Subject: [PATCH 17/35] Moves the YouTube video in the intro to a code cell so it will work in JupyterHub --- .../tutorial/notebook/01_flux_tutorial.ipynb | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/01_flux_tutorial.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/01_flux_tutorial.ipynb index f01498c..47f8104 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/01_flux_tutorial.ipynb +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/01_flux_tutorial.ipynb @@ -19,15 +19,28 @@ " \n", "Flux is a flexible framework for resource management, built for your site. The framework consists of a suite of projects, tools, services, and libraries which may be used to build site-custom resource managers for High Performance Computing centers. Flux is a next-generation resource manager and scheduler with many transformative capabilities like hierarchical scheduling and resource management (you can think of it as \"fractal scheduling\") and directed-graph based resource representations.\n", "\n", - "To provide some brief, added background on Flux and a bit more motivation for our tutorial, start by watching our YouTube video:\n", - "\n", - "\n", - "\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "## I'm ready! How do I do this tutorial? 😁️\n", "\n", "This tutorial is split into several notebooks:\n", From b1a314333b282be3d744ae8e08bd14ee87273333 Mon Sep 17 00:00:00 2001 From: Ian Lumsden Date: Wed, 10 Apr 2024 22:33:11 -0400 Subject: [PATCH 18/35] Adds DYAD to LD_LIBRARY_PATH before launching DLIO --- 2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/dyad_dlio.ipynb | 1 + 1 file changed, 1 insertion(+) diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/dyad_dlio.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/dyad_dlio.ipynb index 5d7c21a..07004da 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/dyad_dlio.ipynb +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/dyad_dlio.ipynb @@ -214,6 +214,7 @@ "DYAD_KVS_NAMESPACE={kvs_namespace}\n", "DYAD_DTL_MODE={dtl_mode}\n", "DYAD_PATH={managed_directory}\n", + "LD_LIBRARY_PATH={dyad_install_prefix}/lib\n", "PYTHONPATH={dlio_extensions_dir}:$PYTHONPATH\n", "DLIO_PROFILER_ENABLE=0\n", "\"\"\"\n", From 07033e14ecabbc0bf3e8f1b064f8c5086e251fe2 Mon Sep 17 00:00:00 2001 From: Ian Lumsden Date: Wed, 10 Apr 2024 22:37:14 -0400 Subject: [PATCH 19/35] Renames dyad_dlio.ipynb to 04_dyad_dlio.ipynb --- .../JupyterNotebook/tutorial/notebook/01_flux_tutorial.ipynb | 2 +- .../tutorial/notebook/{dyad_dlio.ipynb => 04_dyad_dlio.ipynb} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename 2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/{dyad_dlio.ipynb => 04_dyad_dlio.ipynb} (100%) diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/01_flux_tutorial.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/01_flux_tutorial.ipynb index 47f8104..3bf1cd7 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/01_flux_tutorial.ipynb +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/01_flux_tutorial.ipynb @@ -47,7 +47,7 @@ "* [Module 1: Getting started with Flux](./01_flux_tutorial.ipynb) (the rest of this notebook)\n", "* [Module 2: Traditional and hierarchical schedulinng using Flux](./02_flux_scheduling.ipynb)\n", "* [Module 3: TBA](./03_flux_framework.ipynb)\n", - "* [Module 4: Accelerating Distributed Deep Learning (DL) Training with DYAD](./dyad_dlio.ipynb)\n", + "* [Module 4: Accelerating Distributed Deep Learning (DL) Training with DYAD](./04_dyad_dlio.ipynb)\n", "* [Module 5: Conclusions](./05_flux_tutorial_conclusions.ipynb)\n", "\n", "To go through this tutorial, you need to go through these notebooks in the order above. To step through examples in each notebook you need to execute cells. To run a cell, press Shift+Enter on your keyboard. If you prefer, you can also paste the shell commands in the JupyterLab terminal and execute them there.\n", diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/dyad_dlio.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/04_dyad_dlio.ipynb similarity index 100% rename from 2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/dyad_dlio.ipynb rename to 2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/04_dyad_dlio.ipynb From 03f62e6312f8ab067661f4c6ae61f407fba92400 Mon Sep 17 00:00:00 2001 From: Ian Lumsden Date: Wed, 10 Apr 2024 22:37:34 -0400 Subject: [PATCH 20/35] Updates Dockerfile.spawn to get the DLIO use case working --- 2024-RIKEN-AWS/JupyterNotebook/Dockerfile.dl | 140 ------------------ .../JupyterNotebook/docker/Dockerfile.spawn | 82 +++++----- 2 files changed, 39 insertions(+), 183 deletions(-) delete mode 100644 2024-RIKEN-AWS/JupyterNotebook/Dockerfile.dl diff --git a/2024-RIKEN-AWS/JupyterNotebook/Dockerfile.dl b/2024-RIKEN-AWS/JupyterNotebook/Dockerfile.dl deleted file mode 100644 index c6b2d4c..0000000 --- a/2024-RIKEN-AWS/JupyterNotebook/Dockerfile.dl +++ /dev/null @@ -1,140 +0,0 @@ -FROM fluxrm/flux-sched:focal - -# Based off of https://github.com/jupyterhub/zero-to-jupyterhub-k8s/tree/main/images/singleuser-sample -# Local usage -# docker run -p 8888:8888 -v $(pwd):/home/jovyan/work test - -USER root - -ENV NB_USER=jovyan \ - NB_UID=1000 \ - HOME=/home/jovyan \ - VENV_DIR=/home/jovyan/.flux_tutorial_venv - -RUN adduser \ - --disabled-password \ - --gecos "Default user" \ - --uid ${NB_UID} \ - --home ${HOME} \ - --force-badname \ - ${NB_USER} - -RUN apt-get update \ - # && apt-get upgrade -y \ - && apt-get install -y --no-install-recommends \ - gcc-10 \ - g++-10 \ - ca-certificates \ - dnsutils \ - iputils-ping \ - python3.9 \ - python3.9-dev \ - python3-pip \ - python3-venv \ - openmpi-bin \ - openmpi-common \ - libopenmpi-dev \ - liblz4-dev \ - tini \ - # requirement for nbgitpuller - git -#&& rm -rf /var/lib/apt/lists/* - -COPY ./requirements_venv.txt ./requirements_venv.txt -RUN pip install -r requirements_venv.txt - -ENV DLIO_PROFILER_ENABLE=0 -RUN dlio_benchmark workload=unet3d_a100 ++workload.dataset.data_folder=/root/data ++workload.workflow.generate_data=True ++workload.workflow.train=False ++workload.dataset.record_length=4096 ++workload.dataset.record_length_stdev=0 ++workload.dataset.record_length_resize=0 ++workload.reader.batch_size=1 ++workload.dataset.num_files_train=16 ++workload.reader.read_threads=1 - -RUN dlio_benchmark workload=unet3d_a100 ++workload.dataset.data_folder=/root/data ++workload.workflow.generate_data=False ++workload.workflow.train=True ++workload.dataset.record_length=4096 ++workload.dataset.record_length_stdev=0 ++workload.dataset.record_length_resize=0 ++workload.reader.batch_size=1 ++workload.dataset.num_files_train=16 ++workload.reader.read_threads=1 - -COPY ./requirements.txt ./requirements.txt -RUN pip install -r requirements.txt && \ - pip install ipython==7.34.0 && \ - python3 -m IPython kernel install - -COPY ./tutorial /home/jovyan/flux-tutorial-2024 - -# This is code to install DYAD -# This was added to the RADIUSS 2023 tutorials on AWS -RUN git clone https://github.com/openucx/ucx.git \ - && cd ucx \ - && git checkout v1.13.1 \ - && ./autogen.sh \ - && ./configure --disable-optimizations --enable-logging --enable-debug --disable-assertions --enable-mt --disable-params-check \ - --without-go --without-java --disable-cma --without-cuda --without-gdrcopy --without-verbs --without-knem --without-rmdacm \ - --without-rocm --without-xpmem --without-fuse3 --without-ugni --prefix=/usr CC=$(which gcc) CXX=$(which g++) \ - && make -j \ - && sudo make install \ - && cd .. \ - && rm -rf ucx - -RUN $VENV_SOURCE \ - && git clone https://github.com/flux-framework/dyad.git \ - && cd dyad \ - && git checkout tutorial-riken-2024 \ - && cp -r tests/integration/dlio_benchmark /home/jovyan \ - && mkdir build \ - && cd build \ - && cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DDYAD_ENABLE_UCX_DATA=ON .. \ - && sudo make install -j \ - && cd ../pydyad \ - && python3 -m build --wheel . \ - && pip install $(ls ./dist/*.whl | head -1) \ - && cd ../.. \ - && rm -rf dyad \ - && rm /home/jovyan/dlio_benchmark/*.sh \ - && rm -r /home/jovyan/dlio_benchmark/perf_analysis \ - && mv /home/jovyan/dlio_benchmark /home/jovyan/dlio_extensions \ - && mv /home/jovyan/dlio_extensions /home/jovyan/flux-tutorial-2024 - -ENV DLIO_PROFILER_ENABLE=0 - -RUN dlio_benchmark workload=unet3d_a100 ++workload.dataset.data_folder=/root/data ++workload.workflow.generate_data=False ++workload.workflow.train=True ++workload.dataset.record_length=4096 ++workload.dataset.record_length_stdev=0 ++workload.dataset.record_length_resize=0 ++workload.reader.batch_size=1 ++workload.dataset.num_files_train=16 ++workload.reader.read_threads=1 - - -# This adds the flux-tree command, which is provided in flux-sched source -# but not installed alongside production flux-core -COPY ./flux-tree/* /usr/libexec/flux/cmd/ -RUN chmod +x /usr/libexec/flux/cmd/flux-tree* - -# This customizes the launcher UI -# https://jupyter-app-launcher.readthedocs.io/en/latest/usage.html -RUN python3 -m pip install jupyter_app_launcher && \ - python3 -m pip install --upgrade jupyter-server && \ - mkdir -p /usr/local/share/jupyter/lab/jupyter_app_launcher -COPY ./docker/jupyter-launcher.yaml /usr/local/share/jupyter/lab/jupyter_app_launcher/config.yaml -ENV JUPYTER_APP_LAUNCHER_PATH /usr/local/share/jupyter/lab/jupyter_app_launcher - -# Give jovyan user permissions to tutorial materials -RUN chmod -R 777 ~/flux-tutorial-2024 - -RUN echo "dummy" -WORKDIR $HOME -COPY ./docker/flux-icon.png $HOME/flux-icon.png - -RUN ${VENV_SOURCE} && \ - pip install --upgrade --force-reinstall cffi && \ - python3 -m ipykernel install --user --name 'dyad_venv' --display-name 'DYAD Venv' && \ - jupyter kernelspec list - -# note that previous examples are added via git volume in config.yaml -ENV SHELL=/usr/bin/bash -ENV FLUX_URI_RESOLVE_LOCAL=t - -EXPOSE 8888 -ENTRYPOINT ["tini", "--"] - -# This is for JupyterHub -COPY ./docker/entrypoint.sh /entrypoint.sh - -# This is for a local start -COPY ./docker/start.sh /start.sh - -USER ${NB_USER} - -RUN dlio_benchmark workload=unet3d_a100 ++workload.dataset.data_folder=${HOME}/data ++workload.workflow.generate_data=True ++workload.workflow.train=False ++workload.dataset.record_length=4096 ++workload.dataset.record_length_stdev=0 ++workload.dataset.record_length_resize=0 ++workload.reader.batch_size=1 ++workload.dataset.num_files_train=16 ++workload.reader.read_threads=1 - -RUN dlio_benchmark workload=unet3d_a100 ++workload.dataset.data_folder=${HOME}/data ++workload.workflow.generate_data=False ++workload.workflow.train=True ++workload.dataset.record_length=4096 ++workload.dataset.record_length_stdev=0 ++workload.dataset.record_length_resize=0 ++workload.reader.batch_size=1 ++workload.dataset.num_files_train=16 ++workload.reader.read_threads=1 - -CMD ["flux", "start", "--test-size=4", "jupyter", "lab"] \ No newline at end of file diff --git a/2024-RIKEN-AWS/JupyterNotebook/docker/Dockerfile.spawn b/2024-RIKEN-AWS/JupyterNotebook/docker/Dockerfile.spawn index 78454d5..6cf7357 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/docker/Dockerfile.spawn +++ b/2024-RIKEN-AWS/JupyterNotebook/docker/Dockerfile.spawn @@ -8,8 +8,8 @@ USER root ENV NB_USER=jovyan \ NB_UID=1000 \ - HOME=/home/jovyan \ - VENV_DIR=/home/jovyan/.flux_tutorial_venv + HOME=/home/jovyan + # VENV_DIR=/home/jovyan/.flux_tutorial_venv RUN adduser \ --disabled-password \ @@ -37,11 +37,23 @@ RUN apt-get update \ liblz4-dev \ tini \ # requirement for nbgitpuller - git \ - && rm -rf /var/lib/apt/lists/* + git +#&& rm -rf /var/lib/apt/lists/* -ENV CC=gcc-10 \ - CXX=g++-10 +COPY ./requirements_venv.txt ./requirements_venv.txt +RUN python3 -m pip install -r requirements_venv.txt + +# ENV DLIO_PROFILER_ENABLE=0 +# RUN dlio_benchmark workload=unet3d_a100 ++workload.dataset.data_folder=/root/data ++workload.workflow.generate_data=True ++workload.workflow.train=False ++workload.dataset.record_length=4096 ++workload.dataset.record_length_stdev=0 ++workload.dataset.record_length_resize=0 ++workload.reader.batch_size=1 ++workload.dataset.num_files_train=16 ++workload.reader.read_threads=1 +# +# RUN dlio_benchmark workload=unet3d_a100 ++workload.dataset.data_folder=/root/data ++workload.workflow.generate_data=False ++workload.workflow.train=True ++workload.dataset.record_length=4096 ++workload.dataset.record_length_stdev=0 ++workload.dataset.record_length_resize=0 ++workload.reader.batch_size=1 ++workload.dataset.num_files_train=16 ++workload.reader.read_threads=1 + +COPY ./requirements.txt ./requirements.txt +RUN python3 -m pip install -r requirements.txt && \ + python3 -m pip install ipython==7.34.0 && \ + python3 -m IPython kernel install + +COPY ./tutorial /home/jovyan/flux-tutorial-2024 # This is code to install DYAD # This was added to the RADIUSS 2023 tutorials on AWS @@ -57,30 +69,8 @@ RUN git clone https://github.com/openucx/ucx.git \ && cd .. \ && rm -rf ucx -RUN python3 -m venv $VENV_DIR -ENV VENV_SOURCE=". ${VENV_DIR}/bin/activate" - -COPY ./requirements.txt ./requirements.txt -COPY ./requirements_venv.txt ./requirements_venv.txt -RUN ln -s /usr/bin/python3 /usr/bin/python && \ - python -m pip install -r requirements.txt && \ - python -m pip install ipython==7.34.0 && \ - # python -m pip install ipykernel && \ - # python -m ipykernel install --prefix=${VENV_DIR} --name 'dyad_venv' --display-name 'DYAD Venv' && \ - python -m IPython kernel install - -RUN $VENV_SOURCE && \ - pip install -r requirements_venv.txt - -RUN chmod 777 -R $VENV_DIR && \ - mkdir -p $HOME/.local/share && \ - chmod 777 -R $HOME/.local/share - -# This won't be available in K8s, but will be for a single container build -COPY ./tutorial /home/jovyan/flux-tutorial-2024 - -RUN $VENV_SOURCE \ - && git clone https://github.com/flux-framework/dyad.git \ +# RUN $VENV_SOURCE \ +RUN git clone https://github.com/flux-framework/dyad.git \ && cd dyad \ && git checkout tutorial-riken-2024 \ && cp -r tests/integration/dlio_benchmark /home/jovyan \ @@ -98,6 +88,11 @@ RUN $VENV_SOURCE \ && mv /home/jovyan/dlio_benchmark /home/jovyan/dlio_extensions \ && mv /home/jovyan/dlio_extensions /home/jovyan/flux-tutorial-2024 +# ENV DLIO_PROFILER_ENABLE=0 +# +# RUN dlio_benchmark workload=unet3d_a100 ++workload.dataset.data_folder=/root/data ++workload.workflow.generate_data=False ++workload.workflow.train=True ++workload.dataset.record_length=4096 ++workload.dataset.record_length_stdev=0 ++workload.dataset.record_length_resize=0 ++workload.reader.batch_size=1 ++workload.dataset.num_files_train=16 ++workload.reader.read_threads=1 + + # This adds the flux-tree command, which is provided in flux-sched source # but not installed alongside production flux-core COPY ./flux-tree/* /usr/libexec/flux/cmd/ @@ -114,18 +109,13 @@ ENV JUPYTER_APP_LAUNCHER_PATH /usr/local/share/jupyter/lab/jupyter_app_launcher # Give jovyan user permissions to tutorial materials RUN chmod -R 777 ~/flux-tutorial-2024 -RUN apt-get update && \ - apt-get install -y gdb gdbserver tmux - -# No permission errors here -USER ${NB_USER} WORKDIR $HOME COPY ./docker/flux-icon.png $HOME/flux-icon.png -RUN ${VENV_SOURCE} && \ - pip install --upgrade --force-reinstall cffi && \ - python3 -m ipykernel install --user --name 'dyad_venv' --display-name 'DYAD Venv' && \ - jupyter kernelspec list +# RUN ${VENV_SOURCE} && \ +# pip install --upgrade --force-reinstall cffi && \ +# python3 -m ipykernel install --user --name 'dyad_venv' --display-name 'DYAD Venv' && \ +# jupyter kernelspec list # note that previous examples are added via git volume in config.yaml ENV SHELL=/usr/bin/bash @@ -139,8 +129,14 @@ COPY ./docker/entrypoint.sh /entrypoint.sh # This is for a local start COPY ./docker/start.sh /start.sh -CMD ["flux", "start", "--test-size=4", "jupyter", "lab"] -# Previous command for non-kubernetes -# CMD PATH=$HOME/.local/bin:$PATH \ -# flux start --test-size=4 /home/fluxuser/.local/bin/jupyterhub-singleuser +RUN mkdir -p $HOME/.local/share && \ + chmod 777 $HOME/.local/share + +USER ${NB_USER} + +# RUN dlio_benchmark workload=unet3d_a100 ++workload.dataset.data_folder=${HOME}/data ++workload.workflow.generate_data=True ++workload.workflow.train=False ++workload.dataset.record_length=4096 ++workload.dataset.record_length_stdev=0 ++workload.dataset.record_length_resize=0 ++workload.reader.batch_size=1 ++workload.dataset.num_files_train=16 ++workload.reader.read_threads=1 +# +# RUN dlio_benchmark workload=unet3d_a100 ++workload.dataset.data_folder=${HOME}/data ++workload.workflow.generate_data=False ++workload.workflow.train=True ++workload.dataset.record_length=4096 ++workload.dataset.record_length_stdev=0 ++workload.dataset.record_length_resize=0 ++workload.reader.batch_size=1 ++workload.dataset.num_files_train=16 ++workload.reader.read_threads=1 + +CMD ["flux", "start", "--test-size=4", "jupyter", "lab"] From 9af1117f079147563ef19047a3313b8447ec8c15 Mon Sep 17 00:00:00 2001 From: Ian Lumsden Date: Wed, 10 Apr 2024 22:44:31 -0400 Subject: [PATCH 21/35] Adds a tutorial-specific copy of the DYAD Torch data loader --- .../JupyterNotebook/docker/Dockerfile.spawn | 5 +- .../configs/workload/dyad_unet3d_demo.yaml | 35 +++ .../dlio_extensions/dyad_torch_data_loader.py | 223 ++++++++++++++++++ 3 files changed, 259 insertions(+), 4 deletions(-) create mode 100644 2024-RIKEN-AWS/JupyterNotebook/tutorial/dlio_extensions/configs/workload/dyad_unet3d_demo.yaml create mode 100644 2024-RIKEN-AWS/JupyterNotebook/tutorial/dlio_extensions/dyad_torch_data_loader.py diff --git a/2024-RIKEN-AWS/JupyterNotebook/docker/Dockerfile.spawn b/2024-RIKEN-AWS/JupyterNotebook/docker/Dockerfile.spawn index 6cf7357..714b339 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/docker/Dockerfile.spawn +++ b/2024-RIKEN-AWS/JupyterNotebook/docker/Dockerfile.spawn @@ -73,7 +73,6 @@ RUN git clone https://github.com/openucx/ucx.git \ RUN git clone https://github.com/flux-framework/dyad.git \ && cd dyad \ && git checkout tutorial-riken-2024 \ - && cp -r tests/integration/dlio_benchmark /home/jovyan \ && mkdir build \ && cd build \ && cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DDYAD_ENABLE_UCX_DATA=ON .. \ @@ -84,9 +83,7 @@ RUN git clone https://github.com/flux-framework/dyad.git \ && cd ../.. \ && rm -rf dyad \ && rm /home/jovyan/dlio_benchmark/*.sh \ - && rm -r /home/jovyan/dlio_benchmark/perf_analysis \ - && mv /home/jovyan/dlio_benchmark /home/jovyan/dlio_extensions \ - && mv /home/jovyan/dlio_extensions /home/jovyan/flux-tutorial-2024 + && rm -r /home/jovyan/dlio_benchmark/perf_analysis # ENV DLIO_PROFILER_ENABLE=0 # diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/dlio_extensions/configs/workload/dyad_unet3d_demo.yaml b/2024-RIKEN-AWS/JupyterNotebook/tutorial/dlio_extensions/configs/workload/dyad_unet3d_demo.yaml new file mode 100644 index 0000000..27641a3 --- /dev/null +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/dlio_extensions/configs/workload/dyad_unet3d_demo.yaml @@ -0,0 +1,35 @@ +model: unet3d + +framework: pytorch + +workflow: + generate_data: False + train: True + checkpoint: False + +dataset: + data_folder: data/unet3d/ + format: npz + num_files_train: 16 + num_samples_per_file: 1 + record_length: 4096 + +reader: + data_loader: pytorch + batch_size: 1 + read_threads: 1 + file_shuffle: seed + sample_shuffle: seed + multiprocessing_context: spawn + data_loader_classname: dyad_torch_data_loader.DyadTorchDataLoader + data_loader_sampler: index + +train: + epochs: 1 + computation_time: 1 + +checkpoint: + checkpoint_folder: checkpoints/unet3d + checkpoint_after_epoch: 5 + epochs_between_checkpoints: 2 + model_size: 499153191 \ No newline at end of file diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/dlio_extensions/dyad_torch_data_loader.py b/2024-RIKEN-AWS/JupyterNotebook/tutorial/dlio_extensions/dyad_torch_data_loader.py new file mode 100644 index 0000000..cd330be --- /dev/null +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/dlio_extensions/dyad_torch_data_loader.py @@ -0,0 +1,223 @@ +""" + Copyright (c) 2022, UChicago Argonne, LLC + All Rights Reserved + + 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 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" +from time import time +import logging +import math +import pickle +import torch +from torch.utils.data import Dataset, DataLoader, RandomSampler, SequentialSampler + +from dlio_benchmark.common.constants import MODULE_DATA_LOADER +from dlio_benchmark.common.enumerations import Shuffle, DatasetType, DataLoaderType +from dlio_benchmark.data_loader.base_data_loader import BaseDataLoader +from dlio_benchmark.reader.reader_factory import ReaderFactory +from dlio_benchmark.utils.utility import utcnow, DLIOMPI +from dlio_benchmark.utils.config import ConfigArguments +from dlio_profiler.logger import fn_interceptor as Profile + +from pydyad import Dyad, dyad_open +from pydyad.bindings import DTLMode, DTLCommMode +import numpy as np +import flux +import os + +dlp = Profile(MODULE_DATA_LOADER) + + +class DYADTorchDataset(Dataset): + """ + Currently, we only support loading one sample per file + TODO: support multiple samples per file + """ + @dlp.log_init + def __init__(self, format_type, dataset_type, epoch, num_samples, num_workers, batch_size): + self.format_type = format_type + self.dataset_type = dataset_type + self.epoch_number = epoch + self.num_samples = num_samples + self.reader = None + self.num_images_read = 0 + self.batch_size = batch_size + args = ConfigArguments.get_instance() + self.serial_args = pickle.dumps(args) + self.dlp_logger = None + if num_workers == 0: + self.worker_init(-1) + + @dlp.log + def worker_init(self, worker_id): + pickle.loads(self.serial_args) + self._args = ConfigArguments.get_instance() + self._args.configure_dlio_logging(is_child=True) + self.dlp_logger = self._args.configure_dlio_profiler(is_child=True, use_pid=True) + logging.debug(f"{utcnow()} worker initialized {worker_id} with format {self.format_type}") + self.reader = ReaderFactory.get_reader(type=self.format_type, + dataset_type=self.dataset_type, + thread_index=worker_id, + epoch_number=self.epoch_number) + self.dyad_io = Dyad() + is_local = os.getenv("DYAD_LOCAL_TEST", "0") == "1" + self.broker_per_node = int(os.getenv("BROKERS_PER_NODE", "1")) + + self.f = flux.Flux() + self.broker_rank = self.f.get_rank() + if is_local: + self.dyad_managed_directory = os.path.join(os.getenv("DYAD_PATH", ""), str(self.f.get_rank())) + else: + self.dyad_managed_directory = os.getenv("DYAD_PATH", "") + self.my_node_index = int(self.broker_rank*1.0 / self.broker_per_node) + dtl_str = os.getenv("DYAD_DTL_MODE", "FLUX_RPC") + mode = DTLMode.DYAD_DTL_FLUX_RPC + namespace = os.getenv("DYAD_KVS_NAMESPACE") + logging.debug(f"{utcnow()} Rank {DLIOMPI.get_instance().rank()} init dyad {self.dyad_managed_directory} {dtl_str} {namespace}") + if dtl_str == "UCX": + mode = DTLMode.DYAD_DTL_UCX + self.dyad_io.init(debug=self._args.debug, check=False, shared_storage=False, reinit=False, + async_publish=True, fsync_write=False, key_depth=3, + service_mux=self.broker_per_node, + key_bins=1024, kvs_namespace=os.getenv("DYAD_KVS_NAMESPACE"), + prod_managed_path=self.dyad_managed_directory, cons_managed_path=self.dyad_managed_directory, + dtl_mode=mode, dtl_comm_mode=DTLCommMode.DYAD_COMM_RECV) + + def __del__(self): + if self.dlp_logger: + self.dlp_logger.finalize() + @dlp.log + def __len__(self): + return self.num_samples + + @dlp.log + def __getitem__(self, image_idx): + logging.debug(f"{utcnow()} Rank {DLIOMPI.get_instance().rank()} reading {image_idx} image") + self.num_images_read += 1 + step = int(math.ceil(self.num_images_read / self.batch_size)) + filename, sample_index = self._args.global_index_map[image_idx] + is_present = False + file_obj = None + base_fname = filename + dlp.update(args={"fname":filename}) + dlp.update(args={"image_idx":image_idx}) + if self.dyad_managed_directory != "": + logging.debug(f"{utcnow()} Rank {DLIOMPI.get_instance().rank()} reading metadata") + base_fname = os.path.join(self.dyad_managed_directory, os.path.basename(filename)) + file_obj = self.dyad_io.get_metadata(fname=base_fname, should_wait=False, raw=True) + logging.debug(f"Using managed directory {self.dyad_managed_directory} {base_fname} {file_obj}") + is_present = True + if file_obj: + access_mode = "remote" + file_node_index = int(file_obj.contents.owner_rank*1.0 / self.broker_per_node) + if self.my_node_index == file_node_index: + access_mode = "local" + dlp.update(args={"owner_rank":str(file_obj.contents.owner_rank)}) + dlp.update(args={"my_broker":str(self.broker_rank)}) + dlp.update(args={"mode":"dyad"}) + dlp.update(args={"access":access_mode}) + logging.info(f"{utcnow()} Rank {DLIOMPI.get_instance().rank()} reading {image_idx} sample from {access_mode} dyad {file_obj.contents.owner_rank}") + logging.debug(f"Reading from managed directory {base_fname}") + with dyad_open(base_fname, "rb", dyad_ctx=self.dyad_io, metadata_wrapper=file_obj) as f: + try: + data = np.load(f, allow_pickle=True)["x"] + except: + logging.info(f"{utcnow()} Rank {DLIOMPI.get_instance().rank()} got wierd {image_idx} sample from {access_mode} dyad {file_obj.contents.owner_rank}") + data = self._args.resized_image + logging.info(f"{utcnow()} Rank {DLIOMPI.get_instance().rank()} read {image_idx} sample from {access_mode} dyad {file_obj.contents.owner_rank}") + self.dyad_io.free_metadata(file_obj) + else: + dlp.update(args={"mode":"pfs"}) + dlp.update(args={"access":"remote"}) + logging.info(f"{utcnow()} Rank {DLIOMPI.get_instance().rank()} reading {image_idx} sample from pfs {base_fname}") + logging.debug(f"Reading from pfs {base_fname}") + data = self.reader.read_index(image_idx, step) + if is_present: + logging.debug(f"Writing to managed_directory {base_fname}") + with dyad_open(base_fname, "wb", dyad_ctx=self.dyad_io) as f: + np.savez(f, x=data) + logging.debug(f"Read from pfs {base_fname}") + + dlp.update(step=step) + dlp.update(image_size=data.nbytes) + return data + +class DyadTorchDataLoader(BaseDataLoader): + @dlp.log_init + def __init__(self, format_type, dataset_type, epoch_number): + super().__init__(format_type, dataset_type, epoch_number, DataLoaderType.PYTORCH) + + @dlp.log + def read(self): + do_shuffle = True if self._args.sample_shuffle != Shuffle.OFF else False + dataset = DYADTorchDataset(self.format_type, self.dataset_type, self.epoch_number, self.num_samples, self._args.read_threads, self.batch_size) + if do_shuffle: + sampler = RandomSampler(dataset) + else: + sampler = SequentialSampler(dataset) + if self._args.read_threads >= 1: + prefetch_factor = math.ceil(self._args.prefetch_size / self._args.read_threads) + else: + prefetch_factor = self._args.prefetch_size + if prefetch_factor > 0: + if self._args.my_rank == 0: + logging.debug( + f"{utcnow()} Prefetch size is {self._args.prefetch_size}; prefetch factor of {prefetch_factor} will be set to Torch DataLoader.") + else: + prefetch_factor = 2 + if self._args.my_rank == 0: + logging.debug( + f"{utcnow()} Prefetch size is 0; a default prefetch factor of 2 will be set to Torch DataLoader.") + logging.debug(f"{utcnow()} Setup dataloader with {self._args.read_threads} workers {torch.__version__}") + if self._args.read_threads==0: + kwargs={} + else: + kwargs={'multiprocessing_context':self._args.multiprocessing_context, + 'prefetch_factor': prefetch_factor} + if torch.__version__ != '1.3.1': + kwargs['persistent_workers'] = True + if torch.__version__ == '1.3.1': + if 'prefetch_factor' in kwargs: + del kwargs['prefetch_factor'] + self._dataset = DataLoader(dataset, + batch_size=self.batch_size, + sampler=sampler, + num_workers=self._args.read_threads, + pin_memory=True, + drop_last=True, + worker_init_fn=dataset.worker_init, + **kwargs) + else: + self._dataset = DataLoader(dataset, + batch_size=self.batch_size, + sampler=sampler, + num_workers=self._args.read_threads, + pin_memory=True, + drop_last=True, + worker_init_fn=dataset.worker_init, + **kwargs) # 2 is the default value + logging.debug(f"{utcnow()} Rank {self._args.my_rank} will read {len(self._dataset) * self.batch_size} files") + + # self._dataset.sampler.set_epoch(epoch_number) + + @dlp.log + def next(self): + super().next() + total = self._args.training_steps if self.dataset_type is DatasetType.TRAIN else self._args.eval_steps + logging.debug(f"{utcnow()} Rank {self._args.my_rank} should read {total} batches") + for batch in self._dataset: + yield batch + + @dlp.log + def finalize(self): + pass \ No newline at end of file From 9d1fecd9b5014c32015b49e58cdd17c9f6b3e97c Mon Sep 17 00:00:00 2001 From: Ian Lumsden Date: Wed, 10 Apr 2024 22:57:26 -0400 Subject: [PATCH 22/35] Minor bugfixes after moving DLIO extensions into the repo --- .../JupyterNotebook/docker/Dockerfile.spawn | 4 +--- .../tutorial/notebook/02_flux_scheduling.ipynb | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/2024-RIKEN-AWS/JupyterNotebook/docker/Dockerfile.spawn b/2024-RIKEN-AWS/JupyterNotebook/docker/Dockerfile.spawn index 714b339..7b855b3 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/docker/Dockerfile.spawn +++ b/2024-RIKEN-AWS/JupyterNotebook/docker/Dockerfile.spawn @@ -81,9 +81,7 @@ RUN git clone https://github.com/flux-framework/dyad.git \ && python3 -m build --wheel . \ && pip install $(ls ./dist/*.whl | head -1) \ && cd ../.. \ - && rm -rf dyad \ - && rm /home/jovyan/dlio_benchmark/*.sh \ - && rm -r /home/jovyan/dlio_benchmark/perf_analysis + && rm -rf dyad # ENV DLIO_PROFILER_ENABLE=0 # diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb index eacc0f7..dc28110 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb @@ -361,6 +361,15 @@ "!flux cancel $(flux job last)" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!flux jobs" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -377,6 +386,15 @@ "!flux cancel --all" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!flux jobs" + ] + }, { "cell_type": "markdown", "metadata": {}, From 16139072f26635e4c4fda2c757337ca923a17718 Mon Sep 17 00:00:00 2001 From: Ian Lumsden Date: Wed, 10 Apr 2024 23:49:05 -0400 Subject: [PATCH 23/35] Simplifies the DYAD Torch data loader --- .../dlio_extensions/dyad_torch_data_loader.py | 75 +++++-------------- 1 file changed, 18 insertions(+), 57 deletions(-) diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/dlio_extensions/dyad_torch_data_loader.py b/2024-RIKEN-AWS/JupyterNotebook/tutorial/dlio_extensions/dyad_torch_data_loader.py index cd330be..21f652a 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/tutorial/dlio_extensions/dyad_torch_data_loader.py +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/dlio_extensions/dyad_torch_data_loader.py @@ -15,7 +15,6 @@ limitations under the License. """ from time import time -import logging import math import pickle import torch @@ -27,7 +26,6 @@ from dlio_benchmark.reader.reader_factory import ReaderFactory from dlio_benchmark.utils.utility import utcnow, DLIOMPI from dlio_benchmark.utils.config import ConfigArguments -from dlio_profiler.logger import fn_interceptor as Profile from pydyad import Dyad, dyad_open from pydyad.bindings import DTLMode, DTLCommMode @@ -35,7 +33,6 @@ import flux import os -dlp = Profile(MODULE_DATA_LOADER) class DYADTorchDataset(Dataset): @@ -43,7 +40,6 @@ class DYADTorchDataset(Dataset): Currently, we only support loading one sample per file TODO: support multiple samples per file """ - @dlp.log_init def __init__(self, format_type, dataset_type, epoch, num_samples, num_workers, batch_size): self.format_type = format_type self.dataset_type = dataset_type @@ -54,38 +50,33 @@ def __init__(self, format_type, dataset_type, epoch, num_samples, num_workers, b self.batch_size = batch_size args = ConfigArguments.get_instance() self.serial_args = pickle.dumps(args) - self.dlp_logger = None if num_workers == 0: self.worker_init(-1) + self.broker_per_node = 1 - @dlp.log def worker_init(self, worker_id): + # Configure PyTorch components pickle.loads(self.serial_args) self._args = ConfigArguments.get_instance() self._args.configure_dlio_logging(is_child=True) - self.dlp_logger = self._args.configure_dlio_profiler(is_child=True, use_pid=True) - logging.debug(f"{utcnow()} worker initialized {worker_id} with format {self.format_type}") self.reader = ReaderFactory.get_reader(type=self.format_type, dataset_type=self.dataset_type, thread_index=worker_id, epoch_number=self.epoch_number) + # Start initializing DYAD + # Create Dyad object to interact with DYAD's C internals self.dyad_io = Dyad() - is_local = os.getenv("DYAD_LOCAL_TEST", "0") == "1" - self.broker_per_node = int(os.getenv("BROKERS_PER_NODE", "1")) - + # Create a handle to Flux and get the rank of the broker that this process is running on self.f = flux.Flux() self.broker_rank = self.f.get_rank() - if is_local: - self.dyad_managed_directory = os.path.join(os.getenv("DYAD_PATH", ""), str(self.f.get_rank())) - else: - self.dyad_managed_directory = os.getenv("DYAD_PATH", "") - self.my_node_index = int(self.broker_rank*1.0 / self.broker_per_node) + # Obtain DYAD's managed directory from the DYAD_PATH environment variable + self.dyad_managed_directory = os.getenv("DYAD_PATH", "") + # Get the DTL mode (UCX or FLUX_RPC) from the DYAD_DTL_MODE environment variable dtl_str = os.getenv("DYAD_DTL_MODE", "FLUX_RPC") mode = DTLMode.DYAD_DTL_FLUX_RPC - namespace = os.getenv("DYAD_KVS_NAMESPACE") - logging.debug(f"{utcnow()} Rank {DLIOMPI.get_instance().rank()} init dyad {self.dyad_managed_directory} {dtl_str} {namespace}") if dtl_str == "UCX": mode = DTLMode.DYAD_DTL_UCX + # Initialize DYAD self.dyad_io.init(debug=self._args.debug, check=False, shared_storage=False, reinit=False, async_publish=True, fsync_write=False, key_depth=3, service_mux=self.broker_per_node, @@ -93,71 +84,49 @@ def worker_init(self, worker_id): prod_managed_path=self.dyad_managed_directory, cons_managed_path=self.dyad_managed_directory, dtl_mode=mode, dtl_comm_mode=DTLCommMode.DYAD_COMM_RECV) - def __del__(self): - if self.dlp_logger: - self.dlp_logger.finalize() - @dlp.log def __len__(self): return self.num_samples - @dlp.log def __getitem__(self, image_idx): - logging.debug(f"{utcnow()} Rank {DLIOMPI.get_instance().rank()} reading {image_idx} image") + # For the requested sample (indicated by image_idx), determine the file + # containing the sample and the index of the sample within that file self.num_images_read += 1 step = int(math.ceil(self.num_images_read / self.batch_size)) filename, sample_index = self._args.global_index_map[image_idx] is_present = False file_obj = None base_fname = filename - dlp.update(args={"fname":filename}) - dlp.update(args={"image_idx":image_idx}) + # Use DYAD's `get_metadata` function to check if the file has already been cached + # into DYAD if self.dyad_managed_directory != "": - logging.debug(f"{utcnow()} Rank {DLIOMPI.get_instance().rank()} reading metadata") base_fname = os.path.join(self.dyad_managed_directory, os.path.basename(filename)) file_obj = self.dyad_io.get_metadata(fname=base_fname, should_wait=False, raw=True) - logging.debug(f"Using managed directory {self.dyad_managed_directory} {base_fname} {file_obj}") is_present = True + # If the file has already been cached in DYAD, use `dyad_open` to open the file. + # Then, pass the Python File object returned from `dyad_open` to NumPy to read data. if file_obj: access_mode = "remote" file_node_index = int(file_obj.contents.owner_rank*1.0 / self.broker_per_node) - if self.my_node_index == file_node_index: - access_mode = "local" - dlp.update(args={"owner_rank":str(file_obj.contents.owner_rank)}) - dlp.update(args={"my_broker":str(self.broker_rank)}) - dlp.update(args={"mode":"dyad"}) - dlp.update(args={"access":access_mode}) - logging.info(f"{utcnow()} Rank {DLIOMPI.get_instance().rank()} reading {image_idx} sample from {access_mode} dyad {file_obj.contents.owner_rank}") - logging.debug(f"Reading from managed directory {base_fname}") with dyad_open(base_fname, "rb", dyad_ctx=self.dyad_io, metadata_wrapper=file_obj) as f: try: data = np.load(f, allow_pickle=True)["x"] except: - logging.info(f"{utcnow()} Rank {DLIOMPI.get_instance().rank()} got wierd {image_idx} sample from {access_mode} dyad {file_obj.contents.owner_rank}") data = self._args.resized_image - logging.info(f"{utcnow()} Rank {DLIOMPI.get_instance().rank()} read {image_idx} sample from {access_mode} dyad {file_obj.contents.owner_rank}") self.dyad_io.free_metadata(file_obj) + # If the file has not been cached in DYAD, first read the file using a DLIO reader. + # Then, write the data into the DYAD managed directory using `dyad_open`. else: - dlp.update(args={"mode":"pfs"}) - dlp.update(args={"access":"remote"}) - logging.info(f"{utcnow()} Rank {DLIOMPI.get_instance().rank()} reading {image_idx} sample from pfs {base_fname}") - logging.debug(f"Reading from pfs {base_fname}") data = self.reader.read_index(image_idx, step) if is_present: - logging.debug(f"Writing to managed_directory {base_fname}") with dyad_open(base_fname, "wb", dyad_ctx=self.dyad_io) as f: np.savez(f, x=data) - logging.debug(f"Read from pfs {base_fname}") - dlp.update(step=step) - dlp.update(image_size=data.nbytes) return data class DyadTorchDataLoader(BaseDataLoader): - @dlp.log_init def __init__(self, format_type, dataset_type, epoch_number): super().__init__(format_type, dataset_type, epoch_number, DataLoaderType.PYTORCH) - @dlp.log def read(self): do_shuffle = True if self._args.sample_shuffle != Shuffle.OFF else False dataset = DYADTorchDataset(self.format_type, self.dataset_type, self.epoch_number, self.num_samples, self._args.read_threads, self.batch_size) @@ -171,13 +140,9 @@ def read(self): prefetch_factor = self._args.prefetch_size if prefetch_factor > 0: if self._args.my_rank == 0: - logging.debug( - f"{utcnow()} Prefetch size is {self._args.prefetch_size}; prefetch factor of {prefetch_factor} will be set to Torch DataLoader.") else: prefetch_factor = 2 if self._args.my_rank == 0: - logging.debug( - f"{utcnow()} Prefetch size is 0; a default prefetch factor of 2 will be set to Torch DataLoader.") logging.debug(f"{utcnow()} Setup dataloader with {self._args.read_threads} workers {torch.__version__}") if self._args.read_threads==0: kwargs={} @@ -206,18 +171,14 @@ def read(self): drop_last=True, worker_init_fn=dataset.worker_init, **kwargs) # 2 is the default value - logging.debug(f"{utcnow()} Rank {self._args.my_rank} will read {len(self._dataset) * self.batch_size} files") # self._dataset.sampler.set_epoch(epoch_number) - @dlp.log def next(self): super().next() total = self._args.training_steps if self.dataset_type is DatasetType.TRAIN else self._args.eval_steps - logging.debug(f"{utcnow()} Rank {self._args.my_rank} should read {total} batches") for batch in self._dataset: yield batch - @dlp.log def finalize(self): - pass \ No newline at end of file + pass From a874218fdee8722f93e72ea537413797adf5315a Mon Sep 17 00:00:00 2001 From: Ian Lumsden Date: Thu, 11 Apr 2024 09:25:51 -0400 Subject: [PATCH 24/35] Adds reference to LC table and flux proxy optional section --- .../notebook/02_flux_scheduling.ipynb | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb index dc28110..d474836 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb @@ -72,7 +72,9 @@ " scancel\n", " flux cancel\n", " \n", - "" + "\n", + "\n", + "For a more comprehensive cross-reference between Slurm, Flux, and other schedulers, check out LLNL's [Batch System Cross-Reference Guides](https://hpc.llnl.gov/banks-jobs/running-jobs/batch-system-cross-reference-guides)." ] }, { @@ -159,6 +161,22 @@ "Code(filename='hello-batch.sh', language='bash')" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Optional: connecting to an existing Flux instance using flux proxy\n", + "\n", + "One cool feature that Flux provides is the ability to connect to an existing Flux instance/allocation from any other node of the system using `flux proxy`. To use this command, we first need to get the ID of the Flux instance we want to connect to. Assuming the interactive job we just launched is still running, we can get the job ID (which is the same as the instance ID) using `flux jobs`. Once we have that ID, we can run `flux proxy ` to connect to that Flux instance.\n", + "\n", + "Once we're connected to the interactive allocation, we can run the following job in one terminal:\n", + "```bash\n", + "$ flux run --nodes=1 sleep inf\n", + "```\n", + "\n", + "Then, in the other terminal, we should be able to see that `flux run` by running `flux jobs`." + ] + }, { "cell_type": "markdown", "metadata": {}, From 5428b9a8cbe29cd37d43b1625d68268f0e856930 Mon Sep 17 00:00:00 2001 From: Ian Lumsden Date: Thu, 11 Apr 2024 09:48:39 -0400 Subject: [PATCH 25/35] Adds a step to docker-builds to remove unneeded stuff from runner --- .github/workflows/docker-builds.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/docker-builds.yaml b/.github/workflows/docker-builds.yaml index 814d109..ed9d10a 100644 --- a/.github/workflows/docker-builds.yaml +++ b/.github/workflows/docker-builds.yaml @@ -21,6 +21,18 @@ jobs: steps: - name: Clone the code uses: actions/checkout@v3 + + # Note: only works on Ubuntu runner + - name: Remove unneeded stuff in runner to make space for Docker image + uses: jlumbroso/free-disk-space@v1.3.1 + with: + android: true + dotnet: true + haskell: true + large-packages: true # Removes stuff inspired by https://github.com/apache/flink/blob/02d30ace69dc18555a5085eccf70ee884e73a16e/tools/azure-pipelines/free_disk_space.sh + docker-images: false # Keep this in case we need any provided images + tool-cache: false # Keep tool-cache so we still have the pre-installed versions of e.g., Python + swamp-storage: true - name: GHCR Login if: (github.event_name != 'pull_request') From 58ed703858c19933b44d56bdc5c2d8d96f67eb5b Mon Sep 17 00:00:00 2001 From: Ian Lumsden Date: Thu, 11 Apr 2024 09:48:58 -0400 Subject: [PATCH 26/35] Small consistency tweaks --- .../tutorial/notebook/01_flux_tutorial.ipynb | 2 +- .../tutorial/notebook/02_flux_scheduling.ipynb | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/01_flux_tutorial.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/01_flux_tutorial.ipynb index 3bf1cd7..af489e0 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/01_flux_tutorial.ipynb +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/01_flux_tutorial.ipynb @@ -83,7 +83,7 @@ "source": [ "The output indicates the number of brokers started successfully.\n", "\n", - "# This concludes this notebook.\n", + "# This concludes Module 1.\n", "\n", "Next, we will see how to use Flux for both traditional batch scheduling and hierarchical scheduling. To continue, open [02_flux_scheduling.ipynb](./02_flux_scheduling.ipynb)." ] diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb index d474836..bf6fb18 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb @@ -560,14 +560,19 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# This concludes this notebook.\n", + "# This concludes Module 2.\n", "\n", - "In this notebook, we demonstrated how to:\n", + "In this module, we demonstrated how to:\n", "1. Use Flux for traditional batch scheduling similar to what is provided by other schedulers like Slurm\n", "2. Use Flux for hierarchical scheduling to achieve greater scheduling throughput\n", "\n", "To continue with the tutorial, open [03_flux_framework.ipynb](./03_flux_framework.ipynb)." ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] } ], "metadata": { From fc7e37bdea804744a65fa48dc317cf6bc5b98dc1 Mon Sep 17 00:00:00 2001 From: Ian Lumsden Date: Thu, 11 Apr 2024 13:56:02 -0400 Subject: [PATCH 27/35] Adds module 3 --- .github/workflows/docker-builds.yaml | 8 +- .../tutorial/notebook/01_flux_tutorial.ipynb | 4 +- .../notebook/02_flux_scheduling.ipynb | 4 +- .../tutorial/notebook/03_flux_framework.ipynb | 265 +++++++++++++++++- .../notebook/img/flux-broker-design.png | Bin 0 -> 35296 bytes .../notebook/img/flux-instance-pre-tbon.png | Bin 0 -> 12218 bytes .../notebook/img/flux-instance-w-tbon.png | Bin 0 -> 34570 bytes 7 files changed, 271 insertions(+), 10 deletions(-) create mode 100644 2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/img/flux-broker-design.png create mode 100644 2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/img/flux-instance-pre-tbon.png create mode 100644 2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/img/flux-instance-w-tbon.png diff --git a/.github/workflows/docker-builds.yaml b/.github/workflows/docker-builds.yaml index ed9d10a..77bc97f 100644 --- a/.github/workflows/docker-builds.yaml +++ b/.github/workflows/docker-builds.yaml @@ -26,13 +26,13 @@ jobs: - name: Remove unneeded stuff in runner to make space for Docker image uses: jlumbroso/free-disk-space@v1.3.1 with: + tool-cache: false android: true dotnet: true haskell: true - large-packages: true # Removes stuff inspired by https://github.com/apache/flink/blob/02d30ace69dc18555a5085eccf70ee884e73a16e/tools/azure-pipelines/free_disk_space.sh - docker-images: false # Keep this in case we need any provided images - tool-cache: false # Keep tool-cache so we still have the pre-installed versions of e.g., Python - swamp-storage: true + large-packages: true + docker-images: false + swap-storage: true - name: GHCR Login if: (github.event_name != 'pull_request') diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/01_flux_tutorial.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/01_flux_tutorial.ipynb index af489e0..21d1b60 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/01_flux_tutorial.ipynb +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/01_flux_tutorial.ipynb @@ -45,8 +45,8 @@ "\n", "This tutorial is split into several notebooks:\n", "* [Module 1: Getting started with Flux](./01_flux_tutorial.ipynb) (the rest of this notebook)\n", - "* [Module 2: Traditional and hierarchical schedulinng using Flux](./02_flux_scheduling.ipynb)\n", - "* [Module 3: TBA](./03_flux_framework.ipynb)\n", + "* [Module 2: Using Flux for traditional and hierarchical scheduling](./02_flux_scheduling.ipynb)\n", + "* [Module 3: Using Flux to manage and deploy distributed services](./03_flux_framework.ipynb)\n", "* [Module 4: Accelerating Distributed Deep Learning (DL) Training with DYAD](./04_dyad_dlio.ipynb)\n", "* [Module 5: Conclusions](./05_flux_tutorial_conclusions.ipynb)\n", "\n", diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb index bf6fb18..da04f8a 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb @@ -8,9 +8,9 @@ "
\n", "\n", "\n", - "# Module 2: Traditional and hierarchical schedulinng using Flux\n", + "# Module 2: Using Flux for traditional and hierarchical schedulinng\n", "\n", - "As mentioned in the intro video, Flux provides powerful and advanced scheduling capabilities that are important for Exascale systems like El Capitan. In this notebook, we will show how to:\n", + "As mentioned in the intro video, Flux provides powerful and advanced scheduling capabilities that are important for Exascale systems like El Capitan. In this module, we will show how to:\n", "1. Use Flux for traditional batch scheduling similar to what is provided by other schedulers like Slurm\n", "2. Use Flux for hierarchical scheduling to achieve greater scheduling throughput" ] diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/03_flux_framework.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/03_flux_framework.ipynb index 27724e0..819826e 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/03_flux_framework.ipynb +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/03_flux_framework.ipynb @@ -8,13 +8,274 @@ "
\n", "\n", "\n", - "# Module 3: TBA" + "# Module 3: Using Flux to manage and deploy distributed services\n", + "\n", + "Now that we've learned about hierarchical scheduling and its benefits, let's dive deeper into the structure of the individual Flux instances that comprise a hierarchy and how that structure enables the management and deployment of distributed services. In this module, we will cover:\n", + "1. The structure of Flux instances and how that structure enables distributed services (including traditional and hierarchical scheduling)\n", + "2. How to start and stop services in Flux\n", + "3. Two useful services for users of Flux (i.e., `flux kvs` and `flux archive`)\n", + "\n", + "## Structure of Flux instances\n", + "\n", + "As mentioned in Module 1, a Flux instance is comprised of one or more Flux brokers. A high-level depiction of the design of a Flux broker is shown in the figure below.\n", + "\n", + "
\n", + "\n", + "
\n", + "Image created by Ian Lumsden for this tutorial
\n", + "
\n", + "\n", + "Each broker is a program built on top of the βˆ…MQ networking library. The broker contains two main components. First, the broker implements Flux-specific networking abstractions over βˆ…MQ, such as remote-proceedure call (RPC) and publication-subscription (pub-sub). Second, the broker contains several core services, such as PMI (for MPI support), run control support (for enabling automatic startup of other services), and, most importantly, broker module management. The remainder of a Flux broker's functionality comes from broker modules: specially designed services that the broker can deploy in independent OS threads. Some examples of broker modules provided by Flux include:\n", + "* Job scheduling (both traditional and hierarchical)\n", + "* Fluxion (Flux's advanced graph-based scheduler)\n", + "* Banks and accounting (for system-wide deployments of Flux)\n", + "* PMIx (for OpenMPI)\n", + "* An in-memory content store (useful for preloading data into pods on cloud)\n", + "\n", + "When Flux starts, it launches one or more brokers across the resources it manages. By default, Flux will launch one broker per node, but this can be configured (e.g., with the `--test-size` flag to `flux start` shown in Module 1). After launching the brokers, Flux will designate one broker as the \"leader\" and the rest as \"followers\". The leader serves as entrypoint into the Flux instance, and it serves as the starting point for most Flux commands. The distribution of brokers and the \"leader-follower\" designations are shown in the following figure:\n", + "\n", + "
\n", + "\n", + "
\n", + "Image created by Vanessa Sochat for Flux Framework Components documentation
\n", + "
\n", + "\n", + "After launching the brokers and designating a leader, Flux uses the brokers' network abstractions to connect the brokers together into what we call the \"tree-based overlay network\", or TBON for short. This network is shown in the figure below. This overlay network connects brokers together in a pre-defined tree-based topology (e.g., *k*-ary and binomial). Whenever brokers or instances of distributed services running on top of the brokers need to communicate, they can send messages up and down this tree-structured network. This tree-structured network is used over alternative designs (e.g., all-to-all networks used by MPI) because it provides better scalability (by minimizing communication), security, and fault tolerance for a service-focused framework. More information about these benefits and Flux's overall design can be found in our [publications](https://flux-framework.org/publications/) and, particularly, our [2014 paper on Flux](https://ieeexplore.ieee.org/document/7103433) presented at ICPP.\n", + "\n", + "
\n", + "\n", + "
\n", + "Image created by Vanessa Sochat for Flux Framework Components documentation
\n", + "
\n", + "\n", + "As previously mentioned, services in Flux are implemented as broker modules. These broker modules can be deployed across one or more brokers. Once deployed, these services can leverage the other components of the broker, including message routing over the TBON and services provided by other broker modules. As a result, broker modules allow for the creation of composable, easily deployable services for Flux instances." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Starting and stopping Flux services\n", + "\n", + "To manage and query services, Flux provides the `flux module` command. The sub-commands provided by `flux module` can be seen by running the cell below." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!flux module --help" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "While going through Module 2, we've already encountered some built-in services provided by Flux, such as:\n", + "* `job-ingest` (used by Flux submission commands like `flux batch` and `flux run`)\n", + "* `job-list` (used by `flux jobs`)\n", + "* `sched-fluxion-qmanager` (used by `flux tree`)\n", + "* `sched-fluxion-resource` (also used by `flux tree`)\n", + "\n", + "We can see that these services are loaded and available by running the cell below." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!flux module list" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Users and system administrators can easily load and unload services using the `flux module load` and `flux module remove` commands. To show this, let's unload Fluxion (Flux's graph-based scheduler) and replace it with the built-in simple scheduler." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!flux module remove sched-fluxion-qmanager\n", + "!flux module remove sched-fluxion-resource\n", + "!flux module load sched-simple\n", + "!flux module list" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this code block, we unload the 2 services that comprise Fluxion: `sched-fluxion-qmanager` and `sched-fluxion-resource`. Next, we load the simple scheduler (`sched-simple`), and, finally, we look at the running servicees. We now see that Fluxion is not available, and the simple scheduler is.\n", + "\n", + "Next, let's reload Fluxion, but, this time, let's pass some extra arguments to specialize our Flux instance. In particular, we will limit the scheduling depth to 4 and populate Fluxion's resource graph with:\n", + "* Nodes\n", + "* Sockets\n", + "* Cores" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run flux dmesg to make sure sched-simple has no more work before unloading\n", + "!flux dmesg -C\n", + "!flux module remove sched-simple\n", + "!flux module load sched-fluxion-resource load-allowlist=node,socket,core\n", + "!flux module load sched-fluxion-qmanager queue-params=queue-depth=4\n", + "!flux module list" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Useful services in Flux\n", + "\n", + "Although there are plenty of interesting and useful services in Flux (e.g., scheduling), in this section, we will cover two services that can be useful in support of user applications:\n", + "1. `flux kvs`\n", + "2. `flux archive`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### flux kvs\n", + "\n", + "One of the core services built into Flux is the key-value store (KVS). It is used in many other services, including most of Flux's resource management services, the `flux archive` service below, and DYAD (which we will explore in Module 4). These services use the KVS to persistantly store information and retrieve it later (potentially after a restart of Flux).\n", + "\n", + "The `flux kvs` command provides a utility to list and manipulate values of the KVS. As a example of using `flux kvs`, let's use the command to examine information saved by the `resource` service." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!flux kvs ls\n", + "!flux kvs ls resource\n", + "!flux kvs get resource.R | jq" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The KVS is such an essential component of Flux that we provide C and Python APIs to interact with it. To learn more about interacting with the KVS from these languages, take a look at these documentation pages:\n", + "* C's `flux_kvs_commit` [family of functions](https://flux-framework.readthedocs.io/projects/flux-core/en/latest/man3/flux_kvs_commit.html)\n", + "* C's `flux_kvs_copy` [family of functions](https://flux-framework.readthedocs.io/projects/flux-core/en/latest/man3/flux_kvs_copy.html)\n", + "* C's `flux_kvs_getroot` [family of functions](https://flux-framework.readthedocs.io/projects/flux-core/en/latest/man3/flux_kvs_getroot.html)\n", + "* C's `flux_kvs_lookup` [family of functions](https://flux-framework.readthedocs.io/projects/flux-core/en/latest/man3/flux_kvs_lookup.html)\n", + "* C's `flux_kvs_namespace_create` [family of functions](https://flux-framework.readthedocs.io/projects/flux-core/en/latest/man3/flux_kvs_namespace_create.html)\n", + "* C's `flux_kvs_txn_create` [family of functions](https://flux-framework.readthedocs.io/projects/flux-core/en/latest/man3/flux_kvs_txn_create.html)\n", + "* Python's `flux.kvs` [module](https://flux-framework.readthedocs.io/projects/flux-core/en/latest/python/autogenerated/flux.kvs.html#module-flux.kvs)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### flux archive\n", + "\n", + "As Flux is used more in cloud environments, we might find themselves in a situation where we have a cluster without a shared filesystem. The `flux archive` command can help with this. At a high level, `flux archive` allows us to save named pieces of data (e.g., files) to the Flux key-value store for later retrieval.\n", + "\n", + "When using `flux archive`, we first have to create an named archive. In the code below, we will create a text file and then save it into an archive using `flux archive`. Note that, for larger files, you can speed up the creation and extraction of archives by using the `--mmap` flag." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!echo \"Sweet dreams 🌚️ are made of cheese, who am I to diss a brie? πŸ§€οΈ\" > shared-file.txt\n", + "!flux archive create --name myarchive --directory $(pwd) shared-file.txt" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When we run this code, we are creating an archive in the leader broker. Now that the archive is created, we will want to extract its contents onto the other nodes of our cluster. To do this, we first need to ensure that the directory that we will extract into exists on those nodes. This can be done using `flux exec`. The `flux exec` command will execute a command on the nodes associated with specified brokers. Let's use `flux exec` to run `mkdir` on all the nodes of our cluster except the leader broker's node." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!flux exec -r all -x 0 mkdir -p $(pwd)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The flags provided to `flux exec` do the following:\n", + "* `-r all`: run across all brokers in the Flux instance\n", + "* `-x 0`: don't runn on broker 0 (i.e., the leader broker)\n", + "\n", + "Now that the directory has been created on all our nodes, we can extract the archive onto those nodes by combining `flux exec` and `flux archive extract`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!flux exec -r all -x 0 flux archive extract --name myarchive --directory $(pwd) shared-file.txt" ] }, { "cell_type": "markdown", "metadata": {}, - "source": [] + "source": [ + "Finally, when we're done with the archive, we can remove it with `flux archive remove`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!flux archive remove --name myarchive" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Finally, note that `flux archive` was named `flux filemap` in earlier versions of Flux." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# This concludes Module 3.\n", + "\n", + "In this module, we covered:\n", + "1. The structure of Flux instances and how that structure enables distributed services (including traditional and hierarchical scheduling)\n", + "2. How to start and stop services in Flux\n", + "3. Two useful services for users of Flux (i.e., `flux kvs` and `flux archive`)\n", + "\n", + "To continue with the tutorial, open [04_dyad_dlio.ipynb](./04_dyad_dlio.ipynb)." + ] } ], "metadata": { diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/img/flux-broker-design.png b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/img/flux-broker-design.png new file mode 100644 index 0000000000000000000000000000000000000000..267f1a66fd2004f81e26478b2966673d6a03e46c GIT binary patch literal 35296 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 MkhEYqzpmf^0hw59ZvX%Q literal 0 HcmV?d00001 diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/img/flux-instance-pre-tbon.png b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/img/flux-instance-pre-tbon.png new file mode 100644 index 0000000000000000000000000000000000000000..bc40a7e464f98eebf5a420af2432e39e407bc8d0 GIT binary patch literal 12218 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 E4~16Vj{pDw literal 0 HcmV?d00001 diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/img/flux-instance-w-tbon.png b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/img/flux-instance-w-tbon.png new file mode 100644 index 0000000000000000000000000000000000000000..93a276e8c47a077536618abe0cc7194cb5247450 GIT binary patch literal 34570 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 Date: Thu, 11 Apr 2024 13:57:22 -0400 Subject: [PATCH 28/35] Small summary change --- .../JupyterNotebook/tutorial/notebook/01_flux_tutorial.ipynb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/01_flux_tutorial.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/01_flux_tutorial.ipynb index 21d1b60..d8b8411 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/01_flux_tutorial.ipynb +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/01_flux_tutorial.ipynb @@ -85,6 +85,8 @@ "\n", "# This concludes Module 1.\n", "\n", + "In this module, we introduced Flux and showed how to get started with Flux, particularly on systems using a different system-wide scheduler.\n", + "\n", "Next, we will see how to use Flux for both traditional batch scheduling and hierarchical scheduling. To continue, open [02_flux_scheduling.ipynb](./02_flux_scheduling.ipynb)." ] }, From 96897bb98f0eb28f353843e810f606075e390946 Mon Sep 17 00:00:00 2001 From: Ian Lumsden Date: Thu, 11 Apr 2024 21:21:46 -0400 Subject: [PATCH 29/35] Editing and revisions --- .../tutorial/notebook/01_flux_tutorial.ipynb | 12 ++--- .../notebook/02_flux_scheduling.ipynb | 48 ++++++++++--------- .../tutorial/notebook/03_flux_framework.ipynb | 45 ++++++++++------- .../05_flux_tutorial_conclusions.ipynb | 5 +- 4 files changed, 63 insertions(+), 47 deletions(-) diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/01_flux_tutorial.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/01_flux_tutorial.ipynb index d8b8411..c3ec3c9 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/01_flux_tutorial.ipynb +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/01_flux_tutorial.ipynb @@ -13,7 +13,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Welcome to the Flux Tutorial\n", + "# Introduction to Flux: Next-Generation Resource Management for Exascale Workflows and Job Scheduling\n", "\n", "## What is Flux Framework? πŸ€”οΈ\n", " \n", @@ -43,14 +43,14 @@ "source": [ "## I'm ready! How do I do this tutorial? 😁️\n", "\n", - "This tutorial is split into several notebooks:\n", + "This tutorial is split into 5 modules, each of which has a notebook:\n", "* [Module 1: Getting started with Flux](./01_flux_tutorial.ipynb) (the rest of this notebook)\n", "* [Module 2: Using Flux for traditional and hierarchical scheduling](./02_flux_scheduling.ipynb)\n", "* [Module 3: Using Flux to manage and deploy distributed services](./03_flux_framework.ipynb)\n", - "* [Module 4: Accelerating Distributed Deep Learning (DL) Training with DYAD](./04_dyad_dlio.ipynb)\n", - "* [Module 5: Conclusions](./05_flux_tutorial_conclusions.ipynb)\n", + "* [Module 4: Using DYAD to accelerate distributed Deep Learning (DL) training](./04_dyad_dlio.ipynb)\n", + "* [Module 5: Lessons learned, next steps, and discussion](./05_flux_tutorial_conclusions.ipynb)\n", "\n", - "To go through this tutorial, you need to go through these notebooks in the order above. To step through examples in each notebook you need to execute cells. To run a cell, press Shift+Enter on your keyboard. If you prefer, you can also paste the shell commands in the JupyterLab terminal and execute them there.\n", + "To go through this tutorial, you need to go through these modules in the order (1-5). To step through examples in each module's notebook, you need to execute cells. To run a cell, press `Shift+Enter` on your keyboard. If you prefer, you can also paste the shell commands in the JupyterLab terminal and execute them there.\n", "\n", "Let's get started!" ] @@ -87,7 +87,7 @@ "\n", "In this module, we introduced Flux and showed how to get started with Flux, particularly on systems using a different system-wide scheduler.\n", "\n", - "Next, we will see how to use Flux for both traditional batch scheduling and hierarchical scheduling. To continue, open [02_flux_scheduling.ipynb](./02_flux_scheduling.ipynb)." + "Next, we use Flux for both traditional batch scheduling and hierarchical scheduling. To continue, open [Module 2](./02_flux_scheduling.ipynb)." ] }, { diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb index da04f8a..02ee5ab 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/02_flux_scheduling.ipynb @@ -10,9 +10,9 @@ "\n", "# Module 2: Using Flux for traditional and hierarchical schedulinng\n", "\n", - "As mentioned in the intro video, Flux provides powerful and advanced scheduling capabilities that are important for Exascale systems like El Capitan. In this module, we will show how to:\n", - "1. Use Flux for traditional batch scheduling similar to what is provided by other schedulers like Slurm\n", - "2. Use Flux for hierarchical scheduling to achieve greater scheduling throughput" + "Flux provides powerful and advanced scheduling capabilities that are important for exascale systems like El Capitan. In this module, we demonstrate:\n", + "1. Traditional batch scheduling with Flux (similar to what is provided by other schedulers like Slurm)\n", + "2. Hierarchical scheduling with Flux to achieve higher throughput (novel capability of Flux)" ] }, { @@ -29,12 +29,12 @@ "Image created by Vanessa Sochat for Flux Framework Components documentation\n", "\n", "\n", - "To allow for this type of scheduling, schedulers like Slurm provide 3 main operations:\n", + "Traditional schedulers provide 3 main operations:\n", "1. Submitting jobs\n", "2. Running distributed applications within a job\n", "3. Querying the status of jobs or canceling running jobs\n", "\n", - "Although Flux's real benefits come from other types of scheduling (e.g., hierarchical and graph-based), Flux is still capable of handling these traditional scheduling operations. In this section, we will cover how to use Flux to perform these traditional batch scheduling operations. We will cover these operations in the order shown in the table below:\n", + "We use Flux to perform these traditional batch scheduling operations in the order shown in this table:\n", "\n", "\n", " \n", @@ -43,32 +43,32 @@ " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", @@ -167,6 +167,8 @@ "source": [ "#### Optional: connecting to an existing Flux instance using flux proxy\n", "\n", + "TODO check if this text or original example should be put in supplement\n", + "\n", "One cool feature that Flux provides is the ability to connect to an existing Flux instance/allocation from any other node of the system using `flux proxy`. To use this command, we first need to get the ID of the Flux instance we want to connect to. Assuming the interactive job we just launched is still running, we can get the job ID (which is the same as the instance ID) using `flux jobs`. Once we have that ID, we can run `flux proxy ` to connect to that Flux instance.\n", "\n", "Once we're connected to the interactive allocation, we can run the following job in one terminal:\n", @@ -181,7 +183,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Runing distributed applications with waiting for completion\n", + "### Running distributed applications with waiting for completion\n", "\n", "Similar to Slurm's `srun`, users can run distributed (e.g., MPI) applications and wait for completion using `flux run`. To see how `flux run` works, let's run the following command." ] @@ -419,7 +421,7 @@ "source": [ "## Hierarchical scheduling with Flux\n", "\n", - "With traditional batch schedulers (e.g., Slurm), all job requests from all users are submitted to one centralized service. Note that our maximum job throughput is one job per second.\n", + "With traditional batch schedulers (e.g., Slurm), all job requests from all users are submitted to one centralized service. In this case, the maximum job throughput is one job per second.\n", "\n", "
\n", "\n", @@ -435,7 +437,7 @@ "Image created by Vanessa Sochat for Flux Framework Components documentation\n", "
\n", "\n", - "By leveraging a hierarchy of Flux instances to achieve a divide-and-conquer approach to scheduling, we can exponentially increase throughput. The figure below (from our [learning guide](https://flux-framework.readthedocs.io/en/latest/guides/learning_guide.html#fully-hierarchical-resource-management-techniques)) shows this exponential increase in an actual experiment. We were able to submit 500 jobs/second using only a three-level hierarchy, whereas a centralized scheduler (1-Level in the figure) was only able to achieve one 1 job/second.\n", + "By leveraging a hierarchy of Flux instances to achieve a divide-and-conquer approach to scheduling, we can exponentially increase throughput. The figure below (from our [learning guide](https://flux-framework.readthedocs.io/en/latest/guides/learning_guide.html#fully-hierarchical-resource-management-techniques)) shows this exponential increase in an actual experiment. We submit 500 jobs/second using only a three-level hierarchy, whereas a centralized scheduler (1-Level in the figure) achieves only one 1 job/second.\n", "\n", "
\n", "\n", @@ -443,18 +445,20 @@ "Image from Flux learning guide\n", "
\n", "\n", - "There are several ways to create hierarchies of Flux instances. In this tutorial, we will focus on 2 of them:\n", - "1. Through nested invocations of `flux batch`\n", - "2. Through the prototype `flux tree` command" + "There are different ways to create hierarchies of Flux instances. In this tutorial, we will focus on 2 of them:\n", + "1. Nested invocations of `flux batch`\n", + "2. The `flux tree` command" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Hierarchical scheduling with flux batch\n", + "### Nested invocations of flux batch\n", + "\n", + "As mentioned in the [Traditional batch scheduling with Flux]() section, `flux batch` is the command used to submit non-interactive, batch script-based jobs to Flux.\n", "\n", - "As mentioned above, `flux batch` is the command used to submit non-interactive, batch script-based jobs to Flux. When a job submitted with `flux batch` starts running, Flux will create a new Flux instance over the resources reserved for that job. In other words, before even getting to the script that the user provides, `flux batch` creates a new child in the hierarchy of Flux instances. Since a Flux instance has the same capabilities no matter where it lies in the hierarchy, this newly created instance can schedule its resources in the same way that a system-wide Flux instance can. As a result, the newly created Flux instance can be used to perform additional `flux batch` commands over its subset of the resources.\n", + "The `flux batch` command can be invoked in a nested fashion within a batch script run by another `flux batch` command. When a job submitted with `flux batch` starts running, Flux creates a new Flux instance over the resources reserved for that job. In other words, before starting the script that the user provides, `flux batch` creates a new child in the hierarchy of Flux instances. Since a Flux instance has the same capabilities no matter where it lies in the hierarchy, this newly created instance can schedule its resources in the same way that a system-wide Flux instance can. As a result, the newly created Flux instance can be used to perform additional `flux batch` commands over its subset of the resources.\n", "\n", "To show this in action, let's look at `sub_job1.sh` and `sub_job2.sh`." ] @@ -501,7 +505,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Now that we've submitted `sub_job1.sh`, we can look at the hierarchy for all the jobs we've run using `flux pstree`. Normally, this command can be used to show jobs in a Flux instance. However, since we are running in a Jupyter notebook, this command will have limited functionality. So, instead of just running the single command, we will run `flux pstree -a` to look at **all** jobs. In a more complex environment with more jobs, this command would show a deeper nesting. You can see examples of more complex outputs [here](https://flux-framework.readthedocs.io/en/latest/jobs/hierarchies.html?h=pstree#flux-pstree-command)." + "Once we have submitted `sub_job1.sh`, we can look at the hierarchy for all the jobs we've run using `flux pstree`. Normally, this command can be used to show jobs in a Flux instance. However, since we are running in a Jupyter notebook, this command will have limited functionality. So, instead of just running the single command, we will run `flux pstree -a` to look at **all** jobs. In a more complex environment with more jobs, this command would show a deeper nesting. You can see examples of more complex outputs [here](https://flux-framework.readthedocs.io/en/latest/jobs/hierarchies.html?h=pstree#flux-pstree-command)." ] }, { @@ -517,7 +521,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Hierarchical scheduling with flux tree\n", + "### The flux tree command\n", "\n", "`flux tree` is a prototype tool that allows you to easily create a hierarchy of Flux instances and submit work to different levels it. Alternatively, it can be thought of as a way to create a nested hierarchy of jobs that scale out.\n", "\n", @@ -566,7 +570,7 @@ "1. Use Flux for traditional batch scheduling similar to what is provided by other schedulers like Slurm\n", "2. Use Flux for hierarchical scheduling to achieve greater scheduling throughput\n", "\n", - "To continue with the tutorial, open [03_flux_framework.ipynb](./03_flux_framework.ipynb)." + "To continue with the tutorial, open [Module 3](./03_flux_framework.ipynb)." ] }, { diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/03_flux_framework.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/03_flux_framework.ipynb index 819826e..c7d9f45 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/03_flux_framework.ipynb +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/03_flux_framework.ipynb @@ -10,14 +10,14 @@ "\n", "# Module 3: Using Flux to manage and deploy distributed services\n", "\n", - "Now that we've learned about hierarchical scheduling and its benefits, let's dive deeper into the structure of the individual Flux instances that comprise a hierarchy and how that structure enables the management and deployment of distributed services. In this module, we will cover:\n", - "1. The structure of Flux instances and how that structure enables distributed services (including traditional and hierarchical scheduling)\n", - "2. How to start and stop services in Flux\n", - "3. Two useful services for users of Flux (i.e., `flux kvs` and `flux archive`)\n", + "Now that we have learned about hierarchical scheduling and its benefits, let's dive deeper into the structure of the individual Flux instances that comprise a hierarchy and examine how that structure enables the management and deployment of distributed services. In this module, we cover:\n", + "1. The structure of Flux instances\n", + "2. Management of Flux services\n", + "3. Examples of services in Flux (`flux kvs` and `flux archive`)\n", "\n", - "## Structure of Flux instances\n", + "## The structure of Flux instances\n", "\n", - "As mentioned in Module 1, a Flux instance is comprised of one or more Flux brokers. A high-level depiction of the design of a Flux broker is shown in the figure below.\n", + "As mentioned in [Module 1](./01_flux_tutorial.ipynb), a Flux instance is comprised of one or more Flux brokers. A high-level depiction of the design of a Flux broker is shown in the figure below.\n", "\n", "
\n", "\n", @@ -26,13 +26,13 @@ "
\n", "\n", "Each broker is a program built on top of the βˆ…MQ networking library. The broker contains two main components. First, the broker implements Flux-specific networking abstractions over βˆ…MQ, such as remote-proceedure call (RPC) and publication-subscription (pub-sub). Second, the broker contains several core services, such as PMI (for MPI support), run control support (for enabling automatic startup of other services), and, most importantly, broker module management. The remainder of a Flux broker's functionality comes from broker modules: specially designed services that the broker can deploy in independent OS threads. Some examples of broker modules provided by Flux include:\n", - "* Job scheduling (both traditional and hierarchical)\n", + "* Job scheduling (both [traditional and hierarchical](./02_flux_scheduling.ipynb))\n", "* Fluxion (Flux's advanced graph-based scheduler)\n", "* Banks and accounting (for system-wide deployments of Flux)\n", "* PMIx (for OpenMPI)\n", "* An in-memory content store (useful for preloading data into pods on cloud)\n", "\n", - "When Flux starts, it launches one or more brokers across the resources it manages. By default, Flux will launch one broker per node, but this can be configured (e.g., with the `--test-size` flag to `flux start` shown in Module 1). After launching the brokers, Flux will designate one broker as the \"leader\" and the rest as \"followers\". The leader serves as entrypoint into the Flux instance, and it serves as the starting point for most Flux commands. The distribution of brokers and the \"leader-follower\" designations are shown in the following figure:\n", + "When Flux starts, it launches one or more brokers across the resources it manages. By default, Flux will launch one broker per node, but this can be configured (e.g., with the `--test-size` flag to `flux start` shown in [Module 1](./01_flux_tutorial.ipynb)). After launching the brokers, Flux will designate one broker as the \"leader\" and the rest as \"followers\". The leader serves as entrypoint into the Flux instance, and it serves as the starting point for most Flux commands. The distribution of brokers and the \"leader-follower\" designations are shown in the following figure:\n", "\n", "
\n", "\n", @@ -40,7 +40,7 @@ "Image created by Vanessa Sochat for Flux Framework Components documentation\n", "
\n", "\n", - "After launching the brokers and designating a leader, Flux uses the brokers' network abstractions to connect the brokers together into what we call the \"tree-based overlay network\", or TBON for short. This network is shown in the figure below. This overlay network connects brokers together in a pre-defined tree-based topology (e.g., *k*-ary and binomial). Whenever brokers or instances of distributed services running on top of the brokers need to communicate, they can send messages up and down this tree-structured network. This tree-structured network is used over alternative designs (e.g., all-to-all networks used by MPI) because it provides better scalability (by minimizing communication), security, and fault tolerance for a service-focused framework. More information about these benefits and Flux's overall design can be found in our [publications](https://flux-framework.org/publications/) and, particularly, our [2014 paper on Flux](https://ieeexplore.ieee.org/document/7103433) presented at ICPP.\n", + "After launching the brokers and designating a leader, Flux uses the brokers' network abstractions to connect the brokers together into what we call the \"tree-based overlay network\", or TBON for short. This network is shown in the figure below. This overlay network connects brokers together in a pre-defined tree-based topology (e.g., *k*-ary and binomial). Whenever brokers or instances of distributed services running on top of the brokers need to communicate, they can send messages up and down this tree-structured network. This tree-structured network is used over alternative designs (e.g., all-to-all networks used by MPI) because it provides better scalability (by minimizing communication), security, and fault tolerance for a service-focused framework. More information about these benefits and Flux's overall design can be found in our [publications](https://flux-framework.org/publications/) (particularly our [2014 paper on Flux](https://ieeexplore.ieee.org/document/7103433) presented at ICPP).\n", "\n", "
\n", "\n", @@ -48,14 +48,16 @@ "Image created by Vanessa Sochat for Flux Framework Components documentation\n", "
\n", "\n", - "As previously mentioned, services in Flux are implemented as broker modules. These broker modules can be deployed across one or more brokers. Once deployed, these services can leverage the other components of the broker, including message routing over the TBON and services provided by other broker modules. As a result, broker modules allow for the creation of composable, easily deployable services for Flux instances." + "### How Flux instances support services\n", + "\n", + "Services in Flux are implemented as broker modules that can be deployed across one or more brokers. Once deployed, these services can leverage the other components of the broker, including message routing over the TBON and services provided by other broker modules. As a result, broker modules allow for the creation of composable, easily deployable services for Flux instances." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Starting and stopping Flux services\n", + "## Management of Flux services\n", "\n", "To manage and query services, Flux provides the `flux module` command. The sub-commands provided by `flux module` can be seen by running the cell below." ] @@ -73,7 +75,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "While going through Module 2, we've already encountered some built-in services provided by Flux, such as:\n", + "While going through [Module 2](./02_flux_scheduling.ipynb), we've already encountered some built-in services provided by Flux, such as:\n", "* `job-ingest` (used by Flux submission commands like `flux batch` and `flux run`)\n", "* `job-list` (used by `flux jobs`)\n", "* `sched-fluxion-qmanager` (used by `flux tree`)\n", @@ -140,9 +142,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Useful services in Flux\n", + "## Examples of services in Flux\n", "\n", - "Although there are plenty of interesting and useful services in Flux (e.g., scheduling), in this section, we will cover two services that can be useful in support of user applications:\n", + "In this section, we will cover two services that expand Flux's usefulness to diverse applications:\n", "1. `flux kvs`\n", "2. `flux archive`" ] @@ -153,7 +155,7 @@ "source": [ "### flux kvs\n", "\n", - "One of the core services built into Flux is the key-value store (KVS). It is used in many other services, including most of Flux's resource management services, the `flux archive` service below, and DYAD (which we will explore in Module 4). These services use the KVS to persistantly store information and retrieve it later (potentially after a restart of Flux).\n", + "One of the core services built into Flux is the key-value store (KVS). It is used in many other services, including most of Flux's resource management services, the `flux archive` service below, and DYAD (which we will explore in [Module 4](./04_dyad_dlio.ipynb)). These services use the KVS to persistantly store information and retrieve it later (potentially after a restart of Flux).\n", "\n", "The `flux kvs` command provides a utility to list and manipulate values of the KVS. As a example of using `flux kvs`, let's use the command to examine information saved by the `resource` service." ] @@ -189,7 +191,7 @@ "source": [ "### flux archive\n", "\n", - "As Flux is used more in cloud environments, we might find themselves in a situation where we have a cluster without a shared filesystem. The `flux archive` command can help with this. At a high level, `flux archive` allows us to save named pieces of data (e.g., files) to the Flux key-value store for later retrieval.\n", + "As Flux is used more in cloud environments, we might find ourselves in a situation where we have a cluster without a shared filesystem. The `flux archive` command helps with this situation. At a high level, `flux archive` allows us to save named pieces of data (e.g., files) to the Flux KVS for later retrieval.\n", "\n", "When using `flux archive`, we first have to create an named archive. In the code below, we will create a text file and then save it into an archive using `flux archive`. Note that, for larger files, you can speed up the creation and extraction of archives by using the `--mmap` flag." ] @@ -260,7 +262,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Finally, note that `flux archive` was named `flux filemap` in earlier versions of Flux." + "Finally, note that `flux archive` was named `flux filemap` in earlier versions of Flux.\n", + "\n", + "`flux kvs` and `flux archive` are two useful, but simple exammples of Flux services. Flux also supports more complex services, including services for runtime data movement, such as DYAD (covered in [Module 4](./04_dyad_dlio.ipynb))." ] }, { @@ -274,8 +278,13 @@ "2. How to start and stop services in Flux\n", "3. Two useful services for users of Flux (i.e., `flux kvs` and `flux archive`)\n", "\n", - "To continue with the tutorial, open [04_dyad_dlio.ipynb](./04_dyad_dlio.ipynb)." + "To continue with the tutorial, open [Module 4](./04_dyad_dlio.ipynb)." ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] } ], "metadata": { diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/05_flux_tutorial_conclusions.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/05_flux_tutorial_conclusions.ipynb index 5c379a2..dee077c 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/05_flux_tutorial_conclusions.ipynb +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/05_flux_tutorial_conclusions.ipynb @@ -8,9 +8,12 @@ "
\n", "\n", "\n", - "# Moudle 5: Conclusions\n", + "# Module 5: Lessons learned, next steps, and discussion\n", "# This concludes the Flux tutorial! πŸ˜„οΈ\n", "\n", + "In this tutorial, we have [verb]:\n", + "\n", + "\n", "Don't worry, you'll have more opportunities for using Flux! We hope you reach out to us on any of our [project repositories](https://flux-framework.org) and ask any questions that you have. We'd love your contribution to code, documentation, or just saying hello! πŸ‘‹οΈ If you have feedback on the tutorial, please let us know so we can improve it for next year. \n", "\n", "## What do I do now?\n", From 0d0173338ee650d8e055f57158e5fd0dd70982b0 Mon Sep 17 00:00:00 2001 From: Ian Lumsden Date: Thu, 11 Apr 2024 23:08:49 -0400 Subject: [PATCH 30/35] Finishes the DYAD notebook --- .../tutorial/notebook/04_dyad_dlio.ipynb | 156 ++++++++++++------ .../05_flux_tutorial_conclusions.ipynb | 7 + .../tutorial/notebook/img/dl-training-io.png | Bin 0 -> 36427 bytes .../notebook/img/dyad-software-stack.png | Bin 0 -> 89601 bytes .../notebook/img/dyad-unet3d-results.svg | 149 +++++++++++++++++ 5 files changed, 257 insertions(+), 55 deletions(-) create mode 100644 2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/img/dl-training-io.png create mode 100644 2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/img/dyad-software-stack.png create mode 100644 2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/img/dyad-unet3d-results.svg diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/04_dyad_dlio.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/04_dyad_dlio.ipynb index 07004da..9c473b2 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/04_dyad_dlio.ipynb +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/04_dyad_dlio.ipynb @@ -7,43 +7,72 @@ "tags": [] }, "source": [ - "# Welcome to the DYAD component of the Flux tutorial\n" - ] - }, - { - "cell_type": "markdown", - "id": "4db0555f", - "metadata": {}, - "source": [ - "> What is DYAD? πŸ€”οΈ\n", + "# Module 4: Using DYAD to accelerate distributed Deep Learning (DL) training\n", + "\n", + "Now that we have seen how Flux enables the management and deployment of services, let's look at an example of using DYAD, an advanced Flux service for runtime data movement, in a real world application. Specifically, we will show how DYAD speeds up distributed Deep Learning (DL) training. In this module, we cover these topics:\n", + "1. Design of DYAD\n", + "2. Distributed DL training\n", + "3. Deep Learning I/O (DLIO) benchmark\n", + "4. Accelerating distributed DL training\n", "\n", - "DYAD is a transparent, locality-aware, write-once, read-many file cache that runs on top of local NVMe and other burst buffer-style technologies (e.g., El Capitan Rabbit nodes). It is designed to accelerate large, distributed workloads, such as distributed Deep Learning (DL) training and scientific computing workflows, on HPC systems. It is also designed be transparent, which allows users to leverage DYAD with little to no code refactoring. Unlike similar tools (e.g., DataSpaces and UnifyFS), which tend to optimize for write performance, DYAD aims to provide good write **and read** performance. To optimize read performance, DYAD uses a locality-aware \"Hierarchical Data Locator,\" which prioritizes node-local metadata and data retrieval to minimize the amount of network communications. When moving data from another node, DYAD also uses a streaming RPC over RDMA protocol, which uses preallocated buffers and connection caching to maximize network bandwidth. This process is shown in the figure below:\n", + "## Design of DYAD\n", "\n", - "![DYAD Reading Process](img/dyad_design.png)\n", + "DYAD provides transparent, locality-aware, write-once, read-many file caching that runs on top of local NVMe and other burst buffer-style technologies (e.g., El Capitan Rabbit nodes). Figure X shows the components of DYAD, including the DYAD service (implemented as a Flux broker module), the DYAD client, and DYAD's data transport layer. DYAD uses the Flux KVS to store metadata about tracked files, and it uses Flux's remote proceedure call capabilities to communicate between client and service. DYAD also uses UCX to perform RDMA-based data transfer to move files.\n", "\n", - "DYAD uses several services provided by Flux (key-value store, remote proceedure call, broker modules) to orchestrate data movement between nodes. It also uses UCX to move data." + "
\n", + "\n", + "
\n", + "Image created by Ian Lumsden for a poster at SC'23
\n", + "
\n", + "\n", + "DYAD is designed to accelerate large, distributed workloads, such as distributed Deep Learning (DL) training and scientific computing workflows, on HPC systems. It is also designed be transparent, which allows users to leverage DYAD with little to no code refactoring. Unlike similar tools (e.g., DataSpaces and UnifyFS), which tend to optimize for write performance, DYAD aims to provide good write **and read** performance. To optimize read performance, DYAD uses a locality-aware \"Hierarchical Data Locator,\" which prioritizes node-local metadata and data retrieval to minimize the amount of network communications. When moving data from another node, DYAD also uses a streaming RPC over RDMA protocol, which uses preallocated buffers and connection caching to maximize network bandwidth. This process is shown in the figure below:\n", + "\n", + "
\n", + "\n", + "
\n", + "Image created by Hari Devarajan for a paper submitted to SC'24
\n", + "
" ] }, { "cell_type": "markdown", - "id": "badb9753", + "id": "d32e7976", "metadata": {}, "source": [ - "> I'm ready! How do I do this tutorial? 😁️\n", + "## Distributed DL Training\n", + "\n", + "Distributed DL training is an approach to speed up the training of large Deep Learning models by performing multiple epochs of training in parallel across multiple GPUs and, oftentimes, multiple nodes. This approach is supported by most major DL libraries, such as PyTorch and Tensorflow. In this module, we focus on PyTorch. When running training across multiple nodes and GPUs, PyTorch starts by spawning one process per GPU, called the worker. Each worker performs three major tasks:\n", + "1. Determining which samples from the dataset will comprise the batch for the next epoch of training (i.e., epoch *N+1*)\n", + "2. Reading these samples from the filesystem\n", + "3. Building a batch from these samples and moving the batch to the GPU\n", + "\n", + "To assist with reading the samples from the filesystem, each worker also spawns additional I/O processes. Each of these processes reads data and, optionally, transforms the data based on the configuration of the training pipeline. Figure X shows this process for a single GPU, a single worker, and a single spawned I/O process. In this figure, \"I/O\" indicates data being read from the filesystem, and \"Map\" indicates the optional transformation of data. \"Batch\" indicates the building of a batch from the read samples.\n", "\n", - "The process for running this tutorial is the same as `flux.ipynb`. To step through examples in this notebook \n", - "you need to execute cells. To run a cell, press Shift+Enter on your keyboard. If you prefer, you can also paste \n", - "the shell commands in the JupyterLab terminal and execute them there." + "
\n", + "\n", + "
\n", + "Image created by Ian Lumsden based on an image from this article
\n", + "
\n", + "\n", + "One key difference between distributed DL training and many conventional HPC applications (e.g., MPI-based simulations) is the asynchronous loading of data by workers during training. In many conventional HPC applications, data loading and computation are performed one after the one. On the other hand, as shown in Figure X, the loading of data in distributed DL training is asynchronous. In other words, while the GPU is training the DL model for epoch *N*, the worker reading and creating the batch for epoch *N+1*. This asynchronous loading of data can lead to imbalance between data loading and training. For example, Figure X shows a scenario where the data loading takes longer than training, resulting in idle time on the GPU, wasted resources, and, overall, an I/O bound application.\n", + "\n", + "At the end of each epoch of training, all workers and GPUs are synchronized so that the DL models from each GPU can be merged together. This synchronization and merging usually consists of an allreduce-style operation. This synchronization makes the effects of any imbalance between data loading and training more pronounced because, if even one worker and GPU become imbalanced, the performance of the entire distributed training will suffer." ] }, { "cell_type": "markdown", - "id": "c0a9e0f9", + "id": "bf6493c2", "metadata": {}, "source": [ - "# Accelerating Distributed Deep Learning (DL) Training with DYAD\n", + "## Deep Learning I/O Benchmark\n", + "\n", + "Due to limited resources and due to this module being about a data movement service (DYAD), we do not need to actually train a DL model in this tutorial. Instead, we accurately show DYAD's benefit to DL training without performing the training itself by using the Argonne National Laboratory's [Deep Learning I/O benchmark](https://github.com/argonne-lcf/dlio_benchmark), or DLIO for short.\n", "\n", - "Description of distributed DL (from DLIO paper)" + "DLIO is a benchmark that aims to emulate the I/O behavior of Deep Learning applications. It has an extensible and modular design that allows it to use or mimic aspects (e.g., data formats, worker configuration, data loading-training balanced) of real-world applications. DLIO also has several useful support features, such as the ability to generate data with certain characteristics for users.\n", + "\n", + "To learn more about DLIO, check out the following links:\n", + "* [DLIO Paper](https://ieeexplore.ieee.org/document/9499416)\n", + "* [DLIO Repo](https://github.com/argonne-lcf/dlio_benchmark)" ] }, { @@ -51,11 +80,15 @@ "id": "be8da082", "metadata": {}, "source": [ - "## Leveraging DYAD in PyTorch\n", + "## Accelerating DL training\n", + "\n", + "As mentioned in the [Design of DYAD](#design-of-dyad) section, DYAD provides write-once, read-many file caching. This feature is extremely useful in read-heavy workloads, like distributed DL training.\n", "\n", - "When using custom datasets or custom techniques/tools to read a dataset from storage, PyTorch requires the creation of `Dataset` and `DataLoader` classes. To use DYAD in PyTorch-based distributed DL training, we have implemented several of these classes. They can all be found in the [dlio_extensions](../dlio_extensions) directory.\n", + "In this section, we show DYAD's benefits to DL training using DLIO. More specifically, we first show an integration of DYAD into PyTorch through custom `Dataset` and `DataLoader` classes. Then, we run DYAD through a configuration of DLIO that mimics the training of a 3D U-Net model. Due to resource restrictions, we only run a small version of the 3D U-Net training pipeline. Finally, we show the I/O performance of DYAD compared against Lustre and [UnifyFS](https://ieeexplore.ieee.org/document/10177390) in training a full version of the 3D U-Net model at various scales on LLNL's [Corona](https://hpc.llnl.gov/hardware/compute-platforms/corona) supercomputer.\n", "\n", - "The specific classes used in this tutorial are `DYADTorchDataset` and `DyadTorchDataLoader`, which can both be found [here](../dlio_extensions/dyad_torch_data_loader.py). The `DYADTorchDataset` class contains all the DYAD-specific code. The `DyadTorchDataLoader` class is a basic `DataLoader` designed to read individual samples from the `DYADTorchDataset` class.\n", + "### Integrating DYAD into PyTorch\n", + "\n", + "When using custom datasets or custom techniques/tools to read a dataset from storage, PyTorch requires the creation of `Dataset` and `DataLoader` classes. To use DYAD in PyTorch-based distributed DL training, we have implemented several of the `DYADTorchDataset` and `DyadTorchDataLoader` classes, which can both be found [here](../dlio_extensions/dyad_torch_data_loader.py). The `DYADTorchDataset` class is used to read samples from \"remote\" storage (if not previously read) or DYAD (if previously read), and it contains all the DYAD-specific code. The `DyadTorchDataLoader` class is a basic `DataLoader` which configures the \"I/O\" and \"Map\" steps of the data loading pipeline.\n", "\n", "In the following code cells, we show the DYAD-specific code in `DYADTorchDataset`. As you will see, this code is very similar to standard Python file I/O. As a result, this code serves as an example of DYAD's transparency.\n", "\n", @@ -89,7 +122,7 @@ "id": "8007ad75", "metadata": {}, "source": [ - "This first block of code shows the `DYADTorchDataset.worker_init` function. This function is called to initialize the I/O processes that are spawned by the `DyadTorchDataLoader` class. As a result, this function contains two parts: (1) the initialization of PyTorch internals and utilities (e.g., a logger) and (2) the initialization of DYAD.\n", + "This first block of code shows the `DYADTorchDataset.worker_init` function. This function is called to initialize the I/O processes used to read samples. As a result, this function contains two parts: (1) the initialization of PyTorch internals and utilities and (2) the initialization of DYAD.\n", "\n", "Normally, DYAD is configured using environment variables, and, as a result, DYAD's initialization can be hidden from users. However, due to PyTorch's complexity and challenges in correctly propagating environment variables through PyTorch's dynamic process spawning, DYAD's transparent, environment variable-based initialization cannot be used in `DYADTorchDataset`. Instead, we manually initialize and configure DYAD using `Dyad.init()`." ] @@ -127,9 +160,9 @@ "id": "fefd9ae3", "metadata": {}, "source": [ - "## Configure DLIO and DYAD\n", + "### Running DLIO with DYAD for a 3D U-Net model\n", "\n", - "Now that we've seen how DYAD is integrated into PyTorch, we will start configuring DYAD and DLIO." + "Now that we have seen how DYAD is integrated into PyTorch, we configure and run DYAD through a configuration of DLIO that mimics the training of a 3D U-Net model." ] }, { @@ -137,7 +170,9 @@ "id": "731d52a3", "metadata": {}, "source": [ - "First, we will configure DYAD. DYAD requires three settings for configuration:\n", + "#### Configuring DLIO and DYAD\n", + "\n", + "First, we configure DYAD. DYAD requires three settings for configuration:\n", "1. A namespace in the Flux key-value store, which DYAD will use for metadata management\n", "2. A \"managed directory,\" which DYAD will use to determine the files that should be tracked\n", "3. A data transport layer (DTL) mode, which DYAD will use to select the underlying networking library for data transfer " @@ -160,7 +195,7 @@ "id": "6e32bc27", "metadata": {}, "source": [ - "Next, we will configure DLIO. DLIO requires several configuration settings. However, for this tutorial, the only one that should be set is the initial data directory, or the directory where the dataset initially resides at the start of training. When running DLIO, the `DYADTorchDataset` class will dynamically copy files from this directory into DYAD's managed directory." + "Next, we configure DLIO. DLIO requires several configuration settings. However, for this tutorial, the only one that should be set is the initial data directory, or the directory where the dataset initially resides at the start of training. When running DLIO, the `DYADTorchDataset` class dynamically copies files from this directory into DYAD's managed directory." ] }, { @@ -178,7 +213,7 @@ "id": "d979369c", "metadata": {}, "source": [ - "Finally, we will set the remaining configurations for DLIO. These should not be edited because they depend on the directory structure and configuration of this tutorial." + "Finally, we set the remaining configurations for DLIO. These should not be edited because they depend on the directory structure and configuration of this tutorial." ] }, { @@ -200,7 +235,7 @@ "id": "801719eb", "metadata": {}, "source": [ - "To properly set the environment variables needed for running DLIO with DYAD, we will create an environment file that is compatible with the `--env-file` flag of `flux submit`." + "To properly set the environment variables needed for running DLIO with DYAD, we create an environment file that is compatible with the `--env-file` flag of `flux submit`." ] }, { @@ -228,9 +263,9 @@ "id": "398e110f", "metadata": {}, "source": [ - "## Create Flux KVS Namespace and start DYAD service\n", + "#### Creating a Flux KVS namespace and starting the DYAD service\n", "\n", - "Next, we will start the DYAD service. This involves two steps. First, we need to create a namespace withing the Flux key-value store. This namespace will be used by DYAD to store metadata about cached files. This metadata is then used by DYAD's Hierarchical Data Locator to locate files." + "Next, we start the DYAD service. This involves two steps. First, we need to create a namespace withing the Flux key-value store. This namespace is used by DYAD to store metadata about cached files. This metadata is then used by DYAD's Hierarchical Data Locator to locate files." ] }, { @@ -248,7 +283,7 @@ "id": "723cbeaf", "metadata": {}, "source": [ - "After creating the key-value store namespace, we will start the DYAD service. The DYAD service is implemented as a Flux broker module. This allows us to use Flux for service deployment and application-to-service communication. To start the DYAD service, we use the `flux module load` command. We run that command through `flux exec -r all` to deploy the service across all Flux brokers." + "After creating the key-value store namespace, we start the DYAD service itself using the `flux module load` command. We run that command through `flux exec -r all` to deploy the service across all Flux brokers." ] }, { @@ -266,7 +301,7 @@ "id": "f95e0145", "metadata": {}, "source": [ - "Finally, we can check that the service and key-value store namespace were successfully created with the cells below." + "Finally, we check that the service and key-value store namespace were successfully created with the cells below." ] }, { @@ -294,9 +329,9 @@ "id": "c0dfe655", "metadata": {}, "source": [ - "## Generate Data for Unet3D\n", + "#### Generating data for the 3D U-Net\n", "\n", - "Before running DLIO, we need to obtain data for the unet3d use case. Instead of downloading the full dataset, we will use DLIO to generate a smaller, synthetic version of the dataset for this tutorial." + "Before running DLIO, we need to obtain data for emulated training of the 3D U-Net. Instead of downloading the full dataset, we use DLIO to generate a smaller, synthetic version of the dataset for this tutorial." ] }, { @@ -322,7 +357,8 @@ "!flux run -N {num_nodes} -o cpu-affinity=off --tasks-per-node={workers_per_node} --env-file=dlio_env.txt \\\n", " dlio_benchmark --config-dir={dlio_extensions_dir}/configs workload={workload} \\\n", " ++workload.dataset.data_folder={initial_data_directory} ++workload.workflow.generate_data=True \\\n", - " ++workload.workflow.train=False" + " ++workload.workflow.train=False\n", + "!echo \"FINISHED GENERATING DATA\"" ] }, { @@ -330,9 +366,9 @@ "id": "3f14ffdd", "metadata": {}, "source": [ - "## Run \"training\" through DLIO\n", + "#### Emulating training of the 3D U-Net with DLIO\n", "\n", - "Now, we will run DLIO using the command below. As DLIO runs, it will print out logging statements showing how long sample reading is taking. At the end, DLIO will print out a performance summary of the run." + "Now, we run DLIO using the command below. As DLIO runs, it prints out logging statements showing how long sample reading takes. At the end of the run, DLIO prints out a performance summary." ] }, { @@ -345,7 +381,8 @@ "!flux run -N {num_nodes} -o cpu-affinity=on --tasks-per-node={workers_per_node} --env-file=dlio_env.txt \\\n", " dlio_benchmark --config-dir={dlio_extensions_dir}/configs workload={workload} \\\n", " ++workload.dataset.data_folder={initial_data_directory} ++workload.workflow.generate_data=False \\\n", - " ++workload.workflow.train=True" + " ++workload.workflow.train=True\n", + "!echo \"FINISHED TRAINING\"" ] }, { @@ -353,9 +390,9 @@ "id": "573ce232", "metadata": {}, "source": [ - "## Shutdown the DYAD service and cleanup\n", + "#### Shutting down the DYAD service\n", "\n", - "Now that we are done running DLIO, we need to shutdown the DYAD service and remove the key-value store namespace used by DYAD. This can be done with the two Flux commands below." + "Now that we are done running DLIO, we need to shutdown the DYAD service and remove the key-value store namespace used by DYAD. This is done with the two Flux commands below." ] }, { @@ -423,7 +460,21 @@ "id": "68ea2fe7", "metadata": {}, "source": [ - "# Full Scale Results" + "### Evaluating DYAD's performance for the 3D U-Net at scale on Corona\n", + "\n", + "
\n", + "\n", + "
\n", + "
\n", + "
\n", + "\n", + "Figure X shows the performance of Lustre, [UnifyFS](https://ieeexplore.ieee.org/document/10177390), and DYAD in terms of runtime and I/O bandwidth for the full version of the 3D U-Net training. As explained on the [webpage for the KiTS19 Challenge](https://kits19.grand-challenge.org/), the dataset for the full version of this application consists of 10,240, NPZ-formatted image files, resulting in a total dataset size of 1.36 TB. Within each epoch of PyTorch-based training, the model processes batches of 4 images using 6 I/O processes per GPU. The model trains for 20 epochs without checkpointing. The model scales from 8 to 64 nodes of LLNL's [Corona](https://hpc.llnl.gov/hardware/compute-platforms/corona) supercomputer, with 8 GPUs per node.\n", + "\n", + "In the leftmost plot of Figure X, we show the runtime of the training for Lustre, UnifyFS, and DYAD at 8, 16, 32, and 64 nodes. This plot shows that DYAD provides significant runtime improvement compared to Lustre and UnifyFS for the 3D U-Net, mainly due to locality optimizations. DYAD runs up to 7.5 times faster than Lustre and 1.88 times faster than UnifyFS, with less performance variability due to DYAD's use of node-local storage.\n", + "\n", + "In the middle plot of Figure X, we show the bandwidth per epoch of training across 512 GPUs (64 nodes). Because DYAD's capabilities allow for on-the-fly caching of data, its performance starts similar to that of Lustre. As more data is cached into DYAD, its bandwidth increases to 140 GB/s due to DYAD's streaming RPC over RDMA protocol. Finally, as even more data is cached, DYAD's bandwidth reaches 1409 GB/s because DYAD's locality-aware caching allows almost all sample reads to be performed directly on node-local NVMe. In comparison, both Lustre and Unify maintain consistent bandwidths well under those of DYAD. By the 20th epoch, DYAD speeds up training by 10.62 times compared to UnifyFS.\n", + "\n", + "Finally, in the rightmost plot of Figure X, we show how often DYAD retrieved data from node-local storage versus retrieving data from storage on a remote node in terms of percentage of data access requests. Initially, DYAD mostly performs remote requests. As training continues, more and more data is replicated with DYAD's locality-aware caching, resulting in a larger percentage of local requests. By epoch 13, almost all data is accessed through local requests. This transition from mostly remote requests to mostly local requests corresponds with the increase in bandwidth shown in the middle plot of Figure X." ] }, { @@ -431,21 +482,16 @@ "id": "81d7d87f-1e09-42c8-b165-8902551f6847", "metadata": {}, "source": [ - "# This concludes the notebook tutorial for DYAD.\n", + "# This concludes Module 4.\n", "\n", - "If you are interested in learning more about DYAD, check out our [ReadTheDocs page](https://dyad.readthedocs.io/en/latest/), our [GitHub repository](https://github.com/flux-framework/dyad), and our published/presented works:\n", - "* [eScience 2022 Short Paper](https://dyad.readthedocs.io/en/latest/_downloads/27090817b034a89b76e5538e148fea9e/ShortPaper_2022_eScience_LLNL.pdf)\n", - "* [SC 2023 ACM Student Research Competition Extended Abstract](https://github.com/flux-framework/dyad/blob/main/docs/_static/ExtendedAbstract_2023_SC_ACM_SRC_DYAD.pdf)\n", - "* [IPDPS 2024 HiCOMB Workshop Paper](https://github.com/flux-framework/dyad/blob/main/docs/_static/Paper_2024_IPDPS_HiCOMB_DYAD.pdf)\n", + "In this module, we covered:\n", + "1. Design of DYAD\n", + "2. Distributed DL training\n", + "3. Deep Learning I/O (DLIO) benchmark\n", + "4. Accelerating distributed DL training\n", "\n", - "If you are interested in working with us, please reach out to Jae-Seung Yeom (yeom2@llnl.gov), Hariharan Devarajan (hariharandev1@llnl.gov), or Ian Lumsden (ilumsden@vols.utk.edu)." + "To continue with the tutorial, open [Module 5](./05_flux_tutorial_conclusions.ipynb)" ] - }, - { - "cell_type": "markdown", - "id": "d16426a9", - "metadata": {}, - "source": [] } ], "metadata": { diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/05_flux_tutorial_conclusions.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/05_flux_tutorial_conclusions.ipynb index dee077c..254622c 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/05_flux_tutorial_conclusions.ipynb +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/05_flux_tutorial_conclusions.ipynb @@ -40,6 +40,13 @@ " - [Getting Started with Flux and Go](https://converged-computing.github.io/flux-go/)\n", " - [Getting Started with Flux in C](https://converged-computing.github.io/flux-c-examples/) *looking for contributors*\n", "\n", + "We've also got resources for learning about DYAD!\n", + "* [DYAD's ReadTheDocs page](https://dyad.readthedocs.io/en/latest/)\n", + "* [DYAD's GitHub repository](https://github.com/flux-framework/dyad)\n", + "* [eScience 2022 Short Paper](https://dyad.readthedocs.io/en/latest/_downloads/27090817b034a89b76e5538e148fea9e/ShortPaper_2022_eScience_LLNL.pdf)\n", + "* [SC 2023 ACM Student Research Competition Extended Abstract](https://github.com/flux-framework/dyad/blob/main/docs/_static/ExtendedAbstract_2023_SC_ACM_SRC_DYAD.pdf)\n", + "* [IPDPS 2024 HiCOMB Workshop Paper](https://github.com/flux-framework/dyad/blob/main/docs/_static/Paper_2024_IPDPS_HiCOMB_DYAD.pdf)\n", + "\n", "And, of course, you can always reach out to us on any of our [project repositories](https://flux-framework.org) and ask any questions that you have. We'd love your contribution to code, documentation, or just saying hello!\n", "\n", "![https://flux-framework.org/flux-operator/_static/images/flux-operator.png](https://flux-framework.org/flux-operator/_static/images/flux-operator.png)\n", diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/img/dl-training-io.png b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/img/dl-training-io.png new file mode 100644 index 0000000000000000000000000000000000000000..6129d0ef09a6a0f695406d9bdc4277b6b952b990 GIT binary patch literal 36427 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 literal 0 HcmV?d00001 diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/img/dyad-software-stack.png b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/img/dyad-software-stack.png new file mode 100644 index 0000000000000000000000000000000000000000..eae3071c24cb0c9213c416221d8004facdcde1cc GIT binary patch literal 89601 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 b@)}h$=NtN1dwG6L6lpBB# literal 0 HcmV?d00001 diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/img/dyad-unet3d-results.svg b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/img/dyad-unet3d-results.svg new file mode 100644 index 0000000..d02c33d --- /dev/null +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/img/dyad-unet3d-results.svg @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From f24bbed70eee700457f9160995b0f05c122d2c51 Mon Sep 17 00:00:00 2001 From: Ian Lumsden Date: Thu, 11 Apr 2024 23:22:22 -0400 Subject: [PATCH 31/35] Adds the supplement and updates the conclusions --- .../05_flux_tutorial_conclusions.ipynb | 16 +- .../tutorial/notebook/06_supplement.ipynb | 312 ++++++++++++++++++ 2 files changed, 325 insertions(+), 3 deletions(-) create mode 100644 2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/06_supplement.ipynb diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/05_flux_tutorial_conclusions.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/05_flux_tutorial_conclusions.ipynb index 254622c..f245c64 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/05_flux_tutorial_conclusions.ipynb +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/05_flux_tutorial_conclusions.ipynb @@ -11,14 +11,24 @@ "# Module 5: Lessons learned, next steps, and discussion\n", "# This concludes the Flux tutorial! πŸ˜„οΈ\n", "\n", - "In this tutorial, we have [verb]:\n", - "\n", + "In this tutorial, we:\n", + "* Introduced Flux\n", + "* Showed how to get started with Flux\n", + "* Showed how to perform traditional batch scheduling with Flux\n", + "* Showed how to perform hierarchical scheduling with Flux\n", + "* Described the structure of Flux instances and how that structure supports distributed services\n", + "* Explained how to manage services with Flux\n", + "* Showed examples of Flux services\n", + "* Described the design of DYAD, a Flux service for runtime data movement\n", + "* Introduced distributed Deep Learning (DL) training\n", + "* Introduced Argonne National Laboratory's Deep Learning I/O (DLIO) benchmark\n", + "* Used DLIO to show how DYAD accelerates distributed DL training\n", "\n", "Don't worry, you'll have more opportunities for using Flux! We hope you reach out to us on any of our [project repositories](https://flux-framework.org) and ask any questions that you have. We'd love your contribution to code, documentation, or just saying hello! πŸ‘‹οΈ If you have feedback on the tutorial, please let us know so we can improve it for next year. \n", "\n", "## What do I do now?\n", "\n", - "Feel free to experiment more with Flux here, or (for more freedom) in the terminal. You can try more of the examples in the flux-workflow-examples directory one level up in the window to the left. If you're using a shared system like the one on the RADIUSS AWS tutorial please be mindful of other users and don't run compute intensive workloads. If you're running the tutorial in a job on an HPC cluster... compute away! ⚾️\n", + "Feel free to experiment more with Flux here, or (for more freedom) in the terminal. If you want to learn about several other useful features and commands in Flux, you can take a look at the supplemental [Module 6](./06_supplement.ipynb). You can also try more of the examples in the flux-workflow-examples directory one level up in the window to the left. If you're using a shared system like the one on the RADIUSS AWS tutorial please be mindful of other users and don't run compute intensive workloads. If you're running the tutorial in a job on an HPC cluster... compute away! ⚾️\n", "\n", "## How can I run this tutorial on my own?\n", "\n", diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/06_supplement.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/06_supplement.ipynb new file mode 100644 index 0000000..3f0f993 --- /dev/null +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/06_supplement.ipynb @@ -0,0 +1,312 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Module 6: Supplement\n", + "\n", + "This extra module covers various other aspects of Flux that we did not get to in this tutorial. Feel free to try thing out and play around!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Flux uptime\n", + "Flux provides an `uptime` utility to display properties of the Flux instance such as state of the current instance, how long it has been running, its size and if scheduling is disabled or stopped. The output shows how long the instance has been up, the instance owner, the instance depth (depth in the Flux hierarchy), and the size of the instance (number of brokers)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Flux Process and Job Utilities\n", + "### Flux top\n", + "Flux provides a feature-full version of `top` for nested Flux instances and jobs. In the JupyterLab terminal, invoke `flux top` to see the \"sleep\" jobs. If they have already completed you can resubmit them. \n", + "\n", + "We recommend not running `flux top` in the notebook as it is not designed to display output from a command that runs continuously.\n", + "\n", + "### Flux pstree\n", + "In analogy to `top`, Flux provides `flux pstree`. Try it out in the JupyterLab terminal or here in the notebook.\n", + "\n", + "### Flux proxy\n", + "\n", + "#### Interacting with a job hierarchy with `flux proxy`\n", + "\n", + "Flux proxy is used to route messages to and from a Flux instance. We can use `flux proxy` to connect to a running Flux instance and then submit more nested jobs inside it. You may want to edit `sleep_batch.sh` with the JupyterLab text editor (double click the file in the window on the left) to sleep for `60` or `120` seconds. Then from the JupyterLab terminal, run, you'll want to run the below. Yes, we really want you to open a terminal in the Jupyter launcher FILE-> NEW -> TERMINAL and run the commands below!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```bash\n", + "# The terminal will start at the root, ensure you are in the right spot!\n", + "# jovyan - that's you! \n", + "cd /home/jovyan/flux-radiuss-tutorial-2023/notebook/\n", + "\n", + "# Outputs the JOBID\n", + "flux batch --nslots=2 --cores-per-slot=1 --nodes=2 ./sleep_batch.sh\n", + "\n", + "# Put the JOBID into an environment variable\n", + "JOBID=$(flux job last)\n", + "\n", + "# See the flux process tree\n", + "flux pstree -a\n", + "\n", + "# Connect to the Flux instance corresponding to JOBID above\n", + "flux proxy ${JOBID}\n", + "\n", + "# Note the depth is now 1 and the size is 2: we're one level deeper in a Flux hierarchy and we have only 2 brokers now.\n", + "flux uptime\n", + "\n", + "# This instance has 2 \"nodes\" and 2 cores allocated to it\n", + "flux resource list\n", + "\n", + "# Have you used the top command in your terminal? We have one for flux!\n", + "flux top\n", + "```\n", + "\n", + "`flux top` was pretty cool, right? 😎️" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Submission API\n", + "Flux also provides first-class python bindings which can be used to submit jobs programmatically. The following script shows this with the `flux.job.submit()` call:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import json\n", + "import flux\n", + "from flux.job import JobspecV1\n", + "from flux.job.JobID import JobID" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "f = flux.Flux() # connect to the running Flux instance\n", + "compute_jobreq = JobspecV1.from_command(\n", + " command=[\"./compute.py\", \"120\"], num_tasks=1, num_nodes=1, cores_per_task=1\n", + ") # construct a jobspec\n", + "compute_jobreq.cwd = os.path.expanduser(\"~/flux-tutorial/flux-workflow-examples/job-submit-api/\") # set the CWD\n", + "print(JobID(flux.job.submit(f,compute_jobreq)).f58) # submit and print out the jobid (in f58 format)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### `flux.job.get_job(handle, jobid)` to get job info" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# This is a new command to get info about your job from the id!\n", + "fluxjob = flux.job.submit(f,compute_jobreq)\n", + "fluxjobid = JobID(fluxjob.f58)\n", + "print(f\"πŸŽ‰οΈ Hooray, we just submitted {fluxjobid}!\")\n", + "\n", + "# Here is how to get your info. The first argument is the flux handle, then the jobid\n", + "jobinfo = flux.job.get_job(f, fluxjobid)\n", + "print(json.dumps(jobinfo, indent=4))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!flux jobs -a | grep compute" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Under the hood, the `Jobspec` class is creating a YAML document that ultimately gets serialized as JSON and sent to Flux for ingestion, validation, queueing, scheduling, and eventually execution. We can dump the raw JSON jobspec that is submitted, where we can see the exact resources requested and the task set to be executed on those resources." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(compute_jobreq.dumps(indent=2))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can then replicate our previous example of submitting multiple heterogeneous jobs and testing that Flux co-schedules them." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "compute_jobreq = JobspecV1.from_command(\n", + " command=[\"./compute.py\", \"120\"], num_tasks=4, num_nodes=2, cores_per_task=2\n", + ")\n", + "compute_jobreq.cwd = os.path.expanduser(\"~/flux-tutorial/flux-workflow-examples/job-submit-api/\")\n", + "print(JobID(flux.job.submit(f, compute_jobreq)))\n", + "\n", + "io_jobreq = JobspecV1.from_command(\n", + " command=[\"./io-forwarding.py\", \"120\"], num_tasks=1, num_nodes=1, cores_per_task=1\n", + ")\n", + "io_jobreq.cwd = os.path.expanduser(\"~/flux-tutorial/flux-workflow-examples/job-submit-api/\")\n", + "print(JobID(flux.job.submit(f, io_jobreq)))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!flux jobs -a | grep compute" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can use the FluxExecutor class to submit large numbers of jobs to Flux. This method uses python's `concurrent.futures` interface. Example snippet from `~/flux-workflow-examples/async-bulk-job-submit/bulksubmit_executor.py`:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "``` python \n", + "with FluxExecutor() as executor:\n", + " compute_jobspec = JobspecV1.from_command(args.command)\n", + " futures = [executor.submit(compute_jobspec) for _ in range(args.njobs)]\n", + " # wait for the jobid for each job, as a proxy for the job being submitted\n", + " for fut in futures:\n", + " fut.jobid()\n", + " # all jobs submitted - print timings\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Submit a FluxExecutor based script.\n", + "%run ../flux-workflow-examples/async-bulk-job-submit/bulksubmit_executor.py -n200 /bin/sleep 0" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Diving Deeper Into Flux's Internals\n", + "\n", + "Flux uses [hwloc](https://github.com/open-mpi/hwloc) to detect the resources on each node and then to populate its resource graph.\n", + "\n", + "You can access the topology information that Flux collects with the `flux resource` subcommand:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!flux resource list" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Flux can also bootstrap its resource graph based on static input files, like in the case of a multi-user system instance setup by site administrators. [More information on Flux's static resource configuration files](https://flux-framework.readthedocs.io/en/latest/adminguide.html#resource-configuration). Flux provides a more standard interface to listing available resources that works regardless of the resource input source: `flux resource`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# To view status of resources\n", + "!flux resource status" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Flux has a command for controlling the queue within the `job-manager`: `flux queue`. This includes disabling job submission, re-enabling it, waiting for the queue to become idle or empty, and checking the queue status:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!flux queue disable \"maintenance outage\"\n", + "!flux queue enable\n", + "!flux queue -h" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Each Flux instance has a set of attributes that are set at startup that affect the operation of Flux, such as `rank`, `size`, and `local-uri` (the Unix socket usable for communicating with Flux). Many of these attributes can be modified at runtime, such as `log-stderr-level` (1 logs only critical messages to stderr while 7 logs everything, including debug messages)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!flux getattr rank\n", + "!flux getattr size\n", + "!flux getattr local-uri\n", + "!flux setattr log-stderr-level 3\n", + "!flux lsattr -v" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From 14108ca444bb628b69e70fa7541c8c8e3cfe6a55 Mon Sep 17 00:00:00 2001 From: Ian Lumsden Date: Thu, 11 Apr 2024 23:24:00 -0400 Subject: [PATCH 32/35] Tweaks a figure width --- .../JupyterNotebook/tutorial/notebook/04_dyad_dlio.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/04_dyad_dlio.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/04_dyad_dlio.ipynb index 9c473b2..ee80392 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/04_dyad_dlio.ipynb +++ b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/04_dyad_dlio.ipynb @@ -20,7 +20,7 @@ "DYAD provides transparent, locality-aware, write-once, read-many file caching that runs on top of local NVMe and other burst buffer-style technologies (e.g., El Capitan Rabbit nodes). Figure X shows the components of DYAD, including the DYAD service (implemented as a Flux broker module), the DYAD client, and DYAD's data transport layer. DYAD uses the Flux KVS to store metadata about tracked files, and it uses Flux's remote proceedure call capabilities to communicate between client and service. DYAD also uses UCX to perform RDMA-based data transfer to move files.\n", "\n", "

\n", - "\n", + "\n", "
\n", "Image created by Ian Lumsden for a poster at SC'23
\n", "
\n", From 55e27e21c277baae4f9781fbd851b7902adb8145 Mon Sep 17 00:00:00 2001 From: Ian Lumsden Date: Thu, 11 Apr 2024 23:35:44 -0400 Subject: [PATCH 33/35] Removes dead changes for testing DLIO --- .../JupyterNotebook/docker/Dockerfile.spawn | 20 ------- Dockerfile | 54 ------------------- 2 files changed, 74 deletions(-) delete mode 100644 Dockerfile diff --git a/2024-RIKEN-AWS/JupyterNotebook/docker/Dockerfile.spawn b/2024-RIKEN-AWS/JupyterNotebook/docker/Dockerfile.spawn index 7b855b3..e10163f 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/docker/Dockerfile.spawn +++ b/2024-RIKEN-AWS/JupyterNotebook/docker/Dockerfile.spawn @@ -9,7 +9,6 @@ USER root ENV NB_USER=jovyan \ NB_UID=1000 \ HOME=/home/jovyan - # VENV_DIR=/home/jovyan/.flux_tutorial_venv RUN adduser \ --disabled-password \ @@ -43,11 +42,6 @@ RUN apt-get update \ COPY ./requirements_venv.txt ./requirements_venv.txt RUN python3 -m pip install -r requirements_venv.txt -# ENV DLIO_PROFILER_ENABLE=0 -# RUN dlio_benchmark workload=unet3d_a100 ++workload.dataset.data_folder=/root/data ++workload.workflow.generate_data=True ++workload.workflow.train=False ++workload.dataset.record_length=4096 ++workload.dataset.record_length_stdev=0 ++workload.dataset.record_length_resize=0 ++workload.reader.batch_size=1 ++workload.dataset.num_files_train=16 ++workload.reader.read_threads=1 -# -# RUN dlio_benchmark workload=unet3d_a100 ++workload.dataset.data_folder=/root/data ++workload.workflow.generate_data=False ++workload.workflow.train=True ++workload.dataset.record_length=4096 ++workload.dataset.record_length_stdev=0 ++workload.dataset.record_length_resize=0 ++workload.reader.batch_size=1 ++workload.dataset.num_files_train=16 ++workload.reader.read_threads=1 - COPY ./requirements.txt ./requirements.txt RUN python3 -m pip install -r requirements.txt && \ python3 -m pip install ipython==7.34.0 && \ @@ -69,7 +63,6 @@ RUN git clone https://github.com/openucx/ucx.git \ && cd .. \ && rm -rf ucx -# RUN $VENV_SOURCE \ RUN git clone https://github.com/flux-framework/dyad.git \ && cd dyad \ && git checkout tutorial-riken-2024 \ @@ -83,10 +76,6 @@ RUN git clone https://github.com/flux-framework/dyad.git \ && cd ../.. \ && rm -rf dyad -# ENV DLIO_PROFILER_ENABLE=0 -# -# RUN dlio_benchmark workload=unet3d_a100 ++workload.dataset.data_folder=/root/data ++workload.workflow.generate_data=False ++workload.workflow.train=True ++workload.dataset.record_length=4096 ++workload.dataset.record_length_stdev=0 ++workload.dataset.record_length_resize=0 ++workload.reader.batch_size=1 ++workload.dataset.num_files_train=16 ++workload.reader.read_threads=1 - # This adds the flux-tree command, which is provided in flux-sched source # but not installed alongside production flux-core @@ -107,11 +96,6 @@ RUN chmod -R 777 ~/flux-tutorial-2024 WORKDIR $HOME COPY ./docker/flux-icon.png $HOME/flux-icon.png -# RUN ${VENV_SOURCE} && \ -# pip install --upgrade --force-reinstall cffi && \ -# python3 -m ipykernel install --user --name 'dyad_venv' --display-name 'DYAD Venv' && \ -# jupyter kernelspec list - # note that previous examples are added via git volume in config.yaml ENV SHELL=/usr/bin/bash ENV FLUX_URI_RESOLVE_LOCAL=t @@ -130,8 +114,4 @@ RUN mkdir -p $HOME/.local/share && \ USER ${NB_USER} -# RUN dlio_benchmark workload=unet3d_a100 ++workload.dataset.data_folder=${HOME}/data ++workload.workflow.generate_data=True ++workload.workflow.train=False ++workload.dataset.record_length=4096 ++workload.dataset.record_length_stdev=0 ++workload.dataset.record_length_resize=0 ++workload.reader.batch_size=1 ++workload.dataset.num_files_train=16 ++workload.reader.read_threads=1 -# -# RUN dlio_benchmark workload=unet3d_a100 ++workload.dataset.data_folder=${HOME}/data ++workload.workflow.generate_data=False ++workload.workflow.train=True ++workload.dataset.record_length=4096 ++workload.dataset.record_length_stdev=0 ++workload.dataset.record_length_resize=0 ++workload.reader.batch_size=1 ++workload.dataset.num_files_train=16 ++workload.reader.read_threads=1 - CMD ["flux", "start", "--test-size=4", "jupyter", "lab"] diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index ee6ba63..0000000 --- a/Dockerfile +++ /dev/null @@ -1,54 +0,0 @@ -FROM fluxrm/flux-sched:focal - -# Based off of https://github.com/jupyterhub/zero-to-jupyterhub-k8s/tree/main/images/singleuser-sample -# Local usage -# docker run -p 8888:8888 -v $(pwd):/home/jovyan/work test - -USER root - -ENV NB_USER=jovyan \ - NB_UID=1000 \ - HOME=/home/jovyan \ - VENV_DIR=/home/jovyan/.flux_tutorial_venv - -RUN adduser \ - --disabled-password \ - --gecos "Default user" \ - --uid ${NB_UID} \ - --home ${HOME} \ - --force-badname \ - ${NB_USER} - -RUN apt-get update \ - # && apt-get upgrade -y \ - && apt-get install -y --no-install-recommends \ - gcc-10 \ - g++-10 \ - ca-certificates \ - dnsutils \ - iputils-ping \ - python3.9 \ - python3.9-dev \ - python3-pip \ - python3-venv \ - openmpi-bin \ - openmpi-common \ - libopenmpi-dev \ - liblz4-dev \ - tini \ - # requirement for nbgitpuller - git \ - && rm -rf /var/lib/apt/lists/* - -ENV CC=gcc-10 \ - CXX=g++-10 - -RUN pip install git+https://github.com/argonne-lcf/dlio_benchmark.git - -RUN pip install dlio-profiler-py -ENV DLIO_PROFILER_ENABLE=0 -# RUN dlio_benchmark workload=unet3d_a100 ++workload.dataset.data_folder=/root/data ++workload.workflow.generate_data=True ++workload.workflow.train=False ++workload.dataset.record_length=4096 ++workload.dataset.record_length_stdev=0 ++workload.dataset.record_length_resize=0 ++workload.reader.batch_size=1 ++workload.dataset.num_files_train=16 ++workload.reader.read_threads=1 - -# RUN dlio_benchmark workload=unet3d_a100 ++workload.dataset.data_folder=/root/data ++workload.workflow.generate_data=False ++workload.workflow.train=True ++workload.dataset.record_length=4096 ++workload.dataset.record_length_stdev=0 ++workload.dataset.record_length_resize=0 ++workload.reader.batch_size=1 ++workload.dataset.num_files_train=16 ++workload.reader.read_threads=1] - -ENTRYPOINT [ "/bin/bash" ] \ No newline at end of file From 7051f863a7cf47024734a461bd4aad62ab457974 Mon Sep 17 00:00:00 2001 From: Ian Lumsden Date: Thu, 11 Apr 2024 23:41:02 -0400 Subject: [PATCH 34/35] Tries uncommenting the rm on apt lists --- 2024-RIKEN-AWS/JupyterNotebook/docker/Dockerfile.spawn | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/2024-RIKEN-AWS/JupyterNotebook/docker/Dockerfile.spawn b/2024-RIKEN-AWS/JupyterNotebook/docker/Dockerfile.spawn index e10163f..bdc27d6 100644 --- a/2024-RIKEN-AWS/JupyterNotebook/docker/Dockerfile.spawn +++ b/2024-RIKEN-AWS/JupyterNotebook/docker/Dockerfile.spawn @@ -36,8 +36,8 @@ RUN apt-get update \ liblz4-dev \ tini \ # requirement for nbgitpuller - git -#&& rm -rf /var/lib/apt/lists/* + git \ + && rm -rf /var/lib/apt/lists/* COPY ./requirements_venv.txt ./requirements_venv.txt RUN python3 -m pip install -r requirements_venv.txt From 0cb3fb01cdd8b35630c5d7b98434db3212a497b1 Mon Sep 17 00:00:00 2001 From: Ian Lumsden Date: Fri, 12 Apr 2024 00:01:36 -0400 Subject: [PATCH 35/35] Removes the old DYAD notebook from previous years since it's been superceded by 04_dyad_dlio.ipynb --- .../tutorial/notebook/dyad.ipynb | 671 ------------------ 1 file changed, 671 deletions(-) delete mode 100644 2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/dyad.ipynb diff --git a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/dyad.ipynb b/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/dyad.ipynb deleted file mode 100644 index 3ccdc8f..0000000 --- a/2024-RIKEN-AWS/JupyterNotebook/tutorial/notebook/dyad.ipynb +++ /dev/null @@ -1,671 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "dd3e912b-3428-4bc7-88bd-97686406b75a", - "metadata": { - "tags": [] - }, - "source": [ - "# DYAD\n", - "\n", - "DYAD is a synchronization and data movement tool for computational science workflows built on top of Flux. DYAD aims to provide the benefits of in situ and in transit tools (e.g., fine-grained synchronization between producer and consumer applications, fast data access due to spatial locality) while relying on a file-based data abstraction to maximize portability and minimize code change requirements for workflows. More specifically, DYAD aims to overcome the following challenges associated with traditional shared-storage and modern in situ and in transit data movement approaches:\n", - "\n", - "* Lack of per-file object synchronization in shared-storage approaches\n", - "* Poor temporal and spatial locality in shared-storage approaches\n", - "* Poor performance for file metadata operations in shared-storage approaches (and possibly some in situ and in transit approaches)\n", - "* Poor portability and the introduction of required code changes for in situ and in transit approaches\n", - "\n", - "In resolving these challenges, DYAD aims to provide the following to users:\n", - "\n", - "* Good performance (similar to in situ and in transit) due to on- or near-node temporary storage of data\n", - "* Transparent per-file object synchronization between producer and consumer applications\n", - "* Little to no code change to existing workflows to achieve the previous benefits\n", - "\n", - "To demonstrate DYAD's capabilities, we will use the simple demo applications found in the `dyad_demo` directory. This directory contains C and C++ implementations of a single producer application and a single consumer application. The producer application generates several files, each consisting of 10, 32-bit integers, and registers them with DYAD. The consumer application uses DYAD to wait until the desired file is produced. Then, if needed, it will use DYAD to retrieve the generated files from the Flux broker on which the producer application is running. Finally, the consumer application will read and validate the contents of each file.\n", - "\n", - "To start, specify which versions of the producer and consumer applications you would like to use by setting the `producer_program` and `consumer_program` variables. There are two versions for the producer (i.e., `c_prod` and `cpp_prod`) and two versions for the consumer (i.e., `c_cons` and `cpp_cons`)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0fa41e2c-80b1-498e-8ff9-7df6250c7a5d", - "metadata": {}, - "outputs": [], - "source": [ - "producer_program = \"/opt/dyad_demo/c_prod\" # Change to \"/opt/dyad_demo/cpp_prod\" for C++\n", - "consumer_program = \"/opt/dyad_demo/c_cons\" # Change to \"/opt/dyad_demo/cpp_cons\" for C++" - ] - }, - { - "cell_type": "markdown", - "id": "03cacf9d-f98a-45bb-9422-5648428c690f", - "metadata": {}, - "source": [ - "Next, specify the number of files you wish to generate and transfer by setting the `num_files_transfered` variable." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f9b72d51-f294-4e82-ab74-e77f922dc0af", - "metadata": {}, - "outputs": [], - "source": [ - "num_files_transfered = 10" - ] - }, - { - "cell_type": "markdown", - "id": "4dad884f-449e-4b00-bbc9-955fa9f31066", - "metadata": {}, - "source": [ - "The next step is to set the directories for DYAD to track. Each DYAD-enabled application tracks two directories: a **producer-managed directory** and a **consumer-managed directory**. At least one of these directories must be specified to use DYAD.\n", - "\n", - "When a producer-managed directory is provided, DYAD will store information about any file stored in that directory (or its subdirectories) into a namespace within the Flux key-value store (KVS). This information is later used by DYAD to transfer files from producer to consumer.\n", - "\n", - "When a consumer-managed directory is provided, DYAD will block the application whenever a file inside that directory (or subdirectory) is opened. This blocking will last until DYAD sees information about the file in the Flux KVS namespace. If the information retrieved from the KVS indicates that the file is actually located elsewhere, DYAD will use Flux's remote procedure call (RPC) system to ask the Flux broker at the file's location to transfer the file. If a transfer occurs, the file's contents will be stored at the file path passed to the original file opening function (e.g., `open`, `fopen`).\n", - "\n", - "In this demo, we will use 3 different directories: one unique to the consumer (`consumer_managed_directory`), one unique to the producer (`producer_managed_directory`), and one shared between producer and consumer (`shared_managed_directory`). Set the 3 variables in the cell below to specify these directories." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "60b0d5e0-fcf7-4fc9-a203-cdea84cd4950", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "consumer_managed_directory = \"/tmp/cons\"\n", - "producer_managed_directory = \"/tmp/prod\"\n", - "shared_managed_directory = \"/tmp/shared\"" - ] - }, - { - "cell_type": "markdown", - "id": "5bfa6706-0af5-4da8-bbc1-3edb9bccf953", - "metadata": {}, - "source": [ - "Finally, empty these directories or create new ones if they do not already exist." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d0dac6c9-43dd-4b4f-89e3-bfb180122f71", - "metadata": {}, - "outputs": [], - "source": [ - "!rm -rf {consumer_managed_directory}\n", - "!mkdir -p {consumer_managed_directory}\n", - "!chmod 755 {consumer_managed_directory}\n", - "!rm -rf {producer_managed_directory}\n", - "!mkdir -p {producer_managed_directory}\n", - "!chmod 755 {producer_managed_directory}\n", - "!rm -rf {shared_managed_directory}\n", - "!mkdir -p {shared_managed_directory}\n", - "!chmod 755 {shared_managed_directory}" - ] - }, - { - "cell_type": "markdown", - "id": "39b1aeec-d2b1-4f7e-80b1-519e4da2bff0", - "metadata": {}, - "source": [ - "## Example 1\n", - "\n", - "In this first example, we will be using DYAD to transfer data between a producer and consumer in different locations (e.g., on different nodes of a supercomputer). However, since this demo assumes we are running on a single AWS node, we will simulate the difference in locations by specifying different directories for the producer's managed directory and the consumer's managed directory. Normally, these directories would be the same and would both point to local, on-node storage.\n", - "\n", - "In this example, data will be transfered from the proudcer's managed directory to the consumer's managed directory. Additionally, each file opening call (e.g,. `open`, `fopen`) in the consumer application will be blocked until the relevant file is available in the producer's managed directory. The figure below illustrates this transfer and synchronization process." - ] - }, - { - "cell_type": "markdown", - "id": "aa5a6347-e407-47fd-9984-1a8f76b25b38", - "metadata": {}, - "source": [ - "
\n", - "
\n", - "
" - ] - }, - { - "cell_type": "markdown", - "id": "427c8a90-3d00-403d-825c-7e24d2117512", - "metadata": {}, - "source": [ - "Before running the DYAD-enabled applications, there are two things we must do:\n", - "1. Setup a namespace in the Flux KVS to be used by DYAD\n", - "2. Load DYAD's Flux module\n", - "\n", - "To begin, set the `kvs_namespace` variable to the namespace you wish to use for DYAD. This namespace can be any string value you want." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e2190770-fe49-4343-b2c8-eb625eb980d2", - "metadata": {}, - "outputs": [], - "source": [ - "kvs_namespace = \"dyad_test\"" - ] - }, - { - "cell_type": "markdown", - "id": "e116b785-5bdb-441b-9171-47e0b27a6e7d", - "metadata": {}, - "source": [ - "Next, create the namespace by running `flux kvs namespace create`. The cell below also runs `flux kvs namespace list` to allow you to verify that the namespace was created successfully." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a41c3f13-7b04-4ec5-9c5d-c5033d4977ca", - "metadata": {}, - "outputs": [], - "source": [ - "!flux kvs namespace create {kvs_namespace}\n", - "!flux kvs namespace list" - ] - }, - { - "cell_type": "markdown", - "id": "0840b124-f805-432e-9764-1b167df39f64", - "metadata": {}, - "source": [ - "The next step is to load DYAD's Flux module. This module is the component of DYAD that actually sends files from producer to consumer.\n", - "\n", - "To start this step, set `dyad_module` below to the path to the DYAD module (i.e., `dyad.so`). For this demo, DYAD has already been installed under the `/usr` prefix, so the path to the DYAD module should be `/usr/lib/dyad.so`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "cdb1dfc4-1f8a-434d-8be8-626382d124c6", - "metadata": {}, - "outputs": [], - "source": [ - "dyad_module = \"/usr/lib/dyad.so\"" - ] - }, - { - "cell_type": "markdown", - "id": "6c2260bc", - "metadata": {}, - "source": [ - "Next, choose the communication backend for DYAD to use. This backend is used by DYAD's data transport layer (DTL) component to move data from producer to consumer. Currently, valid values are:\n", - "* `UCX`: use Unified Communication X for data movement\n", - "* `FLUX_RPC`: use Flux Remote Procedure Call (RPC) feature for data movement" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "46b38519", - "metadata": {}, - "outputs": [], - "source": [ - "dtl_mode = \"UCX\"" - ] - }, - { - "cell_type": "markdown", - "id": "aab31cd8-5034-4450-bb1b-7b299fc5be86", - "metadata": {}, - "source": [ - "Finally, load the DYAD module by running `flux module load` on each broker. We load the module onto each broker because, normally, we would not know exactly which brokers the producer and consumer would be running on.\n", - "\n", - "When being loaded, the DYAD module takes a single command-line argument: the producer-managed directory. The module uses this directory to determine the path to any files it needs to transfer to consumers." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "edc65fdb-f746-46f6-81df-17602fd94acc", - "metadata": {}, - "outputs": [], - "source": [ - "!flux exec -r all flux module load {dyad_module} {producer_managed_directory} {dtl_mode}" - ] - }, - { - "cell_type": "markdown", - "id": "a71e4d07-f17e-4416-8f8d-0e36137b461a", - "metadata": {}, - "source": [ - "After loading the module, we can double check it has been loaded by running the cell below." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7061d3af-5d12-4edb-aa9e-6a678798ef14", - "metadata": {}, - "outputs": [], - "source": [ - "!flux exec -r all flux module list | grep dyad" - ] - }, - { - "cell_type": "markdown", - "id": "efcaef56-02e4-43fd-af28-2b6689db19e6", - "metadata": {}, - "source": [ - "Now, we will generate the shell commands that we will use to run the producer and consumer applications. These commands can be broken down into three pieces.\n", - "\n", - "First, the commands will set the `LD_PRELOAD` environment variable if running the C version of the producer or consumer. We set `LD_PRELOAD` because DYAD's C API uses the preload trick to intercept the `open`, `close`, `fopen`, and `fclose` functions.\n", - "\n", - "Second, the commands set a couple of environment variables to configure DYAD. The environment variables used in this example are:\n", - "* `DYAD_KVS_NAMESPACE`: specifies the Flux KVS namespace to use with DYAD\n", - "* `DYAD_DTL_MODE`: sets the communication backend to use for data movement\n", - "* `DYAD_PATH_PRODUCER`: sets the producer-managed path\n", - "* `DYAD_PATH_CONSUMER`: sets the consumer-managed path\n", - "\n", - "Finally, the rest of the commands are the invocation of the applications themselves.\n", - "\n", - "Run the following 2 cells to generate and see the commands for the producer and consumer." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c6def81f-f17a-4601-9572-b00d2959287f", - "metadata": {}, - "outputs": [], - "source": [ - "producer_launch_cmd = \"{preload} DYAD_KVS_NAMESPACE={kvs_namespace} DYAD_DTL_MODE={dtl_mode} \\\n", - "DYAD_PATH_PRODUCER={producer_managed_directory} flux exec -r 0 \\\n", - "{producer_program} {num_files_transfered} {producer_managed_directory}\".format(\n", - " preload=\"LD_PRELOAD=\\\"/usr/lib/dyad_wrapper.so\\\"\" if producer_program.split(\"/\")[-1].strip().startswith(\"c_\") else \"\",\n", - " kvs_namespace=kvs_namespace,\n", - " dtl_mode=dtl_mode,\n", - " producer_managed_directory=producer_managed_directory,\n", - " producer_program=producer_program,\n", - " num_files_transfered=num_files_transfered,\n", - ")\n", - "print(producer_launch_cmd)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "42d3007b-d6b4-40b9-a4a1-153aef536c90", - "metadata": {}, - "outputs": [], - "source": [ - "consumer_launch_cmd = \"{preload} DYAD_KVS_NAMESPACE={kvs_namespace} DYAD_DTL_MODE={dtl_mode} \\\n", - "DYAD_PATH_CONSUMER={consumer_managed_directory} flux exec -r 1 \\\n", - "{consumer_program} {num_files_transfered} {consumer_managed_directory}\".format(\n", - " preload=\"LD_PRELOAD=\\\"/usr/lib/dyad_wrapper.so\\\"\" if producer_program.split(\"/\")[-1].strip().startswith(\"c_\") else \"\",\n", - " kvs_namespace=kvs_namespace,\n", - " dtl_mode=dtl_mode,\n", - " consumer_managed_directory=consumer_managed_directory,\n", - " consumer_program=consumer_program,\n", - " num_files_transfered=num_files_transfered,\n", - ")\n", - "print(consumer_launch_cmd)" - ] - }, - { - "cell_type": "markdown", - "id": "7f51f9ea-c48c-4b75-a780-92059a1c7c61", - "metadata": {}, - "source": [ - "Finally, we will run the producer and consumer applications. Thanks to DYAD's fine-grained, per-file synchronization features, the order in which we launch the applications does not matter. In this example, we will run the consumer first to illustrate DYAD's synchronization features.\n", - "\n", - "Run the cell below to run the consumer. You will see that the consumer will immediately begin waiting for data to be made available." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9b2d62ba-d772-40a9-ab54-cef5695fc869", - "metadata": {}, - "outputs": [], - "source": [ - "!{consumer_launch_cmd}" - ] - }, - { - "cell_type": "markdown", - "id": "c413a90d-7429-4cad-bb98-fdaf8bbe5644", - "metadata": {}, - "source": [ - "Now that the consumer is running, we will run the producer. However, Jupyter will not let us launch the producer from within this notebook for as long as the consumer is running. To get around this, we will use the Jupyter Lab terminal.\n", - "\n", - "First, copy the producer command from above. Then, from the top of the file explorer on the left, click the plus (`+`) button. In the new Jupyter Lab tab that opens, click on \"Terminal\" (in the \"Other\" category) to launch the Jupyter Lab terminal. Finally, paste the producer command into the terminal, and run it.\n", - "\n", - "We know that the applications ran successfully if the consumer outputs \"OK\" for each file it checks." - ] - }, - { - "cell_type": "markdown", - "id": "c03e13e5-f8f8-4e33-bd5a-9432309dc2e8", - "metadata": {}, - "source": [ - "To see that the files were transfered, we can check the contents of the producer-managed and consumer-managed directories. If everything worked correctly, we will see the same files in both directories.\n", - "\n", - "Run the next two cells to check the contents of these directories." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3b4e23bc-2f13-4bc3-bf58-7a2dc838d47c", - "metadata": {}, - "outputs": [], - "source": [ - "!flux exec -r 0 ls -lah {producer_managed_directory}" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "55ee61b5-1df8-4036-8709-23dec67de7d6", - "metadata": {}, - "outputs": [], - "source": [ - "!flux exec -r 1 ls -lah {consumer_managed_directory}" - ] - }, - { - "cell_type": "markdown", - "id": "1a5ca339-d930-48d5-89f6-519b01a92fc6", - "metadata": {}, - "source": [ - "Before moving onto the next example, we need to remove the KVS namespace and unload the DYAD module. We cannot just reuse the namspace and module from this example for two reasons.\n", - "\n", - "First, the keys in the KVS that DYAD uses are based on the paths to the files *relative to the producer- and consumer-managed directories.* Since we are using the same applications for the next example, these relative paths will be the same, which means the keys will already be present in the KVS. This can interfere with the synchronization of the consumer.\n", - "\n", - "Second, the DYAD module currently tracks only a single directory at a time. We will be using a different directory for the next example, so we will need to startup the DYAD module from scratch to track this new directory.\n", - "\n", - "Run the next two cells to unload the DYAD module and remove the KVS namespace." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0bcea21e-f2e2-488f-bd95-8534f78c70b6", - "metadata": {}, - "outputs": [], - "source": [ - "!flux exec -r all flux module unload dyad" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d3197840-01e4-4479-8d9b-440e03155ca9", - "metadata": {}, - "outputs": [], - "source": [ - "!flux kvs namespace remove {kvs_namespace}" - ] - }, - { - "cell_type": "markdown", - "id": "4f437928-c3b6-4ff7-8d78-7ed31b09cda0", - "metadata": {}, - "source": [ - "Run this cell to verify that the DYAD module and KVS namespace are no longer present." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d537769c-6566-48cf-a150-20d40150f059", - "metadata": {}, - "outputs": [], - "source": [ - "!echo \"Modules Post-Cleanup\"\n", - "!echo \"====================\"\n", - "!flux module list\n", - "!echo \"\"\n", - "!echo \"KVS Namespaces Post-Cleanup\"\n", - "!echo \"===========================\"\n", - "!flux kvs namespace list" - ] - }, - { - "cell_type": "markdown", - "id": "de9f7143-f22a-4eea-911e-582f6c90e529", - "metadata": {}, - "source": [ - "## Example 2\n", - "\n", - "In the second example, we will show how DYAD can help workflows even if data is in shared storage (e.g., parallel file system) by still providing built-in and transparent fine-grained synchronization.\n", - "\n", - "The figure below illustrates the data movement that will happen in this example." - ] - }, - { - "cell_type": "markdown", - "id": "90ed7911-f507-4a69-a6f9-59185887a097", - "metadata": {}, - "source": [ - "
\n", - "
\n", - "
" - ] - }, - { - "cell_type": "markdown", - "id": "1c13f27e-551c-4841-8f58-825844d88cd9", - "metadata": {}, - "source": [ - "To start, we must setup the Flux KVS namespace and DYAD module again. \n", - "\n", - "Run the cells below to setup the Flux KVS namespace and the DYAD module." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7a9bcc65-0780-4652-87b3-a0b942dd48b2", - "metadata": {}, - "outputs": [], - "source": [ - "!flux kvs namespace create {kvs_namespace}\n", - "!flux kvs namespace list" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c43fcd2a-9291-407c-a313-f9be8a85cf4d", - "metadata": {}, - "outputs": [], - "source": [ - "!flux exec -r all flux module load {dyad_module} {shared_managed_directory} {dtl_mode}" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d8f3db83-8b97-440a-8b5f-f7cf76656928", - "metadata": {}, - "outputs": [], - "source": [ - "!flux exec -r all flux module list | grep dyad" - ] - }, - { - "cell_type": "markdown", - "id": "4b4a1949-2454-4e5b-aeba-ed420e42e620", - "metadata": {}, - "source": [ - "Next, we will generate the shell commands that we will use to run the producer and consumer applications. The only differences between these commands and the ones in Example 1 are as follows:\n", - "* The `DYAD_PATH_PRODUCER`, `DYAD_PATH_CONSUMER`, and second command-line argument to the applications all have the same value (i.e., the value of `shared_managed_directory` from the top of the notebook).\n", - "* The `DYAD_SHARED_STORAGE` environment variable is provided and set to 1. This tells DYAD to only perform fine-grained synchronization, rather than both synchronization and file transfer.\n", - "\n", - "Run the next two cells to generate the commands." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "37fa6ba6-375e-4aba-9253-f5e37cc701b9", - "metadata": {}, - "outputs": [], - "source": [ - "producer_launch_cmd = \"{preload} DYAD_KVS_NAMESPACE={kvs_namespace} DYAD_DTL_MODE={dtl_mode} \\\n", - "DYAD_PATH_PRODUCER={producer_managed_directory} DYAD_SHARED_STORAGE=1 \\\n", - "flux exec -r 0 \\\n", - "{producer_program} {num_files_transfered} {producer_managed_directory}\".format(\n", - " preload=\"LD_PRELOAD=\\\"/usr/lib/dyad_wrapper.so\\\"\" if producer_program.split(\"/\")[-1].strip().startswith(\"c_\") else \"\",\n", - " kvs_namespace=kvs_namespace,\n", - " dtl_mode=dtl_mode,\n", - " producer_managed_directory=shared_managed_directory,\n", - " producer_program=producer_program,\n", - " num_files_transfered=num_files_transfered,\n", - ")\n", - "print(producer_launch_cmd)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "02f13978-be62-4330-a1d7-3574c1d09573", - "metadata": {}, - "outputs": [], - "source": [ - "consumer_launch_cmd = \"{preload} DYAD_KVS_NAMESPACE={kvs_namespace} DYAD_DTL_MODE={dtl_mode} \\\n", - "DYAD_PATH_CONSUMER={consumer_managed_directory} DYAD_SHARED_STORAGE=1 \\\n", - "flux exec -r 1 \\\n", - "{consumer_program} {num_files_transfered} {consumer_managed_directory}\".format(\n", - " preload=\"LD_PRELOAD=\\\"/usr/lib/dyad_wrapper.so\\\"\" if producer_program.split(\"/\")[-1].strip().startswith(\"c_\") else \"\",\n", - " kvs_namespace=kvs_namespace,\n", - " dtl_mode=dtl_mode,\n", - " consumer_managed_directory=shared_managed_directory,\n", - " consumer_program=consumer_program,\n", - " num_files_transfered=num_files_transfered,\n", - ")\n", - "print(consumer_launch_cmd)" - ] - }, - { - "cell_type": "markdown", - "id": "c11fa139-026c-4b8d-8b64-3b73ba4c1ab8", - "metadata": {}, - "source": [ - "Finally, we will run the producer and consumer applications. To show how DYAD provides fine-grained synchronization even to shared storage workflows (e.g., workflows that use the parallel file system for data movement), we will run the consumer first.\n", - "\n", - "Run the cell below to run the consumer. The consumer will immediately begin waiting for data to be made available in shared storage." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ce54e2b4-d5fb-4451-bdf0-143450892292", - "metadata": {}, - "outputs": [], - "source": [ - "!{consumer_launch_cmd}" - ] - }, - { - "cell_type": "markdown", - "id": "ae80ba91-149e-46b6-b1ac-3742626b0664", - "metadata": {}, - "source": [ - "Now that the consumer is running, we will run the producer. Just like Example 1, we will run the producer by copying the producer command from above and running it in the Jupyter Lab terminal.\n", - "\n", - "As with Example 1, we know that the applications ran successfully if the consumer outputs \"OK\" for each file it checks." - ] - }, - { - "cell_type": "markdown", - "id": "eb92651e-ca2d-4c88-bb3f-95aef77d3938", - "metadata": {}, - "source": [ - "Finally, we need to remove the KVS namespace and unload the DYAD module.\n", - "\n", - "Run the next two cells to do this.\n", - "\n", - "Run the final code cell to verify that the DYAD module and KVS namespace are no longer present." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a8be46d2-e138-4849-9b51-6a02542f0bdd", - "metadata": {}, - "outputs": [], - "source": [ - "!flux exec -r all flux module unload dyad" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "de0bec06-3c6b-4644-b6a3-db4183bc3d46", - "metadata": {}, - "outputs": [], - "source": [ - "!flux kvs namespace remove {kvs_namespace}" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "16dbbcc2-aea2-4ba7-9410-4c21c5d0858f", - "metadata": {}, - "outputs": [], - "source": [ - "!echo \"Modules Post-Cleanup\"\n", - "!echo \"====================\"\n", - "!flux module list\n", - "!echo \"\"\n", - "!echo \"KVS Namespaces Post-Cleanup\"\n", - "!echo \"===========================\"\n", - "!flux kvs namespace list" - ] - }, - { - "cell_type": "markdown", - "id": "81d7d87f-1e09-42c8-b165-8902551f6847", - "metadata": {}, - "source": [ - "# This concludes the notebook tutorial for DYAD.\n", - "\n", - "## If you are interested in learning more about DYAD, check out our [ReadTheDocs page](https://dyad.readthedocs.io/en/latest/), our [GitHub repository](https://github.com/flux-framework/dyad), and our [short paper](https://dyad.readthedocs.io/en/latest/_downloads/27090817b034a89b76e5538e148fea9e/ShortPaper_2022_eScience_LLNL.pdf) and [poster](https://dyad.readthedocs.io/en/latest/_downloads/1f11761622683662c33fe0086d1d7ad2/Poster_2022_eScience_LLNL.pdf) from eScience 2022.\n", - "\n", - "## If you are interested in working with us, please reach out to Jae-Seung Yeom (yeom2@llnl.gov) or Ian Lumsden (ilumsden@vols.utk.edu)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "71d04206-343f-4407-880c-d67e659656d6", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.12" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -}
Flux
Submit a jobSubmitting jobssbatchflux batch
Submit an interactive jobSubmiting interactive jobssallocflux alloc
Run distributed application and wait for completionRunning distributed applications with waiting for completionsrunflux run
Run distrubted application and don't wait for completionRunning distrubted applications without waiting for completionN/Aflux submit
Query the status of jobsQuerying the status of jobssqueue/scontrol show job job_idflux jobs/flux job info job_id
Cancel running jobsCancelling running jobsscancelflux cancel