技能创建器
skill-creator
创建、改进和评估自定义技能,把专业流程转化为可重复使用的 AI 能力。
dask-optimization【数据与研究】Advanced sub-skill for Dask focused on distributed system performance, memory management, and task graph optimization. Covers cluster tuning, efficient serialization, data skew mitigation, and dashboard-driven debugging.
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
Parallel computing is not "free". In a distributed environment, the cost of moving data (network I/O) and scheduling tasks can often exceed the computation time. This guide focuses on minimizing overhead and maximizing throughput.
dask.delayed or Futures.client.scatter, dask.compute(optimize_graph=True), repartition, client.restartThe fastest distributed task is the one that doesn't need data from another machine. Aim for data locality.
Every object sent to a worker must be serialized (pickled). Large Python objects (like complex dicts) passed as arguments can slow down the cluster significantly.
from dask.distributed import Client, performance_report
client = Client("tcp://scheduler-address:8786")
# Generate a detailed HTML report of the computation
with performance_report(filename="dask-report.html"):
result = big_computation.compute()
# Tip: Check the "Task Stream" for gaps. Gaps mean workers are idle
# waiting for the scheduler or network.
persist() it in memory to avoid re-calculating the entire graph twice.delayed(func)(large_df), use large_future = client.scatter(large_df) then delayed(func)(large_future).repartition or rechunk.# ❌ BAD: Large object sent to every task (High Overhead)
large_lookup = load_heavy_dict()
results = [delayed(process)(x, large_lookup) for x in data]
# ✅ GOOD: Scatter once, use reference
[large_future] = client.scatter([large_lookup], broadcast=True)
results = [delayed(process)(x, large_future) for x in data]
Worker memory has thresholds:
# Adjusting worker limits via config (distributed.yaml or code)
import dask
dask.config.set({"distributed.worker.memory.target": 0.45})
dask.config.set({"distributed.worker.memory.spill": 0.55})
Dask automatically "fuses" many operations into one task to reduce scheduler overhead.
# Multiple operations on a DataArray
da = da + 1
da = da * 2
da = da.sum()
# When computing, Dask optimizes the graph
# You can inspect it:
da.visualize(filename='graph.pdf', optimize_graph=True)
Joins are expensive because they require moving data between workers (shuffling).
def optimized_join(left_ddf, right_ddf):
# 1. Ensure right side is small enough to be broadcasted
# or ensure both are partitioned by the join key.
# If one DF is small (e.g. < 100MB)
# result = left_ddf.map_partitions(lambda df: df.merge(small_df_local, on='key'))
# 2. If both are large, set the index first (triggers a shuffle once)
left_ddf = left_ddf.set_index('key')
right_ddf = right_ddf.set_index('key')
# 3. Subsequent joins will be "locally aligned" (Zero communication)
return left_ddf.merge(right_ddf, left_index=True, right_index=True)
def fast_save(ddf, path):
# 1. Categorical columns save massive space
ddf = ddf.categorize()
# 2. Write with efficient compression
# 'snappy' is usually the best balance for speed
ddf.to_parquet(path, engine='pyarrow', compression='snappy',
write_metadata_file=True)
# Prevent a "dirty" cluster from slowing down
def clean_cluster_state():
client.cancel(list(client.futures)) # Clear all references
client.restart() # Hard reset all workers
import gc
gc.collect() # Local cleanup
Tell Dask to run specific tasks only on specific workers (e.g., those with a GPU).
# Start worker with: dask-worker ... --resources "GPU=1"
# Submit task requesting resource
future = client.submit(my_gpu_function, data, resources={'GPU': 1})
If your task requires a library that isn't installed on the workers, the task will fail repeatedly.
# ✅ Solution: Use pip_install or conda_install via client
from dask.distributed import PipInstall
client.register_plugin(PipInstall(packages=["scikit-learn"]))
Python's garbage collector isn't immediate. Sometimes workers appear full because "Unmanaged Memory" hasn't been freed.
# ✅ Solution: Manually trigger GC on workers
def worker_gc():
import gc
return gc.collect()
client.run(worker_gc)
If you have 10,000 partitions for a 1GB dataset, you'll spend more time scheduling than calculating.
# ✅ Solution: Repartition to fewer pieces
ddf = ddf.repartition(npartitions=20)
Dask Optimization is the art of balancing resources. By understanding the flow of data through the network and the mechanics of worker memory, you can scale Python logic to planetary-scale datasets with industrial reliability.