fal.distributed. We focus on the methods you’ll actually use in your code.
DistributedRunner
TheDistributedRunner class orchestrates multiple GPU workers for distributed computation. It handles process management, inter-process communication via ZMQ, and coordination between worker processes.
Constructor
worker_cls(type[DistributedWorker]): Your custom worker class that inherits fromDistributedWorker.world_size(int): Total number of worker processes to spawn (typically equalsnum_gpus).
start()
Starts all distributed worker processes and initializes them.timeout(int): Maximum time (in seconds) to wait for all workers to be ready. Default:1800(30 minutes).**kwargs: Additional keyword arguments passed to each worker’ssetup()method.
RuntimeError: If processes are already running or fail to start.TimeoutError: If workers don’t become ready within the timeout period.
- Spawns
world_sizeworker processes (one per GPU) - Each worker runs its
setup()method with the provided**kwargs - Waits for all workers to signal “READY”
- Starts the keepalive timer if configured
- Returns when all workers are initialized and ready
This method must be called before using
invoke() or stream(). It’s typically called once in your app’s setup() method.invoke()
Executes the worker’s__call__() method across all GPUs and returns the final result from rank 0.
payload(dict[str, Any]): Dictionary of arguments to pass to each worker’s__call__()method. Default:{}.timeout(int | None): Maximum time (in seconds) to wait for the result. IfNone, uses the runner’s default timeout. Default:None.
Any: The result returned by rank 0 worker’s__call__()method.
RuntimeError: If workers are not running or encounter an error during execution.TimeoutError: If the operation exceeds the timeout.
- Serializes the payload and sends it to all workers
- Each worker executes its
__call__()method withstreaming=False - Workers coordinate using PyTorch distributed operations (e.g.,
dist.gather()) - Only rank 0 returns the result
- Result is deserialized and returned to the caller
stream()
Streams intermediate results from workers during execution, useful for long-running operations like image generation or training.payload(dict[str, Any]): Dictionary of arguments to pass to each worker’s__call__()method. Default:{}.timeout(int | None): Maximum total time (in seconds) for the entire operation. Default:None(no limit).streaming_timeout(int | None): Maximum time (in seconds) between consecutive yields. If no data is received within this period, raisesTimeoutError. Default:None.as_text_events(bool): IfTrue, yields Server-Sent Events (SSE) formatted as bytes. IfFalse, yields deserialized Python objects. Default:False.
AsyncIterator[Any]: Async iterator yielding intermediate results and the final result.
RuntimeError: If workers are not running, encounter an error, or yield no data.TimeoutError: If the operation exceeds timeout or streaming_timeout.
- Serializes the payload and sends it to all workers
- Each worker executes its
__call__()method withstreaming=True - Workers can call
self.add_streaming_result()to send intermediate updates - The runner yields each intermediate result as it’s received
- After workers finish, yields the final result
- Automatically handles serialization based on
as_text_events
DistributedWorker
TheDistributedWorker class is the base class for your custom GPU workers. Each instance runs on a separate GPU and handles model loading, inference, or training.
Create your own worker by inheriting from DistributedWorker and overriding the setup() and __call__() methods.
Properties
device
Returns the CUDA device assigned to this worker.torch.device: The PyTorch device for this worker, e.g.,cuda:0,cuda:1, etc.
rank
The rank (ID) of this worker, from 0 to world_size-1.world_size
Total number of workers in the distributed setup.Methods to Override
setup()
Called once when the worker is initialized. Use this to load models, download weights, and prepare resources.**kwargs: Any keyword arguments passed torunner.start().
call()
Called for each request. Implement your main processing logic here.streaming(bool):Trueif called viarunner.stream(),Falseif called viarunner.invoke().**kwargs: Arguments from thepayloaddict passed torunner.invoke()orrunner.stream().
Any: The result to return. Only rank 0’s return value is sent back to the caller.
Utility Methods
add_streaming_result()
Sends an intermediate result to the client during streaming.result(Any): The data to stream. Can be a dict, PIL image, or any serializable object.image_format(str): Image format for PIL images ("jpeg"or"png"). Default:"jpeg".as_text_event(bool): IfTrue, formats as Server-Sent Event. Must match theas_text_eventsparameter inrunner.stream(). Default:False.
Only call
add_streaming_result() from rank 0 to avoid duplicate messages to the client.rank_print()
Prints a message with the worker’s rank prefix for easy debugging.message(str): The message to print.debug(bool): IfTrue, prefixes with[debug]. Default:False.
Common Patterns
Pattern 1: Data Parallelism (Inference)
Each GPU processes different data independently:Pattern 2: Distributed Data Parallel (Training)
All GPUs have the same model, process different batches, and sync gradients:Pattern 3: Streaming with Progress Updates
Stream intermediate results during long-running operations:Next Steps
Multi-GPU Inference Tutorial
Complete example with data parallelism
Multi-GPU Training Tutorial
Complete example with DDP training
Event Streaming
Learn about streaming intermediate results
Overview
High-level overview of multi-GPU workloads