90_at.pm Speicherverschwendung/-leck

Begonnen von noansi, 22 Juli 2026, 12:29:14

Vorheriges Thema - Nächstes Thema

noansi

Hallo Rudolf,

in at_SecondsTillTomorrow($) ist mir aufgefallen, dass die caching Variable %at_stt mit der Zeit anwächst, da

  my $dayHour = int($t/3600);mit jeder Stunde größer wird und mit
$at_stt{$dayHour} = 86400+($l1[8]-$l2[8])*3600;bei Nutzung (z.B. periodisches at mit gleichem Tageszeitstart) mindestens ein neuer Eintrag pro Tag bis maximal ein neuer Eintrag pro Stunde hinzu kommt.

Es sind aber mit Sommerzeitumstellung maximal die letzten 25 Einträge (bzw. Stunden) für die Funktion interessant, so weit ich es verstehe. Damit wächst der Speicherverbrauch langsam aber stetig mit unnötigen Daten.
Und mit größer werdendem Hash wird sicherlich auch der Lookup aufwändiger und der Cashing Vorteil nimmt ab.

Daher folgender Vorschlag zum Aufräumen von %at_stt:
sub
at_SecondsTillTomorrow($)  # 86400, if tomorrow is no DST change
{
  my $t = shift;
  my $dayHour = int($t/3600);

  if(!$at_stt{$dayHour}) {
    my @l1 = localtime($t);
    my @l2 = localtime($t+86400);
    $at_stt{$dayHour} = 86400+($l1[8]-$l2[8])*3600;

    my $lim = $dayHour - 25;
    for (keys %at_stt) {
      delete($at_stt{$_}) if ($_ < $lim); #noansi: cleanup memory
    }
  }

  return $at_stt{$dayHour};
}

Oder übersehe ich etwas?

Gruß, Ansgar.

rudolfkoenig

Vielen Dank, habs leicht modifiziert eingecheckt.

Modifiziert, weil ich einigermassen ueberzeugt bin, dass "<$dayHour-23" reicht.

noansi

#2
Hallo Rudolf,

danke für's übernehmen.

ZitatModifiziert, weil ich einigermassen ueberzeugt bin, dass "<$dayHour-23" reicht.

Hmm, noch nicht ganz, ohne es jetzt praktisch mit Zeitumstellerei an der Systemuhr im Kampf gegen NTP ausprobieren zu wollen. In diesem Kontext wird es aufgerufen:
  my $ot = $data{AT_TRIGGERTIME} ? $data{AT_TRIGGERTIME} : gettimeofday();
  $ot = int($ot) if(!$rel);     # No way to specify subseconds
...
    $nt += at_SecondsTillTomorrow($nt) if($ot >= $nt);  # Do it tomorrow...und die kreierte $nt kann mal eine Stunde früher oder eine Stunde später sein, je nach Richtung der Zeitumstellung. Kann also bei Wiederholung eines entsprechenden at bezogen auf einen Tag Stunde 0 bis 22 oder Stunde 0 bis 24 umfassen. Daher hatte ich mich für die 25 entschieden, damit das beabsichtigte Caching sicher klappt. Zumal das gettimeofday() nach der Berechnung der Schaltzeit ausgeführt wird, was ungünstig an einer Stundengrenze auch nochmal ein Zusatzstündchen für den Vergleich vor dem Aufruf erzeugen kann. FHEM+perl+system schaffen auf jedenfall immer mal wieder Verzögerungen > 1s.

Gruß, Ansgar.

rudolfkoenig

Meine Ueberlegung:
<$dayHour-23 bedeutet schonmal 24 Stundenwerte, wir haben ja auf Stunde gerundet.
Selbst an einem Tag mit der Zeitumstellung kann man nicht mehr als 24 unterschiedliche Stunden referenzieren.
Erzeugen kann man zwar einmal im Jahr 25, aber es geht um das referenzieren, um den Cache zu nutzen.

noansi

#4
Zitat<$dayHour-23 bedeutet schonmal 24 Stundenwerte, wir haben ja auf Stunde gerundet.
korrekt.

ZitatSelbst an einem Tag mit der Zeitumstellung kann man nicht mehr als 24 unterschiedliche Stunden referenzieren.
Das ist auch korrekt, aber zwischen den Stundenwerten kann eben eine 2-Stunden-Differenz liegen, je nach Zeitbezug.

ZitatErzeugen kann man zwar einmal im Jahr 25, aber es geht um das referenzieren, um den Cache zu nutzen.
Ja und die hashes (ist ja kein Array) repräsentieren Zeiten in Stundeneinheit der Systemzeit mit oder ohne DST Verrechnung und nicht die Stunde in der Zeitangabe.
Entscheidend ist, was
    $nt = mktime(@lt);bei einer Zeitvorgabe von 00:00 an Zeit ausgibt, die noch vor der Zeitumstellung am Umstellungstag liegt und bestimmt damit, ob der 25. Wert noch für cashing gebraucht wird.
Wenn ich die Doku richtig verstehe, kann das Ergebnis varrieren, je nach verfügbarer Info auf dem System zur DST Bestimmung zum übergebenen Datum?!?
Hier https://forum.fhem.de/index.php?msg=440327 warst Du schon mal an dieser Frage.

Edit: Und das habe ich mal für die Umstellungstage ausprobiert:
{ mktime(0,0,0,29,2,126,0,0,-1) } 29. März 2026 00:00
1774738800 + 3600 * 4 = 1774753200

{ mktime(0,0,4,29,2,126,0,0,-1) } 29. März 2026 04:00
1774749600 - 1774753200 = -3600


{ mktime(0,0,0,25,9,126,0,0,-1) } 25. Oktober 2026 00:00
1792879200 + 3600 * 4 = 1792893600

{ mktime(0,0,4,25,9,126,0,0,-1) } 25. Oktober 2026 04:00
1792897200 - 1792893600 = 3600


{ mktime(0,0,23,25,9,126,0,0,-1) } 25. Oktober 2026 23:00
1792965600 - 3600 * 24 = 1792879200

und
{ join(",", localtime(1792879200)) }
0,0,0,25,9,126,0,297,1

{ join(",", localtime(1792879200 + 86400)) }
0,0,23,25,9,126,0,297,0
Die Zeitdifferenz taucht also in der Systemzeitumrechnung auf, was für mindestens 24 statt 23 spricht.

Ein Testget
sub at_Get($@) {
  my ($h, @a) = @_;
  return $h->{NAME}.', argument is missing' if (int(@a) != 2);
 
  if ($a[1] eq 'at_stt_size') { #noansi: just debugging info, remove later
    my $n = int(keys(%at_stt));
    return '%at_stt has '.$n.' keys'.($n ? ":\n".join("\n", map {' '.$_.': '.FmtDateTime($_*3600)} sort(keys(%at_stt))) : '');
  }

  return 'Unknown argument '.$a[1].', choose one of at_stt_size:noArg';
}
spuckt mir für stündliche ats ab 00:00 nach einem Neustart jetzt um 21:50 diese Liste aus:
%at_stt has 22 keys:
 495742: 2026-07-22 00:00:00
 495743: 2026-07-22 01:00:00
 495744: 2026-07-22 02:00:00
 495745: 2026-07-22 03:00:00
 495746: 2026-07-22 04:00:00
 495747: 2026-07-22 05:00:00
 495748: 2026-07-22 06:00:00
 495749: 2026-07-22 07:00:00
 495750: 2026-07-22 08:00:00
 495751: 2026-07-22 09:00:00
 495752: 2026-07-22 10:00:00
 495753: 2026-07-22 11:00:00
 495754: 2026-07-22 12:00:00
 495755: 2026-07-22 13:00:00
 495756: 2026-07-22 14:00:00
 495757: 2026-07-22 15:00:00
 495758: 2026-07-22 16:00:00
 495759: 2026-07-22 17:00:00
 495760: 2026-07-22 18:00:00
 495761: 2026-07-22 19:00:00
 495762: 2026-07-22 20:00:00
 495763: 2026-07-22 21:00:00
so, wie wir es an einem normalen Tag erwarten.

Letztlich: 23 spart insgesamt 2 hash-Einträge gegenüber 25, mit dem "Risiko" der Gewissheit, dass ggf. an einem Tag im Jahr oder ungünstigem Zeitverhalten nicht durchgängig gecashed werden kann.
Meinerseit habe ich auf wesentlich größere Speichereinsparpotentiale abgezielt.  ;)
Und die sind in beiden Variationen erreicht.

Beim Zeitverhalten bin ich für Cashing soll sicher funktionieren, weil zeitsparend. 2 Hasheinträge mehr dürften dagegen beim Hashlookup nicht auffallen.

Gruß, Ansgar.