Performance beim Ermitteln von Devices und Readings

Begonnen von Thorsten Pferdekaemper, 17 Februar 2016, 21:20:33

Vorheriges Thema - Nächstes Thema

Thorsten Pferdekaemper

Hi,
ich habe folgendes Coding:

  my @names = keys %defs; 
  foreach my $d (@names) {
    if(AttrVal($d, "model", "none") ne "HM-CC-RT-DN"){ next; };
    my $ventil = ReadingsVal($d, "actuator", 0);

Natürlich ist das nur der Anfang, aber den Teil habe ich ganz stark im Verdacht, Probleme zu machen. Auf jeden Fall taucht das regelmäßig ganz oben in apptime auf.
Hat jemand eine Idee, wie man das am Besten schneller macht?
Danke&Gruß,
   Thorsten
FUIP

Talkabout

Wäre es mit eiiner devspec nicht sinnvoller?

my @ventiile = devspec2array("a:model=HM-CC-RT-DN");

Damit müsste man eine Liste aller Geräte erhalten, die im Attribut "model" den Wert "HM-CC-RT-DN" haben.

Die Frage nach der Performance lässt sich wohl nur beantworten wenn man weiss, wie häufig dieser Code-Block ausgeführt wird und vor allem auch, was sonst noch in der "foreach" passiert.

Gruss

rudolfkoenig

Zusaetzlch zu Talkabouts Bemerkungen:
- devspec2array ist nicht schneller als diese Schleife, aber ein de-facto "API"
- ich wuerde diesen Code moeglichst selten aufrufen.
- wenn das nicht geht, dann messen, wielange es dauert.
- wenn es signifikant ist, dann kann man das Ergebins der Filter-Operationen cachen in einem eigenen Hash. Leider muss dieser Hash erneuert werden, falls die Liste sich aendert, aber evtl. reicht einmal nach FHEM-start.

Thorsten Pferdekaemper

Zitat von: Talkabout am 17 Februar 2016, 21:39:25
Die Frage nach der Performance lässt sich wohl nur beantworten wenn man weiss, wie häufig dieser Code-Block ausgeführt wird und vor allem auch, was sonst noch in der "foreach" passiert.
Das sehe ich anders. Meiner Meinung nach wird das Coding nicht schneller oder langsamer, wenn man es öfter ausführt. Es ist auch egal, was danach passiert. Das gezeigte Stückchen Coding dürfte davon nicht groß beeinflusst werden.
Gruß,
   Thorsten
FUIP

Thorsten Pferdekaemper

Zitat von: rudolfkoenig am 17 Februar 2016, 21:45:55
- ich wuerde diesen Code moeglichst selten aufrufen.
- wenn das nicht geht, dann messen, wielange es dauert.
- wenn es signifikant ist,
Das ist schon klar. Es sind zwar nur etwa 170ms im Durchschnitt und es wird nur einmal pro Minute aufgerufen, aber ich habe ein paar ähnliche Dinger, daher ist es für mich schon signifikant. Außerdem wird sich die Anzahl meiner Devices in nächster Zeit erhöhen.

Zitat
dann kann man das Ergebins der Filter-Operationen cachen in einem eigenen Hash. Leider muss dieser Hash erneuert werden, falls die Liste sich aendert, aber evtl. reicht einmal nach FHEM-start.
Das wäre natürlich immer eine Möglichkeit. Die Liste ändert sich nur, wenn ich einen neuen Thermostat an eine Heizung hänge. Das passiert ja nicht soooo oft. Leider ist das halt etwas komplexer, als ich erhofft hatte. Ich hatte gehofft, dass ich irgend einen offensichtlichen Fehler gemacht habe...
Danke&Gruß,
   Thorsten
FUIP

Talkabout

Zitat von: Thorsten Pferdekaemper am 17 Februar 2016, 22:12:15
Das sehe ich anders. Meiner Meinung nach wird das Coding nicht schneller oder langsamer, wenn man es öfter ausführt. Es ist auch egal, was danach passiert. Das gezeigte Stückchen Coding dürfte davon nicht groß beeinflusst werden.
Gruß,
   Thorsten
Ich habe mit apptime bisher nicht gearbeitet. Wenn dieses Tool genau diese Zeilen als "Performance Fresser" ausmacht, dann ist Deine Aussage korrekt. Normalerweise sind Schleifen aber eher deswegen langsam, weil dort viele Operationen ausgeführt werden, deren Laufzeit sich durch die Schleife potenziert. Da Dein Code-Snippet nicht einmal das komplette "foreach"-Statement beinhaltet, ist es daher schwer Vorschläge zu machen.

Gruss

Thorsten Pferdekaemper

Zitat von: Talkabout am 17 Februar 2016, 22:32:25
Ich habe mit apptime bisher nicht gearbeitet. Wenn dieses Tool genau diese Zeilen als "Performance Fresser" ausmacht, dann ist Deine Aussage korrekt. Normalerweise sind Schleifen aber eher deswegen langsam, weil dort viele Operationen ausgeführt werden, deren Laufzeit sich durch die Schleife potenziert.
Das stimmt so wieder nicht ganz. Die Laufzeit einer normalen Schleife hat Komplexität O(n). (Mit n = Anzahl der Schleifendurchläufe.) Da potenziert sich nix, es ist rein linear. Mir geht es hier auch nicht um generelle Betrachtungen, sondern eher um Optimierungen im FHEM-Umfeld. Die prinzipiellen Techniken sind mir schon klar.

Zitat
Da Dein Code-Snippet nicht einmal das komplette "foreach"-Statement beinhaltet, ist es daher schwer Vorschläge zu machen.
Alla guut:

  my $ventilMax = 0.1;
  my @names = keys %defs; 
  foreach my $d (@names) {
    if(AttrVal($d, "model", "none") ne "HM-CC-RT-DN"){ next; };
    my $ventil = ReadingsVal($d, "actuator", 0);
#   remove "%" 
    $ventil =~ s/%| //g;   
    if($ventil > $ventilMax){ $ventilMax = $ventil; };
  }
  readingsSingleUpdate($defs{"HeizRegler"},"valveMax", $ventilMax, 1);

Das ist jetzt das komplette Coding. So viel mehr ist da also nicht. Außerden interessiert das nicht so wirklich, da das, was ich vorher gezeigt habe, nur sozusagen ein Beispiel ist. Ich habe mehrere ähnliche Sachen.
Gruß,
   Thorsten

FUIP

Talkabout

Wenn ich davon ausgehe, dass eine Schleife 2 Statements ausführt, die für sich jeweils 10ms brauchen, multipliziert sich die Laufzeit der 2 Statements mit der Anzahl der Schleifendurchläufe. "potenziert" war das falsche Wort meinerseits.

Ungeachtet dessen ist es wohl das Sinnvollste, die Geräte in einem Hash vorzuhalten, wobei da auch die Frage ist, wie viel das bringt. Mit der Messung solltest Du aber rausfinden können, ob es sich lohnt.

Gruss

rudolfkoenig

Ich wuerde um eine Schokolade wetten, dass die Schleife weniger Zeit braucht, als readingsSingleUpdate() :)
Das loest naemlich ein Event aus, was wiederum alle notifies, FileLogs, etc aufweckt.

Thorsten Pferdekaemper

Zitat von: rudolfkoenig am 17 Februar 2016, 23:25:43
Ich wuerde um eine Schokolade wetten, dass die Schleife weniger Zeit braucht, als readingsSingleUpdate() :)
Das loest naemlich ein Event aus, was wiederum alle notifies, FileLogs, etc aufweckt.
Ich habe eine andere Routine, die ein readingsSingleUpdate() über einen Timer macht. Dadurch sehe ich in apptime, dass die nur 45ms braucht. Allerdings wird das "valveMax" in ein FileLog geschrieben. Das muss ich dann nochmal messen.
...aber ich hätte das Teil schon gern in einem FileLog. Was könnte man jetzt da wieder machen?
Gruß,
   Thorsten
FUIP

rudolfkoenig

Zitat...aber ich hätte das Teil schon gern in einem FileLog. Was könnte man jetzt da wieder machen?
Erst nachweisen, dass die Funktion auch hier soviel benoetigt:  Sonst Standardantwort: Anzahl der notifies/FileLogs/etc reduzieren, und bei notify/FileLog, falls machbar, nur ein Geraet angeben: solche Instanzen werden bei anderen Events nicht "aufgeweckt".

Zum Vergleich: auf meinem Entwicklungsrechner benoetigt diese Schleife 3ms. Natuerlich modifiziert, damit alle 160 Geraete der Konfiguration als  HM-CC-RT-DN aufgefasst werden, ich habe naemlich keins. Das abschliessende Event geht durch 30 FileLog/notify.

Thorsten Pferdekaemper

Zitat von: rudolfkoenig am 18 Februar 2016, 08:01:05
Erst nachweisen, dass die Funktion auch hier soviel benoetigt: 
Klar, das hätte ich erstmal genau messen sollen. ...war spät letzte Nacht.

ZitatZum Vergleich: auf meinem Entwicklungsrechner benoetigt diese Schleife 3ms. Natuerlich modifiziert, damit alle 160 Geraete der Konfiguration als  HM-CC-RT-DN aufgefasst werden, ich habe naemlich keins. Das abschliessende Event geht durch 30 FileLog/notify.
Danke für Deine Bemühungen. Ich habe das ganze auf einem RasPi 1 gemessen. Da sind 3ms eher ein unerreichbarer Traum... Aber für solche Betrachtungen sind absolute Zahlen sowieso schlecht vergleichbar.
Ich werde wahrscheinlich das ganze mal genau durchmessen müssen. 

Gruß,
   Thorsten
FUIP

Thorsten Pferdekaemper

Hi,
ich habe jetzt mal gemessen:
Der erste Teil (also alles bis zum readingsSingleUpdate) braucht meistens so etwa 32ms. Das readingsSingleUpdate braucht meistens so in etwa 54ms. Also hattest Du Recht (Rudolf), dass das readingsSingleUpdate länger dauert. Was ich daran allerdings noch optimieren kann weiß ich auch nicht, es gibt dafür sowieso keine notifies und nur ein FileLog. Das FileLog hat auch nur ein Device.

Eins ist mir dabei noch aufgefallen:
Das ganze habe ich direkt mit gettimeofday() gemessen. Parallel dazu habe ich noch apptime laufen lassen. Da kam dann dabei raus, dass das at_Exec, dass das ganze aufruft immer noch etwa 155ms anzeigt. Da fehlen also in etwa 70ms. Ich dachte nicht, dass das at selbst so einen großen Overhead hat, aber wenn ich ein leeres at (also mit Command {}) mit apptime ausmesse, dann kommt das etwa auf 60ms. Ein at, das eine leere Funktion aus der myUtils aufruft, kommt sogar auf etwa 100ms.

Naja, vielleicht sollte ich mal in Hardware investieren...
Gruß,
   Thorsten

FUIP