[patch] readingsBulkUpdate: event-on-* Regexps nur einmal kompilieren

Begonnen von snoonsons, 20 September 2026, 09:17:55

Vorheriges Thema - Nächstes Thema

snoonsons

Hallo,

ein Anwender hat gemeldet, dass FHEM auf seinem Raspi 4 rund 10% CPU zieht, sobald mein Tool (AstraMeter) seine Werte per MQTT schickt:
https://github.com/tomquist/AstraMeter/issues/663

Der Nutzer will eine Möglichkeit haben die Nachrichtenrate zu drosseln. Gemessen sind es 3,7 Nachrichten/s bei knapp 4 KiB/s. Allerdings lassen sich damit die 10% meiner Meinung nach nicht erklären. Also hab ich FHEM mal mit NYTProf profiliert.

Auffällig ist CORE:regcomp mit 378.779 Aufrufen in 60 Sekunden, bei 224 verarbeiteten Nachrichten. Das sind rund 1.500 kompilierte Regexps pro Nachricht.

Der Grund sitzt in readingsBulkUpdate. Die Attributlisten werden mit m/^$l$/ geprüft, und weil der interpolierte Wert bei jedem Eintrag ein anderer ist, kompiliert perl jedes Mal neu, also pro Eintrag pro geschriebenem Reading. Betroffen sind .attreocr, .attreour, .attrminint, .attrtocr, .attraggr und .or.

Der Anwender hat 19 Einträge in event-min-interval. Einmal mit und einmal ohne das Attribut, sonst gleiche Konfiguration und gleiche Last:

  ohne event-min-interval  1,478% CPU
  mit event-min-interval    2,244% CPU

Das Attribut, das eigentlich Last sparen soll, kostet also gut 50% obendrauf, einfach weil Einträge drinstehen.

Im Anhang ein Patch gegen r31667. Der kompiliert jede Liste einmal und legt sie unter "<attr>.re" im Hash ab, verworfen wird sie, sobald die Liste ersetzt wird oder sich die Länge ändert. An CommandAttr hab ich das bewusst nicht gehängt, weil 44_S7_DRead, 44_S7_DWrite und 44_S7_AWrite .attreocr direkt setzen.

Damit, gleiche Last, viermal abwechselnd gemessen:

  ohne Patch  2,271% CPU
  mit Patch    1,483% CPU

Die Treffer hab ich gegen die alte Variante verglichen, auch mit Einträgen wie Zaehler.*:60, V[12]_power:1, x.y:60, has:colon, temp:20:linear:avg und oldreadingsAlways, da kommt dasselbe raus. @eocrv behält seine Reihenfolge wegen `$eocrv
  • ` weiter unten. Im laufenden Betrieb kommen gleich viele Events raus und die Readings sind dieselben.

Unter Last gefahren hab ich event-on-change-reading, event-on-update-reading und event-min-interval. timestamp-on-change-reading, event-aggregator und oldreadings sind dieselbe Umformung, die hab ich nur isoliert geprüft.




Sidey

Guter Fund.

Ich kenne noch eine Stelle, an der regex sehr oft neu compiliert werden müssen.
Und zwar beim Prüfen, welches logische Modul Daten erhält: next if($dmsg !~ m/$modules{$m}{Match}/s);
Der Wert Match wird im logischen Modul gesetzt, meist ohne die Nutzung von QR sondern rein als String.
Da die obige Prüfung in einer Schleife läuft, wird die regex bei jedem Aufruf neu compiliert. Das müsste aber jeder Maintainer für sein Modul anpassen. Ich werde das mal für meine angehen.


Grüße Sidey

Nutze: SIGNALDuino, Homematic, Raspberry Pi, MQTT, Alexa, Docker, AlexaFhem,zigbee2mqtt, tasmota

Maintainer von: SIGNALduino, SD_WS*, fhem-docker, alexa-fhem-docker, fhempy-docker, WebAuth, fhem-mcp, midea-mqtt, whatsmeow-mqtt, alexa-cookie-service
https://github.com/sidey79?tab=repositories