dask-setup 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dask_setup/__init__.py +101 -0
- dask_setup/cli.py +344 -0
- dask_setup/client.py +280 -0
- dask_setup/cluster.py +174 -0
- dask_setup/config.py +404 -0
- dask_setup/config_manager.py +343 -0
- dask_setup/dashboard.py +71 -0
- dask_setup/error_handling.py +588 -0
- dask_setup/exceptions.py +46 -0
- dask_setup/io_patterns.py +738 -0
- dask_setup/legacy.py +191 -0
- dask_setup/logging.py +296 -0
- dask_setup/resources.py +302 -0
- dask_setup/tempdir.py +111 -0
- dask_setup/topology.py +118 -0
- dask_setup/types.py +51 -0
- dask_setup/xarray.py +636 -0
- dask_setup-1.0.0.dist-info/METADATA +817 -0
- dask_setup-1.0.0.dist-info/RECORD +23 -0
- dask_setup-1.0.0.dist-info/WHEEL +5 -0
- dask_setup-1.0.0.dist-info/entry_points.txt +2 -0
- dask_setup-1.0.0.dist-info/licenses/LICENSE +201 -0
- dask_setup-1.0.0.dist-info/top_level.txt +1 -0
dask_setup/client.py
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
"""Main client setup orchestration for dask_setup."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dask.distributed import Client, LocalCluster
|
|
6
|
+
|
|
7
|
+
from .cluster import calculate_memory_spec, create_cluster
|
|
8
|
+
from .config import DaskSetupConfig
|
|
9
|
+
from .config_manager import ConfigManager
|
|
10
|
+
from .dashboard import print_dashboard_info
|
|
11
|
+
from .exceptions import InsufficientResourcesError
|
|
12
|
+
from .resources import detect_resources
|
|
13
|
+
from .tempdir import create_dask_temp_dir
|
|
14
|
+
from .topology import decide_topology, validate_topology
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _resolve_configuration(
|
|
18
|
+
profile: str | None = None,
|
|
19
|
+
workload_type: str = "io",
|
|
20
|
+
max_workers: int | None = None,
|
|
21
|
+
reserve_mem_gb: float = 50.0,
|
|
22
|
+
max_mem_gb: float | None = None,
|
|
23
|
+
dashboard: bool = True,
|
|
24
|
+
adaptive: bool = False,
|
|
25
|
+
min_workers: int | None = None,
|
|
26
|
+
suggest_chunks: bool = False,
|
|
27
|
+
) -> DaskSetupConfig:
|
|
28
|
+
"""Resolve final configuration from profile and explicit parameters.
|
|
29
|
+
|
|
30
|
+
Priority order (highest to lowest):
|
|
31
|
+
1. Explicit parameters passed to setup_dask_client()
|
|
32
|
+
2. Profile configuration (if specified)
|
|
33
|
+
3. Default values
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
profile: Profile name to load
|
|
37
|
+
**kwargs: Explicit parameters from setup_dask_client()
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
Resolved DaskSetupConfig
|
|
41
|
+
"""
|
|
42
|
+
# Start with defaults
|
|
43
|
+
defaults = DaskSetupConfig()
|
|
44
|
+
|
|
45
|
+
# Load profile if specified
|
|
46
|
+
profile_config = None
|
|
47
|
+
if profile:
|
|
48
|
+
manager = ConfigManager()
|
|
49
|
+
profile_obj = manager.get_profile(profile)
|
|
50
|
+
if profile_obj is None:
|
|
51
|
+
available = list(manager.list_profiles().keys())
|
|
52
|
+
raise ValueError(f"Profile '{profile}' not found. Available profiles: {available}")
|
|
53
|
+
profile_config = profile_obj.config
|
|
54
|
+
|
|
55
|
+
# Create explicit config from parameters (only non-default values)
|
|
56
|
+
explicit_params = {}
|
|
57
|
+
|
|
58
|
+
# Only include parameters that were explicitly passed (not defaults)
|
|
59
|
+
# We'll use a simple heuristic - if it matches the default, assume it wasn't set
|
|
60
|
+
if workload_type != "io":
|
|
61
|
+
explicit_params["workload_type"] = workload_type
|
|
62
|
+
if max_workers is not None:
|
|
63
|
+
explicit_params["max_workers"] = max_workers
|
|
64
|
+
if reserve_mem_gb != 50.0:
|
|
65
|
+
explicit_params["reserve_mem_gb"] = reserve_mem_gb
|
|
66
|
+
if max_mem_gb is not None:
|
|
67
|
+
explicit_params["max_mem_gb"] = max_mem_gb
|
|
68
|
+
if dashboard is not True:
|
|
69
|
+
explicit_params["dashboard"] = dashboard
|
|
70
|
+
if adaptive is not False:
|
|
71
|
+
explicit_params["adaptive"] = adaptive
|
|
72
|
+
if min_workers is not None:
|
|
73
|
+
explicit_params["min_workers"] = min_workers
|
|
74
|
+
if suggest_chunks is not False:
|
|
75
|
+
explicit_params["suggest_chunks"] = suggest_chunks
|
|
76
|
+
|
|
77
|
+
explicit_config = DaskSetupConfig(**explicit_params) if explicit_params else None
|
|
78
|
+
|
|
79
|
+
# Merge configurations: defaults < profile < explicit
|
|
80
|
+
final_config = defaults
|
|
81
|
+
if profile_config:
|
|
82
|
+
final_config = final_config.merge_with(profile_config)
|
|
83
|
+
if explicit_config:
|
|
84
|
+
final_config = final_config.merge_with(explicit_config)
|
|
85
|
+
|
|
86
|
+
return final_config
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def setup_dask_client(
|
|
90
|
+
workload_type: str = "io",
|
|
91
|
+
max_workers: int | None = None,
|
|
92
|
+
reserve_mem_gb: float = 50.0,
|
|
93
|
+
max_mem_gb: float | None = None,
|
|
94
|
+
dashboard: bool = True,
|
|
95
|
+
adaptive: bool = False,
|
|
96
|
+
min_workers: int | None = None,
|
|
97
|
+
profile: str | None = None,
|
|
98
|
+
suggest_chunks: bool = False,
|
|
99
|
+
) -> tuple[Client, LocalCluster, str]:
|
|
100
|
+
"""Create a single-node Dask LocalCluster tuned for HPC login/compute nodes.
|
|
101
|
+
|
|
102
|
+
Routes temp/spill to $PBS_JOBFS when present.
|
|
103
|
+
|
|
104
|
+
Parameters
|
|
105
|
+
----------
|
|
106
|
+
workload_type : {"cpu","io","mixed"}
|
|
107
|
+
Shape worker topology for CPU-bound, I/O-bound, or mixed workloads.
|
|
108
|
+
max_workers : int or None
|
|
109
|
+
Cap on worker processes. Defaults to all logical cores available.
|
|
110
|
+
reserve_mem_gb : float
|
|
111
|
+
Memory to reserve for OS / cache / filesystem (GiB).
|
|
112
|
+
max_mem_gb : float or None
|
|
113
|
+
Cap total memory used by Dask. Default is node total.
|
|
114
|
+
dashboard : bool
|
|
115
|
+
If True, start a dashboard on a random free port and print an SSH tunnel hint.
|
|
116
|
+
adaptive : bool
|
|
117
|
+
Enable single-node adaptive scaling (elastic number of workers).
|
|
118
|
+
min_workers : int or None
|
|
119
|
+
Minimum workers when adaptive=True.
|
|
120
|
+
profile : str or None
|
|
121
|
+
Name of configuration profile to use. Profile settings are overridden by
|
|
122
|
+
explicit parameters.
|
|
123
|
+
suggest_chunks : bool
|
|
124
|
+
If True, print xarray chunking recommendations after cluster setup.
|
|
125
|
+
Requires xarray and numpy to be installed.
|
|
126
|
+
|
|
127
|
+
Returns
|
|
128
|
+
-------
|
|
129
|
+
client : dask.distributed.Client
|
|
130
|
+
cluster : dask.distributed.LocalCluster
|
|
131
|
+
dask_local_dir : str
|
|
132
|
+
Absolute path to the temp/spill directory (under $PBS_JOBFS if available).
|
|
133
|
+
|
|
134
|
+
Raises
|
|
135
|
+
------
|
|
136
|
+
InvalidConfigurationError
|
|
137
|
+
If workload_type is invalid or parameters are inconsistent.
|
|
138
|
+
InsufficientResourcesError
|
|
139
|
+
If system resources are insufficient for the requested configuration.
|
|
140
|
+
ResourceDetectionError
|
|
141
|
+
If resource detection fails completely.
|
|
142
|
+
"""
|
|
143
|
+
# Load and merge configuration
|
|
144
|
+
config = _resolve_configuration(
|
|
145
|
+
profile=profile,
|
|
146
|
+
workload_type=workload_type,
|
|
147
|
+
max_workers=max_workers,
|
|
148
|
+
reserve_mem_gb=reserve_mem_gb,
|
|
149
|
+
max_mem_gb=max_mem_gb,
|
|
150
|
+
dashboard=dashboard,
|
|
151
|
+
adaptive=adaptive,
|
|
152
|
+
min_workers=min_workers,
|
|
153
|
+
suggest_chunks=suggest_chunks,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
# Detect system resources
|
|
157
|
+
resources = detect_resources()
|
|
158
|
+
|
|
159
|
+
# Create temporary directory for spill files (use config for base dir if specified)
|
|
160
|
+
temp_dir = create_dask_temp_dir(base_dir=config.temp_base_dir)
|
|
161
|
+
|
|
162
|
+
# Decide worker topology based on workload type
|
|
163
|
+
topology = decide_topology(
|
|
164
|
+
workload_type=config.workload_type,
|
|
165
|
+
total_cores=resources.total_cores,
|
|
166
|
+
max_workers=config.max_workers,
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
# Validate topology makes sense
|
|
170
|
+
validate_topology(topology, resources.total_cores)
|
|
171
|
+
|
|
172
|
+
# Calculate memory allocation
|
|
173
|
+
try:
|
|
174
|
+
memory_spec = calculate_memory_spec(
|
|
175
|
+
total_mem_bytes=resources.total_mem_bytes,
|
|
176
|
+
n_workers=topology.n_workers,
|
|
177
|
+
reserve_mem_gb=config.reserve_mem_gb,
|
|
178
|
+
max_mem_gb=config.max_mem_gb,
|
|
179
|
+
)
|
|
180
|
+
except ValueError as e:
|
|
181
|
+
# Extract memory values for better error reporting
|
|
182
|
+
total_gib = resources.total_mem_bytes / (1024**3)
|
|
183
|
+
available_gb = total_gib - config.reserve_mem_gb
|
|
184
|
+
required_gb = topology.n_workers * 1.0 # Rough estimate: 1 GB per worker minimum
|
|
185
|
+
|
|
186
|
+
# Generate suggested actions based on the configuration
|
|
187
|
+
suggestions = []
|
|
188
|
+
if config.reserve_mem_gb > available_gb / 2: # Reserve more than half of available
|
|
189
|
+
suggestions.append(
|
|
190
|
+
f"Reduce reserve_mem_gb from {config.reserve_mem_gb:.1f} GB to {available_gb * 0.3:.1f} GB"
|
|
191
|
+
)
|
|
192
|
+
if topology.n_workers > 1:
|
|
193
|
+
suggestions.append(
|
|
194
|
+
f"Limit max_workers to 1 or 2 workers instead of {topology.n_workers}"
|
|
195
|
+
)
|
|
196
|
+
if not suggestions: # Fallback suggestions
|
|
197
|
+
suggestions = [
|
|
198
|
+
"Close other applications to free up memory",
|
|
199
|
+
"Request a larger memory allocation for your job",
|
|
200
|
+
]
|
|
201
|
+
|
|
202
|
+
raise InsufficientResourcesError(
|
|
203
|
+
required_mem=required_gb, available_mem=available_gb, suggested_actions=suggestions
|
|
204
|
+
) from e
|
|
205
|
+
|
|
206
|
+
# Create the cluster
|
|
207
|
+
dashboard_address = ":0" if config.dashboard else None
|
|
208
|
+
if config.dashboard and config.dashboard_port:
|
|
209
|
+
dashboard_address = f":{config.dashboard_port}"
|
|
210
|
+
|
|
211
|
+
cluster = create_cluster(
|
|
212
|
+
topology=topology,
|
|
213
|
+
memory_spec=memory_spec,
|
|
214
|
+
temp_dir=temp_dir,
|
|
215
|
+
dashboard_address=dashboard_address,
|
|
216
|
+
adaptive=config.adaptive,
|
|
217
|
+
min_workers=config.min_workers,
|
|
218
|
+
memory_target=config.memory_target,
|
|
219
|
+
memory_spill=config.memory_spill,
|
|
220
|
+
memory_pause=config.memory_pause,
|
|
221
|
+
memory_terminate=config.memory_terminate,
|
|
222
|
+
spill_compression=config.spill_compression,
|
|
223
|
+
comm_compression=config.comm_compression,
|
|
224
|
+
spill_threads=config.spill_threads,
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
# Connect client
|
|
228
|
+
client = Client(cluster)
|
|
229
|
+
|
|
230
|
+
# Print dashboard info if enabled
|
|
231
|
+
if config.dashboard:
|
|
232
|
+
print_dashboard_info(client, silent=config.silence_logs)
|
|
233
|
+
if not config.silence_logs:
|
|
234
|
+
print() # Add blank line
|
|
235
|
+
|
|
236
|
+
# Print summary information
|
|
237
|
+
spill_threads_str = (
|
|
238
|
+
f" | spill_threads={config.spill_threads}" if config.spill_threads is not None else ""
|
|
239
|
+
)
|
|
240
|
+
print(
|
|
241
|
+
f"[setup_dask_client] temp/spill dir: {temp_dir}\\n"
|
|
242
|
+
f"Workers: {topology.n_workers} | threads/worker: {topology.threads_per_worker} | processes: {topology.processes}\\n"
|
|
243
|
+
f"Mem: total ~{memory_spec.total_mem_gib:.1f} GiB | usable ~{memory_spec.usable_mem_gb:.1f} GiB | per-worker ~{memory_spec.mem_per_worker_bytes / (1024**3):.1f} GiB\\n"
|
|
244
|
+
f"Compression: spill={config.spill_compression} | comm={config.comm_compression}{spill_threads_str}"
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
# Print xarray chunking suggestions if enabled
|
|
248
|
+
if config.suggest_chunks:
|
|
249
|
+
try:
|
|
250
|
+
# Try to import xarray module to check availability
|
|
251
|
+
|
|
252
|
+
print("\n" + "=" * 60)
|
|
253
|
+
print(" Xarray Chunking Recommendations")
|
|
254
|
+
print("=" * 60)
|
|
255
|
+
print(
|
|
256
|
+
"To get optimal chunking suggestions for your xarray datasets:\n"
|
|
257
|
+
"\n"
|
|
258
|
+
" from dask_setup import recommend_chunks\n"
|
|
259
|
+
" chunks = recommend_chunks(your_dataset, client, verbose=True)\n"
|
|
260
|
+
" ds_optimized = your_dataset.chunk(chunks)\n"
|
|
261
|
+
"\n"
|
|
262
|
+
"Or use the standalone function:\n"
|
|
263
|
+
"\n"
|
|
264
|
+
" chunks = recommend_chunks(ds, workload_type='cpu') # or 'io', 'mixed'\n"
|
|
265
|
+
"\n"
|
|
266
|
+
f"Based on your current cluster configuration:\n"
|
|
267
|
+
f"• Workload type: {config.workload_type}\n"
|
|
268
|
+
f"• Target chunk size: 256-512 MiB per chunk\n"
|
|
269
|
+
f"• Safety factor: 60% of worker memory ({memory_spec.mem_per_worker_bytes / (1024**3) * 0.6:.1f} GiB max per chunk)\n"
|
|
270
|
+
f"• {topology.n_workers} workers available for parallelization\n"
|
|
271
|
+
)
|
|
272
|
+
print("=" * 60)
|
|
273
|
+
|
|
274
|
+
except ImportError:
|
|
275
|
+
print(
|
|
276
|
+
"\n Xarray integration requires xarray and numpy to be installed.\n"
|
|
277
|
+
"Install with: pip install xarray numpy"
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
return client, cluster, str(temp_dir)
|
dask_setup/cluster.py
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"""Dask cluster creation and configuration."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import dask
|
|
9
|
+
from dask.distributed import LocalCluster
|
|
10
|
+
|
|
11
|
+
from .types import MemorySpec, TopologySpec
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def calculate_memory_spec(
|
|
15
|
+
total_mem_bytes: int,
|
|
16
|
+
n_workers: int,
|
|
17
|
+
reserve_mem_gb: float = 50.0,
|
|
18
|
+
max_mem_gb: float | None = None,
|
|
19
|
+
) -> MemorySpec:
|
|
20
|
+
"""Calculate memory allocation for Dask workers.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
total_mem_bytes: Total system memory in bytes
|
|
24
|
+
n_workers: Number of workers that will be created
|
|
25
|
+
reserve_mem_gb: Memory to reserve for system in GiB
|
|
26
|
+
max_mem_gb: Optional cap on total memory usage in GiB
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
MemorySpec with calculated memory allocation
|
|
30
|
+
|
|
31
|
+
Raises:
|
|
32
|
+
ValueError: If insufficient memory is available
|
|
33
|
+
"""
|
|
34
|
+
total_mem_gib = total_mem_bytes / (1024**3)
|
|
35
|
+
|
|
36
|
+
# Apply max_mem_gb cap if specified
|
|
37
|
+
effective_total_gb = min(max_mem_gb or total_mem_gib, total_mem_gib)
|
|
38
|
+
|
|
39
|
+
# Calculate usable memory after reservation
|
|
40
|
+
usable_mem_gb = max(0.0, effective_total_gb - reserve_mem_gb)
|
|
41
|
+
|
|
42
|
+
if usable_mem_gb <= 0:
|
|
43
|
+
raise ValueError(
|
|
44
|
+
f"Not enough memory after reserving {reserve_mem_gb:.1f} GiB from "
|
|
45
|
+
f"{total_mem_gib:.1f} GiB total. Lower reserve_mem_gb or increase available memory."
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
# Calculate per-worker memory (minimum 1 GiB per worker)
|
|
49
|
+
mem_per_worker_gb = max(1.0, usable_mem_gb / n_workers)
|
|
50
|
+
mem_per_worker_bytes = int(mem_per_worker_gb * (1024**3))
|
|
51
|
+
|
|
52
|
+
return MemorySpec(
|
|
53
|
+
total_mem_gib=total_mem_gib,
|
|
54
|
+
usable_mem_gb=usable_mem_gb,
|
|
55
|
+
mem_per_worker_bytes=mem_per_worker_bytes,
|
|
56
|
+
reserved_mem_gb=reserve_mem_gb,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def configure_dask_settings(
|
|
61
|
+
temp_dir: Path,
|
|
62
|
+
memory_target: float = 0.75,
|
|
63
|
+
memory_spill: float = 0.85,
|
|
64
|
+
memory_pause: float = 0.92,
|
|
65
|
+
memory_terminate: float = 0.98,
|
|
66
|
+
spill_compression: str = "auto",
|
|
67
|
+
comm_compression: bool = False,
|
|
68
|
+
spill_threads: int | None = None,
|
|
69
|
+
) -> None:
|
|
70
|
+
"""Configure Dask global settings for optimal HPC performance.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
temp_dir: Temporary directory for spill files
|
|
74
|
+
memory_target: Memory target threshold for spilling (0.0-1.0)
|
|
75
|
+
memory_spill: Memory spill threshold for aggressive spilling (0.0-1.0)
|
|
76
|
+
memory_pause: Memory pause threshold for pausing new tasks (0.0-1.0)
|
|
77
|
+
memory_terminate: Memory terminate threshold for killing workers (0.0-1.0)
|
|
78
|
+
spill_compression: Compression algorithm for spill files ('auto', 'lz4', 'zstd', etc.)
|
|
79
|
+
comm_compression: Whether to enable network communication compression
|
|
80
|
+
spill_threads: Number of threads for parallel spill I/O operations (None for default)
|
|
81
|
+
"""
|
|
82
|
+
temp_dir_str = str(temp_dir)
|
|
83
|
+
|
|
84
|
+
config_dict = {
|
|
85
|
+
# Temporary file locations
|
|
86
|
+
"temporary-directory": temp_dir_str,
|
|
87
|
+
"distributed.worker.local-directory": temp_dir_str,
|
|
88
|
+
# Memory management thresholds (configurable)
|
|
89
|
+
"distributed.worker.memory.target": memory_target,
|
|
90
|
+
"distributed.worker.memory.spill": memory_spill,
|
|
91
|
+
"distributed.worker.memory.pause": memory_pause,
|
|
92
|
+
"distributed.worker.memory.terminate": memory_terminate,
|
|
93
|
+
# Compression settings
|
|
94
|
+
"distributed.worker.memory.spill-compression": spill_compression,
|
|
95
|
+
"distributed.comm.compression": comm_compression,
|
|
96
|
+
# Process spawning (more reliable on HPC systems)
|
|
97
|
+
"distributed.worker.multiprocessing-method": "spawn",
|
|
98
|
+
# Array optimization
|
|
99
|
+
"array.slicing.split_large_chunks": True,
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
# Add spill threads configuration if specified
|
|
103
|
+
if spill_threads is not None:
|
|
104
|
+
config_dict["distributed.p2p.threads"] = spill_threads
|
|
105
|
+
|
|
106
|
+
dask.config.set(config_dict)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def create_cluster(
|
|
110
|
+
topology: TopologySpec,
|
|
111
|
+
memory_spec: MemorySpec,
|
|
112
|
+
temp_dir: Path,
|
|
113
|
+
dashboard_address: str | None = ":0",
|
|
114
|
+
silence_logs: int = logging.ERROR,
|
|
115
|
+
adaptive: bool = False,
|
|
116
|
+
min_workers: int | None = None,
|
|
117
|
+
memory_target: float = 0.75,
|
|
118
|
+
memory_spill: float = 0.85,
|
|
119
|
+
memory_pause: float = 0.92,
|
|
120
|
+
memory_terminate: float = 0.98,
|
|
121
|
+
spill_compression: str = "auto",
|
|
122
|
+
comm_compression: bool = False,
|
|
123
|
+
spill_threads: int | None = None,
|
|
124
|
+
) -> LocalCluster:
|
|
125
|
+
"""Create and configure a Dask LocalCluster.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
topology: Worker topology specification
|
|
129
|
+
memory_spec: Memory allocation specification
|
|
130
|
+
temp_dir: Temporary directory for worker files
|
|
131
|
+
dashboard_address: Dashboard bind address (None to disable)
|
|
132
|
+
silence_logs: Log level to suppress worker output
|
|
133
|
+
adaptive: Whether to enable adaptive scaling
|
|
134
|
+
min_workers: Minimum workers for adaptive scaling
|
|
135
|
+
memory_target: Memory target threshold for spilling (0.0-1.0)
|
|
136
|
+
memory_spill: Memory spill threshold for aggressive spilling (0.0-1.0)
|
|
137
|
+
memory_pause: Memory pause threshold for pausing new tasks (0.0-1.0)
|
|
138
|
+
memory_terminate: Memory terminate threshold for killing workers (0.0-1.0)
|
|
139
|
+
spill_compression: Compression algorithm for spill files ('auto', 'lz4', 'zstd', etc.)
|
|
140
|
+
comm_compression: Whether to enable network communication compression
|
|
141
|
+
spill_threads: Number of threads for parallel spill I/O operations (None for default)
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
Configured LocalCluster instance
|
|
145
|
+
"""
|
|
146
|
+
# Configure global Dask settings with compression
|
|
147
|
+
configure_dask_settings(
|
|
148
|
+
temp_dir=temp_dir,
|
|
149
|
+
memory_target=memory_target,
|
|
150
|
+
memory_spill=memory_spill,
|
|
151
|
+
memory_pause=memory_pause,
|
|
152
|
+
memory_terminate=memory_terminate,
|
|
153
|
+
spill_compression=spill_compression,
|
|
154
|
+
comm_compression=comm_compression,
|
|
155
|
+
spill_threads=spill_threads,
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
# Create the cluster
|
|
159
|
+
cluster = LocalCluster(
|
|
160
|
+
n_workers=topology.n_workers,
|
|
161
|
+
threads_per_worker=topology.threads_per_worker,
|
|
162
|
+
processes=topology.processes,
|
|
163
|
+
memory_limit=memory_spec.mem_per_worker_bytes,
|
|
164
|
+
dashboard_address=dashboard_address,
|
|
165
|
+
local_directory=str(temp_dir),
|
|
166
|
+
silence_logs=silence_logs,
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
# Enable adaptive scaling if requested
|
|
170
|
+
if adaptive:
|
|
171
|
+
min_w = min_workers if min_workers is not None else max(1, topology.n_workers // 2)
|
|
172
|
+
cluster.adapt(minimum=min_w, maximum=topology.n_workers, wait_count=2)
|
|
173
|
+
|
|
174
|
+
return cluster
|