-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgpu_memory_config.py
More file actions
369 lines (320 loc) Β· 13 KB
/
Copy pathgpu_memory_config.py
File metadata and controls
369 lines (320 loc) Β· 13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
#!/usr/bin/env python3
"""
GPU Memory Configuration for Docling
Optimized presets for different GPU memory sizes and use cases
"""
import os
import torch
from typing import Dict, Any, Optional
from dataclasses import dataclass
@dataclass
class MemoryConfig:
"""Memory configuration settings"""
memory_fraction: float
max_batch_size: int
vlm_batch_size: int
num_threads: int
images_scale: float
cleanup_threshold: float
pytorch_alloc_conf: Dict[str, Any]
enable_mixed_precision: bool
gradient_checkpointing: bool
description: str
class GPUMemoryOptimizer:
"""GPU Memory optimization configurations for different scenarios"""
# Predefined configurations for different GPU memory sizes
CONFIGS = {
"24gb_conservative": MemoryConfig(
memory_fraction=0.85,
max_batch_size=2,
vlm_batch_size=1,
num_threads=4,
images_scale=1.0,
cleanup_threshold=0.75,
pytorch_alloc_conf={
'max_split_size_mb': 64,
'garbage_collection_threshold': 0.7,
'expandable_segments': True
},
enable_mixed_precision=True,
gradient_checkpointing=True,
description="Ultra-conservative for 24GB - prioritizes stability over speed"
),
"24gb_balanced": MemoryConfig(
memory_fraction=0.90,
max_batch_size=4,
vlm_batch_size=2,
num_threads=6,
images_scale=1.0,
cleanup_threshold=0.80,
pytorch_alloc_conf={
'max_split_size_mb': 128,
'garbage_collection_threshold': 0.8,
'expandable_segments': True
},
enable_mixed_precision=True,
gradient_checkpointing=False,
description="Balanced performance and memory usage for 24GB"
),
"24gb_performance": MemoryConfig(
memory_fraction=0.95,
max_batch_size=8,
vlm_batch_size=4,
num_threads=8,
images_scale=1.5,
cleanup_threshold=0.85,
pytorch_alloc_conf={
'max_split_size_mb': 256,
'garbage_collection_threshold': 0.85,
'expandable_segments': True
},
enable_mixed_precision=False,
gradient_checkpointing=False,
description="Maximum performance for 24GB - higher memory usage"
),
"16gb_optimized": MemoryConfig(
memory_fraction=0.85,
max_batch_size=2,
vlm_batch_size=1,
num_threads=4,
images_scale=0.8,
cleanup_threshold=0.70,
pytorch_alloc_conf={
'max_split_size_mb': 32,
'garbage_collection_threshold': 0.6,
'expandable_segments': True
},
enable_mixed_precision=True,
gradient_checkpointing=True,
description="Optimized for 16GB GPUs"
),
"12gb_minimal": MemoryConfig(
memory_fraction=0.80,
max_batch_size=1,
vlm_batch_size=1,
num_threads=2,
images_scale=0.5,
cleanup_threshold=0.65,
pytorch_alloc_conf={
'max_split_size_mb': 16,
'garbage_collection_threshold': 0.5,
'expandable_segments': True
},
enable_mixed_precision=True,
gradient_checkpointing=True,
description="Minimal memory usage for 12GB GPUs"
),
# ~20GB GPU shared with Ollama (~10GB resident) β production NATS path A
"20gb_nats": MemoryConfig(
memory_fraction=0.45,
max_batch_size=4,
vlm_batch_size=2,
num_threads=4,
images_scale=1.0,
cleanup_threshold=0.75,
pytorch_alloc_conf={
'max_split_size_mb': 64,
'garbage_collection_threshold': 0.7,
'expandable_segments': True
},
enable_mixed_precision=True,
gradient_checkpointing=False,
description="NATS Docling on 20GB GPU alongside Ollama (path A β full Docling GPU)"
),
# Hard cap ~5GB for parser A/B benchmark path B (set_per_process_memory_fraction also applied)
"capped_5gb": MemoryConfig(
memory_fraction=0.25,
max_batch_size=1,
vlm_batch_size=1,
num_threads=2,
images_scale=0.8,
cleanup_threshold=0.60,
pytorch_alloc_conf={
'max_split_size_mb': 16,
'garbage_collection_threshold': 0.5,
'expandable_segments': True
},
enable_mixed_precision=True,
gradient_checkpointing=True,
description="Cap Docling to ~5GB VRAM (path B β small batches, OCR/table only)"
),
# Production shared GPU: 8GB Docling cap, batch=1, leave ~12GB for Ollama
"20gb_capped": MemoryConfig(
memory_fraction=0.40,
max_batch_size=1,
vlm_batch_size=1,
num_threads=2,
images_scale=0.5,
cleanup_threshold=0.70,
pytorch_alloc_conf={
'max_split_size_mb': 128,
'garbage_collection_threshold': 0.7,
'expandable_segments': True
},
enable_mixed_precision=True,
gradient_checkpointing=True,
description="Production: 8GB Docling CUDA cap, batch=1, Ollama reserve via vram_policy"
),
}
@classmethod
def apply_config(cls, config_name: str = "24gb_balanced") -> MemoryConfig:
"""Apply a predefined memory configuration"""
if config_name not in cls.CONFIGS:
available = ", ".join(cls.CONFIGS.keys())
raise ValueError(f"Unknown config '{config_name}'. Available: {available}")
config = cls.CONFIGS[config_name]
print(f"ποΈ Applying GPU memory config: {config_name}")
print(f"π Description: {config.description}")
# Set PyTorch memory fraction
if torch.cuda.is_available():
torch.cuda.set_per_process_memory_fraction(config.memory_fraction)
print(f"π§ Memory fraction: {config.memory_fraction:.2%}")
# Set PyTorch allocator configuration
alloc_conf_str = ",".join([
f"{k}:{v}" for k, v in config.pytorch_alloc_conf.items()
])
os.environ['PYTORCH_CUDA_ALLOC_CONF'] = alloc_conf_str
print(f"π§ PyTorch alloc config: {alloc_conf_str}")
# Set additional environment variables
if config.enable_mixed_precision:
os.environ['TORCH_ALLOW_TF32_CUBLAS_OVERRIDE'] = '1'
print("π§ Mixed precision enabled")
# Memory debugging (optional)
if config_name.endswith('_conservative') or config_name.endswith('_minimal'):
os.environ['PYTORCH_NO_CUDA_MEMORY_CACHING'] = '0' # Keep caching for better performance
print("π§ Conservative memory debugging enabled")
return config
@classmethod
def get_optimal_docling_options(cls, config: MemoryConfig, user_options: Optional[Dict] = None) -> Dict[str, Any]:
"""Generate optimal Docling options based on memory configuration"""
base_options = {
'vlm_batch_size': config.vlm_batch_size,
'images_scale': config.images_scale,
'num_threads': config.num_threads,
'layout_batch_size': 1,
'ocr_batch_size': 1,
'table_batch_size': 1,
'queue_max_size': 1,
'generate_picture_images': False,
'generate_page_images': False,
'force_full_page_ocr': False,
'cuda_use_flash_attention2': not config.gradient_checkpointing,
}
if user_options:
base_options.update(user_options)
if user_options.get('accelerator_device', '').lower() in ('cpu', 'cuda', 'gpu'):
pass
elif 'accelerator_device' not in user_options:
base_options.setdefault('accelerator_device', 'cuda')
base_options['vlm_batch_size'] = min(
base_options.get('vlm_batch_size', config.vlm_batch_size),
config.max_batch_size,
)
print(
f"π§ User options merged, VLM batch size capped at "
f"{base_options['vlm_batch_size']}"
)
return base_options
@classmethod
def detect_optimal_config(cls) -> str:
"""Auto-detect optimal configuration based on available GPU memory"""
if not torch.cuda.is_available():
print("β CUDA not available, using minimal config")
return "12gb_minimal"
# Get GPU memory in GB
device = torch.cuda.current_device()
total_memory_gb = torch.cuda.get_device_properties(device).total_memory / (1024**3)
print(f"π Detected GPU memory: {total_memory_gb:.1f}GB")
# Select config based on memory size
if total_memory_gb >= 22: # 24GB cards
return "24gb_balanced"
elif total_memory_gb >= 15: # 16GB cards
return "16gb_optimized"
elif total_memory_gb >= 10: # 12GB cards
return "12gb_minimal"
else:
print("β οΈ Very low GPU memory detected, using minimal config")
return "12gb_minimal"
@classmethod
def print_memory_status(cls):
"""Print current GPU memory status"""
if not torch.cuda.is_available():
print("β CUDA not available")
return
device = torch.cuda.current_device()
allocated = torch.cuda.memory_allocated(device) / 1024**3
reserved = torch.cuda.memory_reserved(device) / 1024**3
total = torch.cuda.get_device_properties(device).total_memory / 1024**3
print(f"π GPU Memory Status:")
print(f" π Device: {torch.cuda.get_device_name(device)}")
print(f" πΎ Total: {total:.2f}GB")
print(f" π Reserved: {reserved:.2f}GB ({reserved/total*100:.1f}%)")
print(f" β
Allocated: {allocated:.2f}GB ({allocated/total*100:.1f}%)")
print(f" π Free: {total-reserved:.2f}GB ({(total-reserved)/total*100:.1f}%)")
# Convenience function for easy usage
def setup_gpu_optimization(config_name: Optional[str] = None, user_options: Optional[Dict] = None) -> Dict[str, Any]:
"""
One-line setup for GPU optimization
Args:
config_name: Configuration preset name (auto-detected if None)
user_options: User-specific Docling options to merge
Returns:
Optimized Docling options dictionary
"""
# Auto-detect or use provided config
if config_name is None:
config_name = GPUMemoryOptimizer.detect_optimal_config()
# Apply configuration
config = GPUMemoryOptimizer.apply_config(config_name)
# Get optimized options
docling_options = GPUMemoryOptimizer.get_optimal_docling_options(config, user_options)
# Print status
GPUMemoryOptimizer.print_memory_status()
print(f"β
GPU optimization setup complete!")
print(f"π― Recommended options: {list(docling_options.keys())}")
return docling_options
# Example usage configurations
EXAMPLE_CONFIGS = {
"vlm_heavy": {
"do_picture_description": True,
"vlm_model": "smolvlm", # Prefer smaller VLM models
"custom_prompt": "Describe this image concisely.",
"do_picture_classification": False, # Disable to save memory
},
"text_extraction": {
"do_picture_description": False,
"do_ocr": True,
"do_table_structure": True,
"force_full_page_ocr": False, # Conservative
},
"full_processing": {
"do_picture_description": True,
"vlm_model": "smolvlm",
"do_picture_classification": True,
"do_ocr": True,
"do_table_structure": True,
"do_code_enrichment": True,
"do_formula_enrichment": True,
}
}
if __name__ == "__main__":
# Demo the configuration system
print("π GPU Memory Configuration Demo")
print("=" * 50)
# Show available configs
print("\nπ Available configurations:")
for name, config in GPUMemoryOptimizer.CONFIGS.items():
print(f" β’ {name}: {config.description}")
# Auto-detect and setup
print(f"\nπ Auto-detection:")
optimal_config = GPUMemoryOptimizer.detect_optimal_config()
print(f" Recommended: {optimal_config}")
# Setup with VLM processing
print(f"\nποΈ Setting up for VLM processing:")
options = setup_gpu_optimization(
config_name=optimal_config,
user_options=EXAMPLE_CONFIGS["vlm_heavy"]
)
print(f"\nπ― Final Docling options:")
for key, value in options.items():
print(f" {key}: {value}")