From 331d083ad3b7e043b296efe2722542a623a7681c Mon Sep 17 00:00:00 2001 From: Stefan Krawczyk Date: Tue, 31 Dec 2024 22:18:24 -0800 Subject: [PATCH 1/6] Adds lazy threadpool DAG parallelization Taking inspiration from #1263, I implemented a similar adapter to how async works. We get away with this because we don't encounter SERDE boundaries. If you run the example DAG you'll see that: 1. it is parallelized as it should be 2. you can use caching and the tracking adapter Rough edges: - haven't tested this extensively, but seems to just work. - need to add tests for it & docs, etc. --- .../lazy_threadpool_execution/my_functions.py | 55 ++++++++++++++++ .../lazy_threadpool_execution/my_funtions.png | Bin 0 -> 31569 bytes .../lazy_threadpool_execution/run.py | 59 ++++++++++++++++++ hamilton/plugins/h_threadpool.py | 58 +++++++++++++++++ 4 files changed, 172 insertions(+) create mode 100644 examples/parallelism/lazy_threadpool_execution/my_functions.py create mode 100644 examples/parallelism/lazy_threadpool_execution/my_funtions.png create mode 100644 examples/parallelism/lazy_threadpool_execution/run.py create mode 100644 hamilton/plugins/h_threadpool.py diff --git a/examples/parallelism/lazy_threadpool_execution/my_functions.py b/examples/parallelism/lazy_threadpool_execution/my_functions.py new file mode 100644 index 000000000..59e9b9762 --- /dev/null +++ b/examples/parallelism/lazy_threadpool_execution/my_functions.py @@ -0,0 +1,55 @@ +import time + + +def a() -> str: + print("a") + time.sleep(3) + return "a" + + +def b() -> str: + print("b") + time.sleep(3) + return "b" + + +def c(a: str, b: str) -> str: + print("c") + time.sleep(3) + return a + " " + b + + +def d() -> str: + print("d") + time.sleep(3) + return "d" + + +def e(c: str, d: str) -> str: + print("e") + time.sleep(3) + return c + " " + d + + +def z() -> str: + print("z") + time.sleep(3) + return "z" + + +def y() -> str: + print("y") + time.sleep(3) + return "y" + + +def x(z: str, y: str) -> str: + print("x") + time.sleep(3) + return z + " " + y + + +def s(x: str, e: str) -> str: + print("s") + time.sleep(3) + return x + " " + e diff --git a/examples/parallelism/lazy_threadpool_execution/my_funtions.png b/examples/parallelism/lazy_threadpool_execution/my_funtions.png new file mode 100644 index 0000000000000000000000000000000000000000..b7af35d49565939561aca4a5567de788a6cbadd0 GIT binary patch literal 31569 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 xw1Z6l-iFArkfWx_=pf7j9+3Q7c=#{{cl9L5lzY literal 0 HcmV?d00001 diff --git a/examples/parallelism/lazy_threadpool_execution/run.py b/examples/parallelism/lazy_threadpool_execution/run.py new file mode 100644 index 000000000..9c8a34061 --- /dev/null +++ b/examples/parallelism/lazy_threadpool_execution/run.py @@ -0,0 +1,59 @@ +import time + +import my_functions + +from hamilton import driver +from hamilton.plugins import h_threadpool + +start = time.time() +adapter = h_threadpool.FutureAdapter() +dr = driver.Builder().with_modules(my_functions).with_adapters(adapter).build() +dr.display_all_functions("my_funtions.png") +r = dr.execute("s") +print("got return from dr") +print(r) +print("Time taken with", time.time() - start) + +from hamilton_sdk import adapters + +tracker = adapters.HamiltonTracker( + project_id=21, # modify this as needed + username="elijah@dagworks.io", + dag_name="with_caching", + tags={"environment": "DEV", "cached": "False", "team": "MY_TEAM", "version": "1"}, +) + +start = time.time() +dr = ( + driver.Builder().with_modules(my_functions).with_adapters(tracker, adapter).with_cache().build() +) +dr.display_all_functions("a.png") +r = dr.execute("s") +print("got return from dr") +print(r) +print("Time taken with cold cache", time.time() - start) + +tracker = adapters.HamiltonTracker( + project_id=21, # modify this as needed + username="elijah@dagworks.io", + dag_name="with_caching", + tags={"environment": "DEV", "cached": "True", "team": "MY_TEAM", "version": "1"}, +) + +start = time.time() +dr = ( + driver.Builder().with_modules(my_functions).with_adapters(tracker, adapter).with_cache().build() +) +dr.display_all_functions("a.png") +r = dr.execute("s") +print("got return from dr") +print(r) +print("Time taken with warm cache", time.time() - start) + +start = time.time() +dr = driver.Builder().with_modules(my_functions).build() +dr.display_all_functions("a.png") +r = dr.execute("s") +print("got return from dr") +print(r) +print("Time taken without", time.time() - start) diff --git a/hamilton/plugins/h_threadpool.py b/hamilton/plugins/h_threadpool.py new file mode 100644 index 000000000..45102c956 --- /dev/null +++ b/hamilton/plugins/h_threadpool.py @@ -0,0 +1,58 @@ +from concurrent.futures import Future, ThreadPoolExecutor +from typing import Any, Callable, Dict + +from hamilton import registry + +registry.disable_autoload() + +from hamilton import lifecycle, node +from hamilton.lifecycle import base + + +def _new_fn(fn, **fn_kwargs): + """Function that runs in the thread. + + It can recursively check for Futures because we don't have to worry about + process serialization. + :param fn: Function to run + :param fn_kwargs: Keyword arguments to pass to the function + """ + for k, v in fn_kwargs.items(): + if isinstance(v, Future): + while isinstance(v, Future): + v = v.result() + fn_kwargs[k] = v + # execute the function once all the futures are resolved + return fn(**fn_kwargs) + + +class FutureAdapter(base.BaseDoRemoteExecute, lifecycle.ResultBuilder): + def __init__(self, max_workers: int = None): + self.executor = ThreadPoolExecutor(max_workers=max_workers) + # self.executor = ProcessPoolExecutor(max_workers=max_workers) + + def do_remote_execute( + self, + *, + execute_lifecycle_for_node: Callable, + node: node.Node, + **kwargs: Dict[str, Any], + ) -> Any: + """Method that is called to implement correct remote execution of hooks. This makes sure that all the pre-node and post-node hooks get executed in the remote environment which is necessary for some adapters. Node execution is called the same as before through "do_node_execute". + + :param node: Node that is being executed + :param kwargs: Keyword arguments that are being passed into the node + :param execute_lifecycle_for_node: Function executing lifecycle_hooks and lifecycle_methods + """ + return self.executor.submit(_new_fn, execute_lifecycle_for_node, **kwargs) + + def build_result(self, **outputs: Any) -> Any: + """Given a set of outputs, build the result. + + :param outputs: the outputs from the execution of the graph. + :return: the result of the execution of the graph. + """ + for k, v in outputs.items(): + if isinstance(v, Future): + outputs[k] = v.result() + return outputs From f72bfb698f8ec9605f256eaf2163edbeb0dba694 Mon Sep 17 00:00:00 2001 From: Stefan Krawczyk Date: Wed, 1 Jan 2025 21:22:59 -0800 Subject: [PATCH 2/6] Adds docs, tests, and example --- docs/concepts/parallel-task.rst | 42 +++ .../ThreadPoolFutureAdatper.rst | 11 + docs/reference/graph-adapters/index.rst | 1 + .../lazy_threadpool_execution/README.md | 48 +++ .../my_functions_async.py | 55 +++ .../lazy_threadpool_execution/my_funtions.png | Bin 31569 -> 33036 bytes .../lazy_threadpool_execution/notebook.ipynb | 325 ++++++++++++++++++ .../lazy_threadpool_execution/run.py | 30 +- hamilton/plugins/h_threadpool.py | 40 ++- tests/plugins/test_h_threadpool.py | 60 ++++ 10 files changed, 597 insertions(+), 15 deletions(-) create mode 100644 docs/reference/graph-adapters/ThreadPoolFutureAdatper.rst create mode 100644 examples/parallelism/lazy_threadpool_execution/README.md create mode 100644 examples/parallelism/lazy_threadpool_execution/my_functions_async.py create mode 100644 examples/parallelism/lazy_threadpool_execution/notebook.ipynb create mode 100644 tests/plugins/test_h_threadpool.py diff --git a/docs/concepts/parallel-task.rst b/docs/concepts/parallel-task.rst index 6159b753d..1b09a5c82 100644 --- a/docs/concepts/parallel-task.rst +++ b/docs/concepts/parallel-task.rst @@ -1,6 +1,48 @@ Dynamic DAGs/Parallel Execution ---------------------------------- +There are two approaches to parallel execution in Hamilton: + +1. Using an adapter that submits each node/function to a system that handles execution, e.g. ray, dask, async, or a threadpool. +2. Using the `Parallelizable[]` and `Collect[]` types + delegating to an executor. + +Using an Adapter +================ +The adapter approach effectively farms out the execution of each node/function to a system that can handle resolving +futures. That is, Hamilton walks the DAG and submits each node to the adapter, which then submits the node for execution, +and internally the execution resolves any Futures from prior submitted nodes. + +To make use of this, the general pattern is you apply an adapter to the driver and don't need to touch your Hamilton functions!: + +.. code-block:: python + + from hamilton import driver + from hamilton.execution import executors + from hamilton.plugins.h_threadpool import FutureAdapter + # from hamilton.plugins.h_ray import RayGraphAdapter + # from hamilton.plugins.h_dask import DaskGraphAdapter + + dr = ( + driver.Builder() + .with_modules(foo_module) + .with_adapter(FutureAdapter()) + .build() + ) + + dr.execute(["my_variable"], inputs={...}, overrides={...}) + +The code above will execute the DAG submitting to a `ThreadPoolExecutor` (see :doc:`../reference/graph-adapters/ThreadPoolFutureAdapter`), +which is great if you're doing a lot of I/O bound work, e.g. making API calls, reading from a database, etc. + +See this `Threadpool based example `_ for a complete example. + +Other adapters, e.g. Ray :doc:`../reference/graph-adapters/RayGraphAdapter`, Dask :doc:`../reference/graph-adapters/DaskGraphAdapter`, etc... will submit to their respective executors, but will involve object serialization +(see caveats below). + +Using the `Parallelizable[]` and `Collect[]` types +================================================== + + Hamilton now has pluggable execution, which allows for the following: 1. Grouping of nodes into "tasks" (discrete execution unit between serialization boundaries) diff --git a/docs/reference/graph-adapters/ThreadPoolFutureAdatper.rst b/docs/reference/graph-adapters/ThreadPoolFutureAdatper.rst new file mode 100644 index 000000000..ca34d1461 --- /dev/null +++ b/docs/reference/graph-adapters/ThreadPoolFutureAdatper.rst @@ -0,0 +1,11 @@ +========================== +h_threadpool.FutureAdapter +========================== + +This is an adapter to delegate execution of the individual nodes in a Hamilton graph to a threadpool. +This is useful when you have a graph with many nodes that can be executed in parallel. + +.. autoclass:: hamilton.plugins.h_threadpool.FutureAdapter + :special-members: __init__ + :members: + :inherited-members: diff --git a/docs/reference/graph-adapters/index.rst b/docs/reference/graph-adapters/index.rst index 4f9f54de4..ffb43dd66 100644 --- a/docs/reference/graph-adapters/index.rst +++ b/docs/reference/graph-adapters/index.rst @@ -15,6 +15,7 @@ Reference SimplePythonGraphAdapter HamiltonGraphAdapter AsyncGraphAdapter + ThreadPoolFutureAdapter CachingGraphAdapter DaskGraphAdapter PySparkUDFGraphAdapter diff --git a/examples/parallelism/lazy_threadpool_execution/README.md b/examples/parallelism/lazy_threadpool_execution/README.md new file mode 100644 index 000000000..518e3f1fd --- /dev/null +++ b/examples/parallelism/lazy_threadpool_execution/README.md @@ -0,0 +1,48 @@ +# Lazy threadpool execution + +[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/dagworks-inc/hamilton/blob/main/examples/parallelism/lazy_threadpool_execution/notebook.ipynb) + +This example is different from the other examples under /parallelism/ in that +it demonstrates how to use an adapter to put each +function into a threadpool that allows for lazy DAG evaluation and for parallelism +to be achieved. This is useful when you have a lot of +functions doing I/O bound tasks and you want to speed +up the execution of your program. E.g. doing lots of +HTTP requests, reading/writing to disk, LLM API calls, etc. + +> Note: this adapter does not support DAGs with Parallelizable and Collect functions; create an issue if you need this feature. + +When you execute `run.py`, you will output that shows: + +1. The DAG running in parallel -- check the image against what is printed. +2. The DAG logging to the Hamilton UI -- please adjust for you project. +3. The DAG running without the adapter -- this is to show the difference in execution time. +4. An async version of the DAG running in parallel -- this is to show that the performance of this approach is similar. + +```bash +python run.py +``` + +To use this adapter: + +```python +from hamilton import driver +from hamilton.plugins import h_threadpool + +# import your hamilton functions +import my_functions + +# Create the adapter +adapter = h_threadpool.FutureAdapter() + +# Create a driver +dr = ( + driver.Builder() + .with_modules(my_functions) + .with_adapters(adapter) + .build() +) +# execute +dr.execute(["s", "x", "a"]) # if the DAG can be parallelized it will be + +``` diff --git a/examples/parallelism/lazy_threadpool_execution/my_functions_async.py b/examples/parallelism/lazy_threadpool_execution/my_functions_async.py new file mode 100644 index 000000000..a639ed963 --- /dev/null +++ b/examples/parallelism/lazy_threadpool_execution/my_functions_async.py @@ -0,0 +1,55 @@ +import asyncio + + +async def a() -> str: + print("a") + await asyncio.sleep(3) + return "a" + + +async def b() -> str: + print("b") + await asyncio.sleep(3) + return "b" + + +async def c(a: str, b: str) -> str: + print("c") + await asyncio.sleep(3) + return a + " " + b + + +async def d() -> str: + print("d") + await asyncio.sleep(3) + return "d" + + +async def e(c: str, d: str) -> str: + print("e") + await asyncio.sleep(3) + return c + " " + d + + +async def z() -> str: + print("z") + await asyncio.sleep(3) + return "z" + + +async def y() -> str: + print("y") + await asyncio.sleep(3) + return "y" + + +async def x(z: str, y: str) -> str: + print("x") + await asyncio.sleep(3) + return z + " " + y + + +async def s(x: str, e: str) -> str: + print("s") + await asyncio.sleep(3) + return x + " " + e diff --git a/examples/parallelism/lazy_threadpool_execution/my_funtions.png b/examples/parallelism/lazy_threadpool_execution/my_funtions.png index b7af35d49565939561aca4a5567de788a6cbadd0..fb0b5cc0860e076e3b3019a67b64f83abbbd601b 100644 GIT binary patch literal 33036 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

myp;UP!O^NCp>Du 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 literal 31569 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 xw1Z6l-iFArkfWx_=pf7j9+3Q7c=#{{cl9L5lzY diff --git a/examples/parallelism/lazy_threadpool_execution/notebook.ipynb b/examples/parallelism/lazy_threadpool_execution/notebook.ipynb new file mode 100644 index 000000000..2b66908b9 --- /dev/null +++ b/examples/parallelism/lazy_threadpool_execution/notebook.ipynb @@ -0,0 +1,325 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "initial_id", + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# Execute this cell to install dependencies\n", + "%pip install sf-hamilton[visualization]" + ] + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "# run me in google colab [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/dagworks-inc/hamilton/blob/main/examples/parallelism/lazy_threadpool_execution/notebook.ipynb) [![GitHub badge](https://img.shields.io/badge/github-view_source-2b3137?logo=github)](https://github.com/dagworks-inc/hamilton/blob/main/examples/parallelism/lazy_threadpool_execution/notebook.ipynb)\n", + "id": "7b55978b426b6e42" + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-02T05:00:01.808537Z", + "start_time": "2025-01-02T04:59:53.847872Z" + } + }, + "cell_type": "code", + "source": "%load_ext hamilton.plugins.jupyter_magic", + "id": "a1f2f8937a0b0488", + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/stefankrawczyk/.pyenv/versions/knowledge_retrieval-py39/lib/python3.9/site-packages/pyspark/pandas/__init__.py:50: UserWarning: 'PYARROW_IGNORE_TIMEZONE' environment variable was not set. It is required to set this environment variable to '1' in both driver and executor sides if you use pyarrow>=2.0.0. pandas-on-Spark will set it for you but it does not work if there is a Spark context already launched.\n", + " warnings.warn(\n" + ] + } + ], + "execution_count": 1 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": [ + "# Create a module with some functions\n", + "This hopefully shows a good example of what could be parallelized given the structure of the DAG." + ], + "id": "32c01561386fc348" + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-02T05:00:16.514305Z", + "start_time": "2025-01-02T05:00:16.101379Z" + } + }, + "cell_type": "code", + "source": [ + "%%cell_to_module my_functions --display\n", + "\n", + "import time\n", + "\n", + "\n", + "def a() -> str:\n", + " print(\"a\")\n", + " time.sleep(3)\n", + " return \"a\"\n", + "\n", + "\n", + "def b() -> str:\n", + " print(\"b\")\n", + " time.sleep(3)\n", + " return \"b\"\n", + "\n", + "\n", + "def c(a: str, b: str) -> str:\n", + " print(\"c\")\n", + " time.sleep(3)\n", + " return a + \" \" + b\n", + "\n", + "\n", + "def d() -> str:\n", + " print(\"d\")\n", + " time.sleep(3)\n", + " return \"d\"\n", + "\n", + "\n", + "def e(c: str, d: str) -> str:\n", + " print(\"e\")\n", + " time.sleep(3)\n", + " return c + \" \" + d\n", + "\n", + "\n", + "def z() -> str:\n", + " print(\"z\")\n", + " time.sleep(3)\n", + " return \"z\"\n", + "\n", + "\n", + "def y() -> str:\n", + " print(\"y\")\n", + " time.sleep(3)\n", + " return \"y\"\n", + "\n", + "\n", + "def x(z: str, y: str) -> str:\n", + " print(\"x\")\n", + " time.sleep(3)\n", + " return z + \" \" + y\n", + "\n", + "\n", + "def s(x: str, e: str) -> str:\n", + " print(\"s\")\n", + " time.sleep(3)\n", + " return x + \" \" + e\n", + "\n" + ], + "id": "8e0e3b7a96ca1d44", + "outputs": [ + { + "data": { + "image/svg+xml": "\n\n\n\n\n\n\n\ncluster__legend\n\nLegend\n\n\n\nc\n\nc\nstr\n\n\n\ne\n\ne\nstr\n\n\n\nc->e\n\n\n\n\n\nx\n\nx\nstr\n\n\n\ns\n\ns\nstr\n\n\n\nx->s\n\n\n\n\n\na\n\na\nstr\n\n\n\na->c\n\n\n\n\n\ne->s\n\n\n\n\n\nb\n\nb\nstr\n\n\n\nb->c\n\n\n\n\n\nz\n\nz\nstr\n\n\n\nz->x\n\n\n\n\n\nd\n\nd\nstr\n\n\n\nd->e\n\n\n\n\n\ny\n\ny\nstr\n\n\n\ny->x\n\n\n\n\n\nfunction\n\nfunction\n\n\n\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "execution_count": 2 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "# Run it without the adapter", + "id": "60355598274f9b79" + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-02T05:02:44.719265Z", + "start_time": "2025-01-02T05:02:44.423066Z" + } + }, + "cell_type": "code", + "source": [ + "from hamilton import driver\n", + "dr = driver.Builder().with_modules(my_functions).build()\n", + "dr" + ], + "id": "fcb0677daf5b4a31", + "outputs": [ + { + "data": { + "image/svg+xml": "\n\n\n\n\n\n\n\ncluster__legend\n\nLegend\n\n\n\nc\n\nc\nstr\n\n\n\ne\n\ne\nstr\n\n\n\nc->e\n\n\n\n\n\nx\n\nx\nstr\n\n\n\ns\n\ns\nstr\n\n\n\nx->s\n\n\n\n\n\na\n\na\nstr\n\n\n\na->c\n\n\n\n\n\ne->s\n\n\n\n\n\nb\n\nb\nstr\n\n\n\nb->c\n\n\n\n\n\nz\n\nz\nstr\n\n\n\nz->x\n\n\n\n\n\nd\n\nd\nstr\n\n\n\nd->e\n\n\n\n\n\ny\n\ny\nstr\n\n\n\ny->x\n\n\n\n\n\nfunction\n\nfunction\n\n\n\n", + "text/plain": [ + "" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "execution_count": 6 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-02T05:03:18.620774Z", + "start_time": "2025-01-02T05:02:51.536385Z" + } + }, + "cell_type": "code", + "source": [ + "start = time.time()\n", + "r = dr.execute([\"s\", \"x\", \"a\"])\n", + "print(\"got return from dr\")\n", + "print(r)\n", + "print(\"Time taken with\", time.time() - start)" + ], + "id": "960f9f5d5f018b38", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "z\n", + "y\n", + "x\n", + "a\n", + "b\n", + "c\n", + "d\n", + "e\n", + "s\n", + "got return from dr\n", + "{'s': 'z y a b d', 'x': 'z y', 'a': 'a'}\n", + "Time taken with 27.080925941467285\n" + ] + } + ], + "execution_count": 7 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "# Run it with the adapter -- note the parallelism & time taken", + "id": "8a1d2b183b914034" + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-02T05:03:18.904861Z", + "start_time": "2025-01-02T05:03:18.632385Z" + } + }, + "cell_type": "code", + "source": [ + "from hamilton import driver\n", + "from hamilton.plugins import h_threadpool\n", + "\n", + "adapter = h_threadpool.FutureAdapter()\n", + "dr = driver.Builder().with_modules(my_functions).with_adapters(adapter).build()\n", + "dr" + ], + "id": "63853f111ef28439", + "outputs": [ + { + "data": { + "image/svg+xml": "\n\n\n\n\n\n\n\ncluster__legend\n\nLegend\n\n\n\nc\n\nc\nstr\n\n\n\ne\n\ne\nstr\n\n\n\nc->e\n\n\n\n\n\nx\n\nx\nstr\n\n\n\ns\n\ns\nstr\n\n\n\nx->s\n\n\n\n\n\na\n\na\nstr\n\n\n\na->c\n\n\n\n\n\ne->s\n\n\n\n\n\nb\n\nb\nstr\n\n\n\nb->c\n\n\n\n\n\nz\n\nz\nstr\n\n\n\nz->x\n\n\n\n\n\nd\n\nd\nstr\n\n\n\nd->e\n\n\n\n\n\ny\n\ny\nstr\n\n\n\ny->x\n\n\n\n\n\nfunction\n\nfunction\n\n\n\n", + "text/plain": [ + "" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "execution_count": 8 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-02T05:03:30.949086Z", + "start_time": "2025-01-02T05:03:18.925667Z" + } + }, + "cell_type": "code", + "source": [ + "start = time.time()\n", + "r = dr.execute([\"s\", \"x\", \"a\"])\n", + "print(\"got return from dr\")\n", + "print(r)\n", + "print(\"Time taken with\", time.time() - start)" + ], + "id": "1bb057f4277705de", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "z\n", + "y\n", + "a\n", + "b\n", + "d\n", + "x\n", + "c\n", + "e\n", + "s\n", + "got return from dr\n", + "{'s': 'z y a b d', 'x': 'z y', 'a': 'a'}\n", + "Time taken with 12.019250869750977\n" + ] + } + ], + "execution_count": 9 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": [ + "# 27 seconds vs 12 seconds\n", + "\n", + "With the adapter we see a significant improvement in time taken to execute the DAG. This is because the adapter is able to parallelize the execution." + ], + "id": "56e9fb7445639984" + }, + { + "metadata": {}, + "cell_type": "code", + "outputs": [], + "execution_count": null, + "source": "", + "id": "e31132a4fd211887" + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/parallelism/lazy_threadpool_execution/run.py b/examples/parallelism/lazy_threadpool_execution/run.py index 9c8a34061..6d995b904 100644 --- a/examples/parallelism/lazy_threadpool_execution/run.py +++ b/examples/parallelism/lazy_threadpool_execution/run.py @@ -2,14 +2,14 @@ import my_functions -from hamilton import driver +from hamilton import async_driver, driver from hamilton.plugins import h_threadpool start = time.time() adapter = h_threadpool.FutureAdapter() dr = driver.Builder().with_modules(my_functions).with_adapters(adapter).build() dr.display_all_functions("my_funtions.png") -r = dr.execute("s") +r = dr.execute(["s", "x", "a"]) print("got return from dr") print(r) print("Time taken with", time.time() - start) @@ -27,8 +27,7 @@ dr = ( driver.Builder().with_modules(my_functions).with_adapters(tracker, adapter).with_cache().build() ) -dr.display_all_functions("a.png") -r = dr.execute("s") +r = dr.execute(["s", "x", "a"]) print("got return from dr") print(r) print("Time taken with cold cache", time.time() - start) @@ -44,16 +43,31 @@ dr = ( driver.Builder().with_modules(my_functions).with_adapters(tracker, adapter).with_cache().build() ) -dr.display_all_functions("a.png") -r = dr.execute("s") +r = dr.execute(["s", "x", "a"]) print("got return from dr") print(r) print("Time taken with warm cache", time.time() - start) start = time.time() dr = driver.Builder().with_modules(my_functions).build() -dr.display_all_functions("a.png") -r = dr.execute("s") +r = dr.execute(["s", "x", "a"]) print("got return from dr") print(r) print("Time taken without", time.time() - start) + + +async def run_async(): + import my_functions_async + + start = time.time() + dr = await async_driver.Builder().with_modules(my_functions_async).build() + dr.display_all_functions("my_functions_async.png") + r = await dr.execute(["s", "x", "a"]) + print("got return from dr") + print(r) + print("Async Time taken without", time.time() - start) + + +import asyncio + +asyncio.run(run_async()) diff --git a/hamilton/plugins/h_threadpool.py b/hamilton/plugins/h_threadpool.py index 45102c956..644c41fcf 100644 --- a/hamilton/plugins/h_threadpool.py +++ b/hamilton/plugins/h_threadpool.py @@ -9,7 +9,7 @@ from hamilton.lifecycle import base -def _new_fn(fn, **fn_kwargs): +def _new_fn(fn: Callable, **fn_kwargs) -> Any: """Function that runs in the thread. It can recursively check for Futures because we don't have to worry about @@ -20,16 +20,39 @@ def _new_fn(fn, **fn_kwargs): for k, v in fn_kwargs.items(): if isinstance(v, Future): while isinstance(v, Future): - v = v.result() + v = v.result() # this blocks until the future is resolved fn_kwargs[k] = v # execute the function once all the futures are resolved return fn(**fn_kwargs) class FutureAdapter(base.BaseDoRemoteExecute, lifecycle.ResultBuilder): - def __init__(self, max_workers: int = None): - self.executor = ThreadPoolExecutor(max_workers=max_workers) - # self.executor = ProcessPoolExecutor(max_workers=max_workers) + """Adapter that lazily submits each function for execution to a ThreadpoolExecutor. + + This adapter has similar behavior to the async Hamilton driver which allows for parallel execution of functions. + + This adapter works because we don't have to worry about object serialization. + + Caveats: + - DAGs with lots of CPU intense functions will limit usefulness of this adapter, unless they release the GIL. + - DAGs with lots of I/O bound work will benefit from this adapter, e.g. making API calls. + - The max parallelism is limited by the number of threads in the ThreadPoolExecutor. + + Unsupported behavior: + - The FutureAdapter does not support DAGs with Parallelizable & Collect functions. This is due to laziness + rather than anything inherently technical. If you'd like this feature, please open an issue on the Hamilton + repository. + + """ + + def __init__(self, max_workers: int = None, thread_name_prefix: str = ""): + """Constructor. + :param max_workers: The maximum number of threads that can be used to execute the given calls. + :param thread_name_prefix: An optional name prefix to give our threads. + """ + self.executor = ThreadPoolExecutor( + max_workers=max_workers, thread_name_prefix=thread_name_prefix + ) def do_remote_execute( self, @@ -38,17 +61,20 @@ def do_remote_execute( node: node.Node, **kwargs: Dict[str, Any], ) -> Any: - """Method that is called to implement correct remote execution of hooks. This makes sure that all the pre-node and post-node hooks get executed in the remote environment which is necessary for some adapters. Node execution is called the same as before through "do_node_execute". + """Function that submits the passed in function to the ThreadPoolExecutor to be executed + after wrapping it with the _new_fn function. :param node: Node that is being executed - :param kwargs: Keyword arguments that are being passed into the node :param execute_lifecycle_for_node: Function executing lifecycle_hooks and lifecycle_methods + :param kwargs: Keyword arguments that are being passed into the function """ return self.executor.submit(_new_fn, execute_lifecycle_for_node, **kwargs) def build_result(self, **outputs: Any) -> Any: """Given a set of outputs, build the result. + This function will block until all futures are resolved. + :param outputs: the outputs from the execution of the graph. :return: the result of the execution of the graph. """ diff --git a/tests/plugins/test_h_threadpool.py b/tests/plugins/test_h_threadpool.py new file mode 100644 index 000000000..ead9f9a3a --- /dev/null +++ b/tests/plugins/test_h_threadpool.py @@ -0,0 +1,60 @@ +from concurrent.futures import Future + +from hamilton.plugins.h_threadpool import FutureAdapter, _new_fn + + +def test_new_fn_with_no_futures(): + def sample_fn(a, b): + return a + b + + result = _new_fn(sample_fn, a=1, b=2) + assert result == 3 + + +def test_new_fn_with_futures(): + def sample_fn(a, b): + return a + b + + future_a = Future() + future_b = Future() + future_a.set_result(1) + future_b.set_result(2) + + result = _new_fn(sample_fn, a=future_a, b=future_b) + assert result == 3 + + +def test_future_adapter_do_remote_execute(): + def sample_fn(a, b): + return a + b + + adapter = FutureAdapter(max_workers=2) + future = adapter.do_remote_execute(execute_lifecycle_for_node=sample_fn, node=None, a=1, b=2) + assert future.result() == 3 + + +def test_future_adapter_do_remote_execute_with_futures(): + def sample_fn(a, b): + return a + b + + future_a = Future() + future_b = Future() + future_a.set_result(1) + future_b.set_result(2) + + adapter = FutureAdapter(max_workers=2) + future = adapter.do_remote_execute( + execute_lifecycle_for_node=sample_fn, node=None, a=future_a, b=future_b + ) + assert future.result() == 3 + + +def test_future_adapter_build_result(): + adapter = FutureAdapter(max_workers=2) + future_a = Future() + future_b = Future() + future_a.set_result(1) + future_b.set_result(2) + + result = adapter.build_result(a=future_a, b=future_b) + assert result == {"a": 1, "b": 2} From 037c9150e4cf36967284d553218a04d772ec842b Mon Sep 17 00:00:00 2001 From: Stefan Krawczyk Date: Wed, 1 Jan 2025 21:30:30 -0800 Subject: [PATCH 3/6] Fix file name --- .../{ThreadPoolFutureAdatper.rst => ThreadPoolFutureAdapter.rst} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/reference/graph-adapters/{ThreadPoolFutureAdatper.rst => ThreadPoolFutureAdapter.rst} (100%) diff --git a/docs/reference/graph-adapters/ThreadPoolFutureAdatper.rst b/docs/reference/graph-adapters/ThreadPoolFutureAdapter.rst similarity index 100% rename from docs/reference/graph-adapters/ThreadPoolFutureAdatper.rst rename to docs/reference/graph-adapters/ThreadPoolFutureAdapter.rst From cecc05081216cbe56df82ea659844f75b700d5cb Mon Sep 17 00:00:00 2001 From: Stefan Krawczyk Date: Wed, 1 Jan 2025 21:45:48 -0800 Subject: [PATCH 4/6] Fixes example image --- .../lazy_threadpool_execution/README.md | 5 +++++ .../lazy_threadpool_execution/my_functions.png | Bin 0 -> 31958 bytes .../my_functions_async.png | Bin 0 -> 31958 bytes .../lazy_threadpool_execution/my_funtions.png | Bin 33036 -> 0 bytes .../lazy_threadpool_execution/run.py | 2 +- 5 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 examples/parallelism/lazy_threadpool_execution/my_functions.png create mode 100644 examples/parallelism/lazy_threadpool_execution/my_functions_async.png delete mode 100644 examples/parallelism/lazy_threadpool_execution/my_funtions.png diff --git a/examples/parallelism/lazy_threadpool_execution/README.md b/examples/parallelism/lazy_threadpool_execution/README.md index 518e3f1fd..f82b70f33 100644 --- a/examples/parallelism/lazy_threadpool_execution/README.md +++ b/examples/parallelism/lazy_threadpool_execution/README.md @@ -12,6 +12,11 @@ HTTP requests, reading/writing to disk, LLM API calls, etc. > Note: this adapter does not support DAGs with Parallelizable and Collect functions; create an issue if you need this feature. +![DAG](my_functions.png) + +The above image shows the DAG that will be executed. You can see from the structure +that the DAG can be parallelized, i.e. the left most nodes can be executed in parallel. + When you execute `run.py`, you will output that shows: 1. The DAG running in parallel -- check the image against what is printed. diff --git a/examples/parallelism/lazy_threadpool_execution/my_functions.png b/examples/parallelism/lazy_threadpool_execution/my_functions.png new file mode 100644 index 0000000000000000000000000000000000000000..cf4bd1927fa28b0608d29a6cac72312f62a1baa8 GIT binary patch literal 31958 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 RgpJBEWF!>Ci%?JB{x6Z1rS<>- literal 0 HcmV?d00001 diff --git a/examples/parallelism/lazy_threadpool_execution/my_functions_async.png b/examples/parallelism/lazy_threadpool_execution/my_functions_async.png new file mode 100644 index 0000000000000000000000000000000000000000..cf4bd1927fa28b0608d29a6cac72312f62a1baa8 GIT binary patch literal 31958 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 RgpJBEWF!>Ci%?JB{x6Z1rS<>- literal 0 HcmV?d00001 diff --git a/examples/parallelism/lazy_threadpool_execution/my_funtions.png b/examples/parallelism/lazy_threadpool_execution/my_funtions.png deleted file mode 100644 index fb0b5cc0860e076e3b3019a67b64f83abbbd601b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33036 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

myp;UP!O^NCp>Du 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 diff --git a/examples/parallelism/lazy_threadpool_execution/run.py b/examples/parallelism/lazy_threadpool_execution/run.py index 6d995b904..734842c39 100644 --- a/examples/parallelism/lazy_threadpool_execution/run.py +++ b/examples/parallelism/lazy_threadpool_execution/run.py @@ -8,7 +8,7 @@ start = time.time() adapter = h_threadpool.FutureAdapter() dr = driver.Builder().with_modules(my_functions).with_adapters(adapter).build() -dr.display_all_functions("my_funtions.png") +dr.display_all_functions("my_functions.png") r = dr.execute(["s", "x", "a"]) print("got return from dr") print(r) From 4ac52f18964d6f44b97079320e836203e300d945 Mon Sep 17 00:00:00 2001 From: Stefan Krawczyk Date: Wed, 1 Jan 2025 21:47:20 -0800 Subject: [PATCH 5/6] adds requirements.txt --- .../parallelism/lazy_threadpool_execution/requirements.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 examples/parallelism/lazy_threadpool_execution/requirements.txt diff --git a/examples/parallelism/lazy_threadpool_execution/requirements.txt b/examples/parallelism/lazy_threadpool_execution/requirements.txt new file mode 100644 index 000000000..0bd997a6e --- /dev/null +++ b/examples/parallelism/lazy_threadpool_execution/requirements.txt @@ -0,0 +1,3 @@ +sf-hamilton-sdk # optional +sf-hamilton-ui # optional +sf-hamilton[visualization] From 4f136d40d88c0f9c06acdb41b7a11da689543f3e Mon Sep 17 00:00:00 2001 From: Stefan Krawczyk Date: Thu, 2 Jan 2025 09:46:19 -0800 Subject: [PATCH 6/6] Adds wrapping a result builder So that people can adjust the result accordingly. --- hamilton/plugins/h_threadpool.py | 31 +++++++++++++++- tests/plugins/test_h_threadpool.py | 59 ++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 2 deletions(-) diff --git a/hamilton/plugins/h_threadpool.py b/hamilton/plugins/h_threadpool.py index 644c41fcf..7f275095e 100644 --- a/hamilton/plugins/h_threadpool.py +++ b/hamilton/plugins/h_threadpool.py @@ -1,5 +1,5 @@ from concurrent.futures import Future, ThreadPoolExecutor -from typing import Any, Callable, Dict +from typing import Any, Callable, Dict, List, Type from hamilton import registry @@ -45,14 +45,39 @@ class FutureAdapter(base.BaseDoRemoteExecute, lifecycle.ResultBuilder): """ - def __init__(self, max_workers: int = None, thread_name_prefix: str = ""): + def __init__( + self, + max_workers: int = None, + thread_name_prefix: str = "", + result_builder: lifecycle.ResultBuilder = None, + ): """Constructor. :param max_workers: The maximum number of threads that can be used to execute the given calls. :param thread_name_prefix: An optional name prefix to give our threads. + :param result_builder: Optional. Result builder to use for building the result. """ self.executor = ThreadPoolExecutor( max_workers=max_workers, thread_name_prefix=thread_name_prefix ) + self.result_builder = result_builder + + def input_types(self) -> List[Type[Type]]: + """Gives the applicable types to this result builder. + This is optional for backwards compatibility, but is recommended. + + :return: A list of types that this can apply to. + """ + # since this wraps a potential result builder, expose the input types of the wrapped + # result builder doesn't make sense. + return [Any] + + def output_type(self) -> Type: + """Returns the output type of this result builder + :return: the type that this creates + """ + if self.result_builder: + return self.result_builder.output_type() + return Any def do_remote_execute( self, @@ -81,4 +106,6 @@ def build_result(self, **outputs: Any) -> Any: for k, v in outputs.items(): if isinstance(v, Future): outputs[k] = v.result() + if self.result_builder: + return self.result_builder.build_result(**outputs) return outputs diff --git a/tests/plugins/test_h_threadpool.py b/tests/plugins/test_h_threadpool.py index ead9f9a3a..a92c8ca33 100644 --- a/tests/plugins/test_h_threadpool.py +++ b/tests/plugins/test_h_threadpool.py @@ -1,5 +1,7 @@ from concurrent.futures import Future +from typing import Any +from hamilton import lifecycle from hamilton.plugins.h_threadpool import FutureAdapter, _new_fn @@ -58,3 +60,60 @@ def test_future_adapter_build_result(): result = adapter.build_result(a=future_a, b=future_b) assert result == {"a": 1, "b": 2} + + +def test_future_adapter_input_types(): + adapter = FutureAdapter() + assert adapter.input_types() == [Any] + + +def test_future_adapter_output_type(): + adapter = FutureAdapter() + assert adapter.output_type() == Any + + +def test_future_adapter_input_types_with_result_builder(): + """Tests that we ignore exposing the input types of the wrapped result builder.""" + + class MockResultBuilder(lifecycle.ResultBuilder): + def build_result(self, **outputs: Any) -> Any: + pass + + def input_types(self): + return [int, str] + + adapter = FutureAdapter(result_builder=MockResultBuilder()) + assert adapter.input_types() == [Any] + + +def test_future_adapter_output_type_with_result_builder(): + class MockResultBuilder(lifecycle.ResultBuilder): + def build_result(self, **outputs: Any) -> Any: + pass + + def output_type(self): + return dict + + adapter = FutureAdapter(result_builder=MockResultBuilder()) + assert adapter.output_type() == dict + + +def test_future_adapter_build_result_with_result_builder(): + class MockResultBuilder(lifecycle.ResultBuilder): + def build_result(self, **outputs): + return sum(outputs.values()) + + def input_types(self): + return [int] + + def output_type(self): + return int + + adapter = FutureAdapter(result_builder=MockResultBuilder()) + future_a = Future() + future_b = Future() + future_a.set_result(1) + future_b.set_result(2) + + result = adapter.build_result(a=future_a, b=future_b) + assert result == 3