Documentation Index
Fetch the complete documentation index at: https://mintlify.com/modal-labs/modal-client/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Modal provides powerful primitives for parallel execution, allowing you to process large datasets efficiently by distributing work across multiple containers.
Parallel map
The map() method allows you to apply a function to multiple inputs in parallel. Modal automatically manages the distribution of work across containers.
Basic usage
import modal
app = modal.App()
@app.function()
def square(x):
return x ** 2
@app.local_entrypoint()
def main():
# Process inputs in parallel
results = list(square.map([1, 2, 3, 4]))
print(results) # [1, 4, 9, 16]
Multiple arguments
You can pass multiple iterators to map(), one for each argument:
@app.function()
def multiply(a, b):
return a * b
@app.local_entrypoint()
def main():
# Each iterator provides values for one argument
results = list(multiply.map([1, 2, 3], [10, 20, 30]))
print(results) # [10, 40, 90]
Ordering outputs
By default, map() returns results in the same order as inputs. Set order_outputs=False to get results as they complete:
@app.local_entrypoint()
def main():
# Get results in completion order (potentially faster)
for result in multiply.map([1, 2, 3], [10, 20, 30], order_outputs=False):
print(result)
Setting order_outputs=False can improve throughput when some inputs take longer to process than others.
Exception handling
Use return_exceptions=True to collect exceptions instead of raising them:
@app.function()
def divide(a, b):
if b == 0:
raise ValueError("Cannot divide by zero")
return a / b
@app.local_entrypoint()
def main():
results = list(divide.map([10, 20, 30], [2, 0, 5], return_exceptions=True))
# Results include both values and exceptions
for i, result in enumerate(results):
if isinstance(result, Exception):
print(f"Input {i} failed: {result}")
else:
print(f"Input {i} result: {result}")
When using return_exceptions=True, exceptions are returned as UserCodeException objects that wrap the original exception.
Starmap
The starmap() method unpacks each item from a sequence as arguments:
@app.function()
def add(a, b, c):
return a + b + c
@app.local_entrypoint()
def main():
# Each tuple is unpacked as arguments
inputs = [(1, 2, 3), (4, 5, 6), (7, 8, 9)]
results = list(add.starmap(inputs))
print(results) # [6, 15, 24]
For each
When you only need to execute a function for side effects and don’t care about the results:
@app.function()
def process_data(item):
# Do some work, no return value needed
print(f"Processing {item}")
@app.local_entrypoint()
def main():
# Waits for all executions to complete
process_data.for_each([1, 2, 3, 4, 5])
Spawn map
The spawn_map() method starts parallel execution but returns immediately without waiting for results:
@app.function()
def expensive_computation(x):
import time
time.sleep(10)
return x ** 2
@app.local_entrypoint()
def main():
# Start processing and return immediately
expensive_computation.spawn_map([1, 2, 3, 4])
print("Computation started in background")
With spawn_map(), you cannot currently retrieve results programmatically. Use map() if you need to process outputs.
Async usage
All map operations support async/await syntax:
@app.function()
async def async_process(x):
await asyncio.sleep(0.1)
return x * 2
@app.local_entrypoint()
async def main():
# Use async iteration
results = []
async for result in async_process.map.aio([1, 2, 3, 4]):
results.append(result)
print(f"Got result: {result}")
Modal’s map operations are designed to handle large-scale workloads efficiently:
- Chunking: Inputs are sent to the server in chunks (49 inputs per request by default)
- Backpressure: The system automatically throttles input creation if the server is overwhelmed
- Retry logic: Failed inputs are automatically retried with exponential backoff
Modal provides debug logging for map operations:
import logging
logging.basicConfig(level=logging.DEBUG)
Adjust container scaling to control parallelism:
@app.function(concurrency_limit=100)
def process(x):
return x ** 2
For external APIs with rate limits, consider adding delays or using fewer containers.
Best practices
- Use ordered outputs sparingly: Only set
order_outputs=True when you need deterministic ordering
- Handle exceptions gracefully: Use
return_exceptions=True for robust error handling
- Monitor resource usage: Watch container counts and adjust
concurrency_limit as needed
- Batch related work: Group related operations to minimize overhead