Pointer auf Scripting.Dictionary

  • Hallo Zusammen!

    Aus diversen Gründen muss ich einige Programmfunktionen in eine separate Datei auslagern, welche ich vom Hauptprogramm aus anspreche.
    Dies sind vor allem Zeitgründe.
    Damit will ich nicht sagen, dass AutoIt zu langsam ist. Ganz und gar nicht. Es sind viel mehr die angeschlossenen Festplatten / Memory-Sticks etc. die langsam sind.

    Nehmen wir an, ich muss für mein Projekt viele Dateien auf zwei verschiedenen Medien (Memory-Sticks oder CDs....) vergleichen.
    Dann kann ich das entweder tun, in dem ich zuerst alle benötigten Daten vom Medium A einlese, danach vom Medium B und danach nach meinen Kriterien / Vorgaben vergleiche!

    Das braucht aber viel Zeit.
    Nun könnte ich aber Zeit sparen, in dem ich Medium A und Medium B parallel mit jeweils einem separaten Programm einlese und die gesammelten Daten dem Hauptprogramm zur Verfügung stelle, welches dann die Daten vergleicht!

    Soweit zum Hintergrund.
    Aber wie mache ich das?

    Die Daten, sollen in ein Scripting.Dictionary eingelesen werden, welche ich mit

    [autoit]


    $oData1 = ObjCreate('Scripting.Dictionary')
    $oData2 = ObjCreate('Scripting.Dictionary')

    [/autoit]


    im Hauptprogramm erstelle.

    Nun möchte ich aber nach dem Erstellen dieser Objekte jeweils einen Pointer auf diese Objekte den zwei nun zu startenden Programmen übergeben.
    Diese Programme füllen dann diese Dictionaries ab und geben im Anschluss den Pointer wieder zurück.
    Dann kann das Hauptprogramm mit dem Daten in den Scripting.Dictionaries weiterarbeiten!

    Wie kann ich das bewerkstelligen?
    Oder gibt es andere (schnelle!) Strukturen, welche anstelle von Scripting.Dictionary eingesetz werden können, auf welche dann mit einem Pointer gearbeitet werden kann?
    (ArrayList, Hashtable, Queue und Stack habe ich schon versucht, aber die sind - in meinem Fall - langsamer als Scripting.Dictionary)
    Ich muss ja die Tabellen schlussendlich auch wieder durchsuchen können. Und da ist halt das Scripting.Dictionary optimal!

    Vielleicht könnte mir hier dieser Beitrag helfen???? (2. Beispiel) Klick

    Gruss
    Veronesi

    3 Mal editiert, zuletzt von veronesi (11. September 2012 um 07:21)

  • Hallo

    OK, ich habe mich vermutlich nicht so klar ausgedrückt.

    Kurzform, was ich möchte:
    Ich möchte im Hauptprogramm eine Array ähnliche Struktur erstellen.
    Diese Struktur soll dann in einem separaten Programm gefüllt werden.
    Danach muss das Hauptprogramm diese Daten in der Struktur auswerten.

    Ich denke, dass ich irgendwie mit einem Pointer auf ein Array (oder ähnlich) arbeiten muss.
    Und die Struktur muss in einem separatem Programm abgefüllt werden! Also keine Funktion im Hauptprogramm!

    Doch geht das überhaupt mit AutoIt?
    Es muss kein Array sein, sondern einfach eine Array ähnliche Struktur.....
    Das Element sollte Platz bieten für > 100 Millionen Einträge

    Mein Programm funktioniert schon jetzt (mit Scripting.Dictionary), einfach wird alles im Hauptprogramm gemacht und deshalb sequentiell. Dies möchte ich optimieren.

    Gruss Veronesi

  • Interprozesskommunikation ist nicht trivial und man kann durch die nötigen Wartezeiten untereinander auch das Gegenteil erreichen von dem was man möchte.
    Statt ein derart kompliziertes Multi-Processing in AutoIt zu implementieren kommst du denke ich besser wenn du für den Fall auf eine Sprache ausweicht welche Threading beherrscht (Python z.B.).

    Desweiteren wäre es interessant ob man den sequentiellen Teil nicht doch noch deutlich optimieren könnte.

  • Hallo AspirinJunkie

    Danke für die Antwort.
    In dem Fall werde ich vermutlich auf Delphi ausweichen (müssen).
    Das müssen dann aber meine Arbeitskollegen übernehmen!

    Ich habe mal einen Test gemacht:
    Mein Programmteil, welcher sequentiell ausgeführt wird gegen den gleichen Programmteil, aber in Delphi geschrieben.
    Das Resultat war, dass mein Script bloss 3% langsamer war, als das in Delphi!

    Warum?
    Weil es bei meinem Script zu 99% auf die Festplatten / CD / DVD / .... Geschwindigkeit ankommt.
    Es muss (im Moment) praktisch nichts damit gemacht oder gerechnet werden.

    Deshalb wäre es eine schöne Aufgabe in AutoIt. Aber schön wäre natürlich gewisse parallelisierung!

    Danke für Deine Antwort.
    Wenn niemand ein einfaches Beispiel oder eine "einfach" zu verwendende UDF hat, dann werde ich das wohl den Spezialisten bei uns übergeben müssen. Die machen dann das in Delphi, C# oder WPF.

    Veronesi

    Edit
    Ach ja: So viel müsste ich ja gar nicht zwischen den Scripten kommunizieren.
    Ich müsste einfach zwei Scripte mit Parametern starten.
    Die können dann ca. 1h parallel arbeiten und müssen nie irgendwelche Daten austauschen.

    Erst nach dieser Stunde müsste jedes Script einmal den Pointer auf das Scripting.Dictionary übergeben!

  • haltet mich für Dämmlich oder Ahnungslos aber

    Spoiler anzeigen
    Zitat von Hilfe


    Bemerkungen
    Es wird ObjCreate() verwendet, um eine neue Instanz zu der verweisenden Applikation zu erhalten.
    Möchte man die Instanz mit einem bereits existierenden Prozess verbinden, ist stattdessen ObjGet()zu benutzen.


    X(

  • Skilkor

    Tönt in der Tat interessant.
    Doch ich weiss leider nicht, ob man damit auf ein Scripting.Dictionary zugreiffen kann.
    Vermutlich eher nicht.

    Aber da müssten die Spezialisten eher etwas dazu wissen...
    Das ist leider nicht mein Beruf ;)

  • AutoItObject enthält eine Funktion, um ein Objekt global allen Programmen zur Verfügung zu stellen.
    Wenn die meiste Zeit bei dir aber bei Festplattenzugriffen drauf geht, kannst du auch folgendes versuchen, das ist vermutlich besser aber in AutoIt viel komplexer:
    - Erstelle 4 Bereiche für geteilten Speicher
    - Prozess 1 sperrt und füllt Bereich 1
    - Prozess 2 sperrt und füllt Bereich 2
    - Prozess 0 wartet bis bereiche 1, 2 frei sind, sperrt sie und liest dann
    - Prozess 1 sperrt und füllt solange Bereich 3
    - Prozess 2 sperrt und füllt solange Bereich 4
    - Prozess 0 wartet bis bereiche 3, 4 frei sind und sperrt sie und ließt dann
    - Prozess 1 - Bereich 1 usw usf... ;)

  • Hallo progandy

    So wie Du es beschrieben hast, möchte ich es ja in AutoIt umsetzen.
    Mein Hauptprogramm (Prozess 0) erstellt zwei Scripting.Dictionary.
    Dann startet es zwei weitere Programme (Prozess 1 und Prozess 2) und übergibt denen z.B. einen Pointer auf diese Dictionaries. Die beiden Prozesse befüllen dann diese.
    Das Hauptprogramm (Prozess 0) überwacht die beiden Prozesse 1 und 2 und wartet bis sie fertig sind.
    Anschliessend kann das Hauptprogramm (Prozess 0) mit den Daten arbeiten.

    Genauso möchte ich es und genau so habe ich Deine Beschreibung verstanden.
    Allerdings muss anschliessend nicht noch mal der Prozess 1 und 2 gestartet werden. Ich brauche das nur einmal!

    Aber die Schwierigkeit ist, diese Bereiche zu erstellen und Pointer dazu zu übergeben. Ich möchte ja nicht die ganzen
    Daten hin und her kopieren, denn das dauert viel zu lange.

    Kannst Du mir einen Tipp geben, wie ich das mit AutoItObject bewerkstelligen kann?

    Edit:
    Möglicherweise geht es damit: _AutoItObject_IDispatchToPtr() und _AutoItObject_PtrToIDispatch().
    Aber wie kann ich dann diese Pointer einem anderen Programm übergeben? Mit diesemScript übergebe ich ja nur Strings!

    Einmal editiert, zuletzt von veronesi (10. September 2012 um 15:43)

  • So?
    [ offen ] Objekte übergeben (Der Code sind 2 Skripte in einer Datei)

    Zitat

    Allerdings muss anschliessend nicht noch mal der Prozess 1 und 2 gestartet werden. Ich brauche das nur einmal!


    Das war ein erweitertes Beispiel, die Prozesse werden nicht erneut gestartet sondern laufen parallel. P1 und P2 füllen jeweils einen Buffer, P0 leert dann diese Buffer langsam wieder währent P1 und P2 andere Buffer füllen. Wenn die einen Buffer wieder leer und die anderen voll sind, nimmt P0 die vollen Buffer und leert sie währent P1 und P2 die leeren wieder füllt.

    Zitat

    Aber die Schwierigkeit ist, diese Bereiche zu erstellen und Pointer dazu zu übergeben. Ich möchte ja nicht die ganzen
    Daten hin und her kopieren, denn das dauert viel zu lange.


    Wirklich? Ich dachte dass das lesen von der Festplatte so langsam ist, dass das rumkopieren im Speicher egal ist.

  • Wirklich? Ich dachte dass das lesen von der Festplatte so langsam ist, dass das rumkopieren im Speicher egal ist.


    Je nach Speichermedium ist es tatsächlich egal. Aber es gibt auch Speichermedien, welche ich bedienen soll, bei welchen es doch einen erheblichen Unterschied macht!

    Ähhhhh...... die Funktion _AutoItObject_RegisterObject() gibt es bei mir gar nicht. (Ich habe AutoItObject V1.2.2.0)

    Edit:
    Peinlich!
    Ich lese zuerst mal den Thread den Du verlinkt hast, nochmals genau durch ;)
    Es geht ja um etwas änliches! Das alte Programm habe ich gar nicht mehr präsent gehabt :cursing:

  • Da mit Autorität RAM mannipullation möglich ist wäre es eventuell ein Ansatz den RAM von Programm 0 in bestimmten Sätzen durch Programm 1 und 2 zu manipulieren ?
    Wäre sehr interessant !

    -

  • Nachid
    Interessant vielleicht schon, aber für mich dann doch ein definitiver Overkill.
    Dann lasse ich mein Programm lieber, wie es ist!

    @progandy
    Ich finde die Funktion _AutoItObject_RegisterObject wirklich nicht mehr.
    Wo kann ich die finden?

  • Hi,
    wie kommst du darauf, das das

    Zitat

    Dann kann ich das entweder tun, in dem ich zuerst alle benötigten Daten vom Medium A einlese, danach vom Medium B und danach nach meinen Kriterien / Vorgaben vergleiche!

    langsam ist?
    Die Frage, die sich mir stellt ist, wie du die Daten liest. Bzw. was musst du miteinander vergleichen?
    Ein

    Code
    X:\Dir /a /s > Inhalt_Laufwerk_X.txt

    schreibt alle auf Laufwerk X abgelegten Dateien in eine Datei. Viel schneller wirds kaum gehen, ausser man greift auf den Windows-eigenen Index-Dienst zurück. Da hat man aber das Problem, dass ggf. nicht alle Files indiziert wurden....
    Du kannst jetzt natürlich "parallel" arbeiten, indem du einfach eine neue CMD öffnest und das nächste Laufwerk auswählst.
    Das geht mit beliebig vielen Laufwerken. Dein Prozessor wird dabei wahrscheinlich in irgendeinen Schlafmodus fallen, aber die Dauer des Gerödels auf den angeschlossenen Platten ist abhängig von der Übertragungsrate des Laufwerks.

    Schlussendlich hast du nach ca. 1-2 Minuten eine Handvoll Dateien mit den Inhalten aller Laufwerke, und die kannst du öffnen und beliebig miteinander vergleichen.

  • Hallo Andy

    Leider gibt mir dieser Dos Befehl nicht alle benötigten Daten zurück.
    Ich muss aus verschiedenen Medien Dateien (evtl. auch Ordner) auslesen. Von jeder Datei brauche ich zudem das Attribut Company.
    Dieses Property kann ich bequem mit Shell.Application........... .NameSpace.......... .Parsename......... .GetDetailsOf herauslesen.
    Das klappt wunderbar. Ich brauche leider genau dieses Attribut von den Dateien.

    Und es handelt sich um die verschiedensten Medien (USB-Stick, CD, DVD, Backup-Tape, USB-HDD, IDE-HDD, SAS-HDD, SSD-HDD, SAN-Speicherarray,....)
    Auf den kleinsten sind bloss ca. 50'000 Dateien drauf.
    Aber auf den grössten (habe ich letzte Nacht extra zählen lassen) sind bis zu 11'975'868'102 Dateien drauf. (In Worten: Fast 12 Milliarden Dateien)

    Wenn ich nun zwei solch grosse Datenträger vergleichen muss, und die Daten sequentiell einlese, dauert das schon ein Weilchen!
    Könnte ich sie parallel einlesen, dann geht das (evtl.) etwas schneller.
    Wenn ich zweimal 12 Milliarden Dateien in eine (riesige) Datei schreiben muss, und diese danach auch wieder komplett zweimal auslesen um zu vergleichen, bin ich überzeugt, dass dies langsamer ist!
    In einem kleinen Test habe ich folgendes herausgefunden:

    • Einlesen Verzeichnis 1 (knapp 1 Million Dateien): 60 Sekunden
    • Einlesen Verzeichnis 2 (knapp 1 Million Dateien): 68 Sekunden
    • Vergleich der beiden Verzeichnisse: 3.8 Sekunden (Ich hatte einen Wassergekühlten Superblade von unserer internen IT zur Verfügung, also ein schnelles Teil)

    Klar, ich lese aber nicht alle Dateien auf einmal in den Speicher. Dann hätte ich zu wenig RAM. Ich lese immer nur entweder 1 Verzeichnis oder maximal 1'000'000 Dateien ein um danach zu vergleichen.
    Auch der Vorschlag von Progandy während der Vergleichsphase weitere Datein einzulesen ist gut.

  • @progandy
    Vielen Dank für den Link!

    Ich konnte das nun testen. Und leider muss ich sagen, dass diese Lösung massiv langsamer ist, als wenn ich sequentiell einlese.
    (Also das gleiche Ergebnis, wie ich schon früher erhalten habe! Sorry, aber ich konnte mich wirklich nicht mehr an diesen Thread erinnern!)

    Ich werde das Ganze nun wirklich so machen, dass ich es sequentiell einlese und immer nach 1 Verzeichnis (oder ca. 1 Million Dateien) die Datensätze vergleiche.
    Alles andere scheint mir wirklich ein zu grosser Aufwand zu sein, mit fragwürdigen Zeiteinsparungen.

    Vielen Dank Euch allen für die angeregte Diskusion!
    Gruss, Veronesi

    Edit:
    Nun habe ich soeben noch folgendes herausgefunden:
    Wenn man in ein Scripting.Dictionary 300'000 Elemente speichert, dann dauert das etwa doppelt so lange, wie wenn man drei Scripting.Dictionary Objekte mit jeweils 100'000 Elementen befüllt.
    Allenfalls könnte ich dies noch als Geschwindigkeitszuwachs nehmen, dass ich maximal 100'000 Dateien suche und dann schon vergleiche!

    Hier mal ein erweitertes Beispiel zum testen dazu:

    Spoiler anzeigen
    [autoit]

    Global $oSource1 = ObjCreate("Scripting.Dictionary")
    Global $oSource2 = ObjCreate("Scripting.Dictionary")
    Global $oSource3 = ObjCreate("Scripting.Dictionary")
    Global $oSource4 = ObjCreate("Scripting.Dictionary")
    Global $oSource5 = ObjCreate("Scripting.Dictionary")
    Global $oSource6 = ObjCreate("Scripting.Dictionary")
    Global $oSource7 = ObjCreate("Scripting.Dictionary")
    Global $oSource8 = ObjCreate("Scripting.Dictionary")
    Global $oSource9 = ObjCreate("Scripting.Dictionary")
    Global $oSource10 = ObjCreate("Scripting.Dictionary")

    [/autoit] [autoit][/autoit] [autoit]

    Global $iMax = 100000
    Global $iTimer = TimerInit()
    For $i = 1 To $iMax
    $oSource1.Add($i, "Teststring")
    Next
    For $i = 1 To $iMax
    $oSource2.Add($i, "Teststring")
    Next
    For $i = 1 To $iMax
    $oSource3.Add($i, "Teststring")
    Next
    For $i = 1 To $iMax
    $oSource4.Add($i, "Teststring")
    Next
    For $i = 1 To $iMax
    $oSource5.Add($i, "Teststring")
    Next
    For $i = 1 To $iMax
    $oSource6.Add($i, "Teststring")
    Next
    For $i = 1 To $iMax
    $oSource7.Add($i, "Teststring")
    Next
    For $i = 1 To $iMax
    $oSource8.Add($i, "Teststring")
    Next
    For $i = 1 To $iMax
    $oSource9.Add($i, "Teststring")
    Next
    For $i = 1 To $iMax
    $oSource10.Add($i, "Teststring")
    Next
    Global $iEnd = TimerDiff($iTimer)
    ConsoleWrite("10x 100'000: " & Round($iEnd, 2) & "ms" & @CRLF)
    $oSource1 = 0
    $oSource2 = 0
    $oSource3 = 0
    $oSource4 = 0
    $oSource5 = 0
    $oSource6 = 0
    $oSource7 = 0
    $oSource8 = 0
    $oSource9 = 0
    $oSource10 = 0

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    $oSource1 = ObjCreate("Scripting.Dictionary")
    $iMax = 1000000
    $iTimer = TimerInit()
    For $i = 1 To $iMax
    $oSource1.Add($i, "Teststring")
    Next
    $iEnd = TimerDiff($iTimer)
    ConsoleWrite("1'000'000: " & Round($iEnd, 2) & "ms" & @CRLF)
    $oSource1 = 0

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit]

    3 Mal editiert, zuletzt von veronesi (11. September 2012 um 07:39)

  • Die Zeit für das Einfügen eines Elementes in ein Scripting.Dictionary skaliert anscheinend mit Anzahl der enthaltenen Elemente.
    Für derart große Datenmengen ist diese Datenstruktur daher ungeeignet.
    Ein assoziatives Array welches man in AutoIt nutzen kann und welches hingegen eine konstante Einfügezeit bietet wäre die Hashtable aus dem .Net-Framework.

    Hier mal ein kleines Beispiel um dessen Vorteile gegenüber Scripting.Dictionary bei sehr vielen Elementen zu demonstrieren:

    Vergleich Scripting.Dictionary und System.Collections.Hashtable bei großen Datenmengen
    [autoit]

    Global $oDic = ObjCreate("Scripting.Dictionary")
    Global $oHash = ObjCreate("System.Collections.Hashtable")

    [/autoit] [autoit][/autoit] [autoit]

    Global Const $N = 1e6

    [/autoit] [autoit][/autoit] [autoit]

    $iT = TimerInit()
    For $i = 1 To $N
    $oDic.add($i, "Teststring")
    Next
    $iT = TimerDiff($iT)
    ConsoleWrite(StringFormat("Dictionary:\t%7.1f\n", $it))

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    $iT = TimerInit()
    For $i = 1 To $N
    $oHash.add($i, "Teststring")
    Next
    $iT = TimerDiff($iT)
    ConsoleWrite(StringFormat("Hash-Table:\t%7.1f\n", $it))

    [/autoit]
  • Danke!
    Ich habe die Hashtable auch schon ausprobiert.
    Aber leider muss ich auch für kleinere Verzeichnisse (50'000) Dateien schnell sein.
    Und da ist die Hashtable (jedenfalls bei mir) massiv langsamer.

    Bei etwa 600'000 Einträgen sind die beiden Objekte in etwa gleich schnell.
    (Habe ich hier auch schon mal getestet: Link)

    Einmal editiert, zuletzt von veronesi (11. September 2012 um 09:01)