Performance-Revolution: Saxon-Worker-Pool eliminiert JVM-Startup-Overhead
Implementiert persistente JVM-Worker-Pool für 5-10x schnellere Transformationen: VORHER: - 82 Dateien in 60s (12 Worker) = 0.73s/Datei - JVM-Start bei jeder Transformation (~500ms Overhead) - Classpath wird jedes Mal neu geladen NACHHER (erwartet): - 82 Dateien in ~8-12s (12 Worker) = 0.10-0.15s/Datei - JVM läuft persistent (einmalig ~500ms beim Start) - 5-10x schneller! 🚀 Architektur: - SaxonWorkerPool: Verwaltet N lang-laufende JVM-Prozesse - SaxonWorker.java: Java-Daemon der Saxon-Transformationen ausführt - Kommunikation via stdin/stdout (Tab-separated Job-Format) - Automatisches Fallback auf subprocess bei Pool-Fehlern - Graceful Shutdown beim Beenden der Anwendung Neue Dateien: - src/saxon_pool.py: Worker-Pool-Implementierung - Kompiliert SaxonWorker.java zur Laufzeit - Startet N JVM-Prozesse beim Projekt-Öffnen - Thread-safe Job-Verteilung mit Locks - Context Manager für sauberen Shutdown Änderungen: - transform.py: Nutzt Pool wenn verfügbar, Fallback auf subprocess - MainWindow.py: Initialisiert Pool beim Projekt-Öffnen, beendet bei Close - set_saxon_worker_pool() zum globalen Pool-Management Technische Details: - Java-Code als String eingebettet, Runtime-Kompilierung mit javac - stdout für Job-Ergebnisse, stderr für Saxon-Logs - Tab-separated Format: source\txsl\toutput\tparams - Worker antworten mit "OK" oder "ERROR: message" Nächster Test wird zeigen ob 8-12s erreicht werden! 🎯 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
+62
-1
@@ -29,7 +29,8 @@ from ui.TreeNodeEditDialog import TreeNodeEditDialog
|
||||
from ui.XslFileEditDialog import XslFileEditDialog
|
||||
from ui.XmlToXslAssignDialog import XmlToXslAssignDialog
|
||||
from conf import app_settings, Project, ProjectData, TreeNode, XslFile, XmlFile
|
||||
from transform import TransformationJob
|
||||
from transform import TransformationJob, set_saxon_worker_pool
|
||||
from saxon_pool import SaxonWorkerPool
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
@@ -723,6 +724,9 @@ class MainWindow(QMainWindow):
|
||||
# Starte Hash-Berechnung für alle XML-Dateien
|
||||
self._start_xml_hash_calculation()
|
||||
|
||||
# Initialisiere Saxon-Worker-Pool für schnellere Transformationen
|
||||
self._initialize_saxon_worker_pool()
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Laden des Projekts '{project.name}': {e}")
|
||||
# Fallback: Erstelle Standard-Einstellungen
|
||||
@@ -734,6 +738,60 @@ class MainWindow(QMainWindow):
|
||||
except Exception as fallback_error:
|
||||
logger.error(f"Fehler beim Erstellen der Fallback-Einstellungen: {fallback_error}")
|
||||
|
||||
def _initialize_saxon_worker_pool(self):
|
||||
"""Initialisiert den Saxon-Worker-Pool für schnelle Transformationen."""
|
||||
try:
|
||||
# Shutdown vorherigen Pool falls vorhanden
|
||||
self._shutdown_saxon_worker_pool()
|
||||
|
||||
if not self.project:
|
||||
logger.warning("Kein Projekt geladen, Saxon-Worker-Pool nicht initialisiert")
|
||||
return
|
||||
|
||||
# Hole Tool-Konfigurationen
|
||||
java_vm = next((vm for vm in app_settings.java_vms if vm.id == self.project.java_vm_id), None)
|
||||
saxon_jar = next((jar for jar in app_settings.saxon_jars if jar.id == self.project.saxon_jar_id), None)
|
||||
|
||||
if not java_vm or not saxon_jar:
|
||||
logger.warning("Java VM oder Saxon JAR nicht gefunden, Pool nicht initialisiert")
|
||||
return
|
||||
|
||||
# Erstelle Worker-Pool
|
||||
num_workers = app_settings.max_workers
|
||||
pool = SaxonWorkerPool(
|
||||
num_workers=num_workers,
|
||||
java_vm_path=java_vm.path_to_binary_file,
|
||||
saxon_jar_path=saxon_jar.path_to_jar_file,
|
||||
classpath_cache=TransformationJob._classpath_cache,
|
||||
)
|
||||
|
||||
# Setze globalen Pool
|
||||
set_saxon_worker_pool(pool)
|
||||
|
||||
logger.info(
|
||||
f"Saxon-Worker-Pool initialisiert: {num_workers} Worker "
|
||||
f"(erwartet: {num_workers}x schneller für Saxon-Transformationen)"
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Initialisieren des Saxon-Worker-Pools: {e}")
|
||||
# Kein Pool ist OK - Fallback auf subprocess
|
||||
|
||||
def _shutdown_saxon_worker_pool(self):
|
||||
"""Beendet den Saxon-Worker-Pool sauber."""
|
||||
try:
|
||||
# Importiere transform um Zugriff auf globalen Pool zu haben
|
||||
import transform
|
||||
|
||||
if transform._saxon_worker_pool:
|
||||
logger.info("Beende Saxon-Worker-Pool...")
|
||||
transform._saxon_worker_pool.shutdown()
|
||||
set_saxon_worker_pool(None)
|
||||
logger.info("Saxon-Worker-Pool beendet")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Beenden des Saxon-Worker-Pools: {e}")
|
||||
|
||||
def change_theme(self, theme_name):
|
||||
"""
|
||||
Wechselt das Theme der Anwendung.
|
||||
@@ -4613,6 +4671,9 @@ class MainWindow(QMainWindow):
|
||||
self.transformation_thread.quit()
|
||||
self.transformation_thread.wait()
|
||||
|
||||
# Beende Saxon-Worker-Pool
|
||||
self._shutdown_saxon_worker_pool()
|
||||
|
||||
# PDF-Dokumente schließen ist bei QtPdf automatisch durch Garbage Collection
|
||||
super().closeEvent(event)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user