Globale Variablen mit den gleichen daten werden anscheinend von Autoit zusammengefasst

  • Bin bei der arbeit mit einer UDF darauf gestoßen das Globale variablen die die gleichen Daten tragen anscheinend von Autoit nicht erneut in den Speicher geschrieben werden, sondern wohl möglich nur einen Pointer bekommen.

    Beispiel.

    1. Ich Erstelle ein test datensatz von 1 MB

    2. Ich schreibe diese per Assign in 1000 verschiedene globale variablen. Immer den selben datensatz.

    3. prüfe danach ob die variablen auch wirklich die daten tragen die sie sollen

    4. prüfe mit ProcessGetStats() wie der RAM verbrauch ist

    und stelle fest

    Vor dem schreiben: 15.3 MB

    nach dem schreiben: 15.7 MB

    Und das obwohl 1 GB an daten vorhanden sind und auch ohne probleme ausgelesen werden können.

    Jetzt verändere ich den Test

    1. Ich Erstelle ein test datensatz von 1 MB

    2. Ich schreibe diese per Assign in 1000 verschiedene globale variablen und füge dem datensatz noch $i hinzu

    3. prüfe danach ob die variablen auch wirklich die daten tragen die sie sollen

    4. prüfe mit ProcessGetStats() wie der RAM verbrauch ist

    und stelle jetzt fest

    Vor dem schreiben: 15.3 MB

    nach dem schreiben: 1,97 GB

    Ich fand das interessant zu sehen, ist mir so vorher noch nicht aufgefallen. Vielleicht findet ihr das auch interessant. Deshalb der Post.

    UDF gibts im Anhang oder hier zum selbst probieren: https://github.com/OfficialLambdax/_storageS-UDF

  • das meiste landet dann doch auf der Platte in einem TMP-File. ^^

    Nun ja die 1 GB sind in 4 ms gelesen oder 5 GB in 5 ms. Egal ob die daten immer die selben sind oder sich für jede variable unterscheiden. So schnell ist meine HDD jetzt nicht. Aber deine vermutung das Windows dafür verantwortlich ist halte ich für möglich, auch wenn ich es nicht weiß.

  • Klingt für mich nach (intelligentem) Copy on Write.

    Wikipedia

    Keine Ahnung, wie der NT-Kernel das handhabt, aber alle halbwegs modernen Linux und Unix Systeme verwenden CoW im Hauptspeicher auf Memory-Page-Basis.

    Im Linux-Kernel sind 4 KB als Standard-Page-Größe eingestellt.

    Falls die Windows-Speicherverwaltung sowas wie CoW intelligent und eigenständig (=> d.h. ohne Notwendigkeit einer expliziten Copy-Anweisung) umsetzt, könnte das das Phänomen erklären.

    1 MB wird in 250 Pages a 4 KB aufgeteilt. Ab dem zweiten 1MB-Block ist nur die letzte Page vom ersten Block verschieden, d.h. nur diese wird tatsächlich neu im RAM abgelegt.

    Edit: Das scheint Windows ja nicht so zu machen.

  • Wenn du dich weiter einlesen möchtest: man nennt dieses Vorgehen "Copy-On-Write (COW)"

    Das bedeutet, dass für Daten beim Kopieren erst dann eine physische Kopie angefertigt wird, wenn die Daten verändert werden.

    Bei AutoIt hätte ich es spontan nicht erwartet, da man dies extra implementieren müsste (C-Strings und std:string bringen das nicht von Haus aus mit).
    Im alten AutoIt-Quellcode wird auch einfach nur strcpy() verwendet. Das Feature muss also später hinzugefügt worden sein.

    Bei Strings ist mir das auch bisher nicht geläufig gewesen. Wirklich spannend wird es aber wenn man das Prinzip bei Dateisystemen anwendet.
    Dann kann man ratz fatz Snapshots vom gesamten Dateisystem erstellen die nur ganz wenig overhead haben und kann so zwischen verschiedenen Zuständen der Daten hin und her springen. Die bekanntesten Vertreter dieser Dateisysteme sind BTRFS und ZFS.

    Edit: Chesstiger war schneller

  • AspirinJunkie Das wirft für mich jetzt die spannende Frage auf, wer von uns beiden im Detail richtig vermutet.

    Wird Copy on Write hier von AutoIt umgesetzt? Oder vom Betriebssystem? In den StdLibs auf keinen Fall, da hat das nichts verloren.

    Wie du selber schon sagst, wäre eigentlich nicht zu erwarten gewesen, dass AutoIt das selber umsetzt. Garantiert nicht auf String-Basis.

    Und auch wenn man an AutoIts komplette interne Speicherverwaltung denkt, halte ich das eigentlich für unplausibel.

    Was sollte das Motiv dafür sein? Das durchschnittliche AutoIt-Skript speichert ja keine Unmengen an Daten im RAM.

    Und bei der Programmierung wurde mit Sicherheit nicht berücksichtigt, wie sehr das deutsche Forum AutoIt schon mal missbraucht und verbiegt. :D

    Es scheint für mich logischer zu sein, dass das tatsächlich innerhalb der Windows-Speicherverwaltung umgesetzt wird.

    Das würde dann aber ja heißen, dass auch ein Programm ohne eigene Speicherverwaltung von dem Effekt betroffen sein müsste.

    Ich such mir grad mal eine Windows-Kiste zum Testen. ^^

  • Naja die alten AutoIt-Versionen benutzen hierfür strcpy was auch nur ein Wrapper für memcpy ist.

    Vorher wird erst die Größe des Zuweisungsstrings bestimmt und mit der Info neuer Speicher für den Zielstring alloziert.
    Der Speicher wird also erstmal auf jeden Fall alloziert.

    Dann werden (per memcpy) die Inhalte vom einen Speicherbereich auf den anderen übertragen.

    Meines Wissens nach ist mit memcpy alleine kein cow machbar und ich wüsste auch nicht wie Windows da reingrätschen könnte um drumherum ein cow zu basteln.

  • Du hast recht, das muss AutoIt regeln.

    Beim Ausführen des folgenden Codes unter Windows springt der Speicherbedarf bei mir von 1.420K auf 1.054.092K.

    Spoiler anzeigen

    Dann frage ich mich wirklich, was da Jons Motivation war.

    Edit: Doch tatsächlich im Eifer des Gefechts src und dest bei memcpy verwechselt... Eben korrigiert. ^^

    Edit 2:

    Vielleicht passiert das doch in den Standard-Libs.

    Es gibt scheinbar Implementierungen von std::string, die über Referenzzähler eine Art Copy-on-Write realisieren.

    Je nach Version von libc++ kann das also automatisch passieren (Vgl Google-Suche nach "std string copy on write").

  • Ich kann nicht behaupten alles davon zu verstehen, aber ich denke ich kann es mir vorstellen.

    Jedenfalls hab ich mal ein wenig herum probiert umzusehen wer noch so davon profitiert in Autoit.

    Locale Variablen - Ja

    Arrays - Nein

    Maps - Nein

    DictObj - Nein

    Dann hab ich einfach mal eintausend variablen erstellt in ein script geschrieben

    Code
    ....
    Global $990 = _Data()
    Global $991 = _Data()
    Global $992 = _Data()
    Global $993 = _Data()
    Global $994 = _Data()
    Global $995 = _Data()
    ....

    und diese mit je 1 MB gefüllt. Das selbe dann nochmal mit Localen und ohne sie in einen Scope zusetzen.

    Und auch hier profitiert Autoit davon.

  • Es gibt scheinbar Implementierungen von std::string, die über Referenzzähler eine Art Copy-on-Write realisieren.

    Je nach Version von libc++ kann das also automatisch passieren (Vgl Google-Suche nach "std string copy on write").

    Ja das wäre ja eine entsprechende Änderung die Jon vollzogen hätte.
    In den alten Quellcodes hat er für Strings noch C-Strings anstatt std:string verwendet.

    Da das aber auch meine erste Vermutung war habe ich kurz recherchiert und bin auf folgende Aussage gestoßen, nach der cow bei std:string wohl dem C++11-Standard wiedersprechen würde: https://stackoverflow.com/a/12199969

    Also entweder hat Jon das direkt in AutoIt implementiert oder den Varianttyp auf Grundlage einer Lib aufgebaut die das mitbringt.
    Ich bin mir jedoch relativ sicher, dass das Verhalten nicht von Windows kommt.