Rendre la carte autonome, télécharger la pyramide en fond, refermer les trous de zoom et borner les workers GPU par la VRAM
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@ -91,7 +91,7 @@ from .visualizations import (
|
||||
generate_anomaly_mask,
|
||||
generate_relief_oriente,
|
||||
)
|
||||
from .gpu import gpu_cleanup, num_gpus, available_gpu_ids, restrict_gpus, safe_gpu_call
|
||||
from .gpu import gpu_cleanup, num_gpus, available_gpu_ids, restrict_gpus, safe_gpu_call, gpu_worker_slots
|
||||
from .ign import generate_ign_overlay
|
||||
from .rendering import tif_to_crop
|
||||
|
||||
@ -750,13 +750,27 @@ class LidarArchaeoPipeline:
|
||||
logger.info(f"Traitement parallèle avec {self.workers} workers...")
|
||||
logger.info(f"Fichiers: {len(files)}")
|
||||
|
||||
with ProcessPoolExecutor(max_workers=self.workers) as executor:
|
||||
# Round-robin assign each file to a real GPU host index
|
||||
active_ids = self.gpu_ids if self.gpu_ids else available_gpu_ids()
|
||||
# Une place fixe par processus du pool (GPU ou CPU), prise à sa
|
||||
# création : au plus « VRAM libre / pic d'un worker » processus par
|
||||
# GPU. L'affectation par numéro de fichier (round-robin) laissait
|
||||
# 6 workers sur chaque GPU de 8 Go avec LIDAR_WORKERS=auto : OOM.
|
||||
active_ids = self.gpu_ids if self.gpu_ids else available_gpu_ids()
|
||||
slots = gpu_worker_slots(active_ids, self.workers)
|
||||
if active_ids:
|
||||
per_gpu = ", ".join(f"GPU {g} : {slots.count(g)}" for g in active_ids)
|
||||
n_cpu = slots.count(-1)
|
||||
logger.info(f"Répartition des workers : {per_gpu}"
|
||||
+ (f", CPU : {n_cpu} (VRAM insuffisante)" if n_cpu else ""))
|
||||
slot_queue = multiprocessing.Queue()
|
||||
for slot in slots:
|
||||
slot_queue.put(slot)
|
||||
with ProcessPoolExecutor(max_workers=self.workers,
|
||||
initializer=_init_worker_slot,
|
||||
initargs=(slot_queue,)) as executor:
|
||||
resolutions_str = ','.join(str(r) for r in self.resolutions)
|
||||
future_to_file = {
|
||||
executor.submit(_process_file_standalone, str(laz_file), str(self.input_dir), str(self.output_dir), resolutions_str, self.force, self.ground_method, self.ign_classes, self.force_classify, self.keep_tif, self.quality, self.only_viz, self.skip_viz, self.output_format, active_ids[file_idx % len(active_ids)] if active_ids else None, self.openness_downsample, self.edge_buffer): laz_file
|
||||
for file_idx, laz_file in enumerate(files)
|
||||
executor.submit(_process_file_standalone, str(laz_file), str(self.input_dir), str(self.output_dir), resolutions_str, self.force, self.ground_method, self.ign_classes, self.force_classify, self.keep_tif, self.quality, self.only_viz, self.skip_viz, self.output_format, None, self.openness_downsample, self.edge_buffer): laz_file
|
||||
for laz_file in files
|
||||
}
|
||||
done = 0
|
||||
t_deadline = time.time() + 7200
|
||||
@ -857,6 +871,26 @@ class LidarArchaeoPipeline:
|
||||
logger.warning(f" Note: Impossible de supprimer les fichiers temporaires: {e}")
|
||||
|
||||
|
||||
def _init_worker_slot(slot_queue):
|
||||
"""Initialiseur du pool : le processus prend sa place une fois pour toutes.
|
||||
|
||||
Indice GPU → set_active_gpu ; -1 → CPU forcé (VRAM insuffisante) ;
|
||||
None ou file vide → choix laissé au worker (pas de GPU détecté).
|
||||
"""
|
||||
import queue
|
||||
from . import gpu
|
||||
try:
|
||||
slot = slot_queue.get_nowait()
|
||||
except queue.Empty:
|
||||
return
|
||||
if slot is None:
|
||||
return
|
||||
if slot < 0:
|
||||
gpu.force_cpu()
|
||||
else:
|
||||
gpu.set_active_gpu(slot)
|
||||
|
||||
|
||||
def _process_file_standalone(laz_file_str, input_dir, output_dir, resolution, force=False, ground_method='auto', ign_classes="sol", force_classify=False, keep_tif=False, quality=60, only_viz=None, skip_viz=None, output_format='avif', gpu_id=None, openness_downsample=None, edge_buffer=0.0):
|
||||
"""Standalone function for multiprocessing — creates its own pipeline instance.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user