Seite 1 von 1

Intelligentes Array

Verfasst: 02.04.2009 20:06
von cxAlex
Hier mal ein Code für ein Intelligentes Array. Ich hab es mir für ein Management System ausgedacht, speziell für sehr hohe Indexes, wie Bsp. hWnds, ClientIDs, ....

Der Witz daran ist das z.B. wenn ich den Index 1205381 habe nicht 1 Mio. Einträge füllen muss sondern nur der Eintrag 1205381 erstellt wird. Hat natürlich zur Folge das jeder Eintrag mehr Platz im Speicher braucht als bei einem klassischem, ist also eher nicht dazu geeignet wenn man ein Array von 0 - ... füllen will. Das ganze basiert auf Ebenen. Jede Ebene umfast die Zahlen 0 - 9. Ist die Zahl größer wird eine Eben tiefer gegangen. 1205381 währe auf Ebene 1-8-3-5-0-2-1 (Tiefe 7).

Das Array ist trotzdem sehr schnell, wobei es die Möglichkeit gibt die Zahl vor dem Indexen zu drehen, was sich wie folgt auswirkt:
Bereich: 0 - 10 Mio, ohne drehen hat geschrieben:Schreiben & Erstellen: Zugriffe: 6958942.00/Sek
Lesen: Zugriffe: 8097166.00/Sek
Aufräumen: 0.44 sek
Bereich: 0 - 10 Mio, mit drehen hat geschrieben:Schreiben & Erstellen: Zugriffe: 4849660.50/Sek
Lesen: Zugriffe: 5333333.50/Sek
Aufräumen: 0.19 sek
Bereich: 10 Mio - 20 Mio, ohne drehen hat geschrieben:Schreiben & Erstellen: Zugriffe: 2006420.50/Sek
Lesen: Zugriffe: 3315649.75/Sek
Aufräumen: 12.77 sek
Bereich: 10 Mio - 20 Mio, mit drehen hat geschrieben:Schreiben & Erstellen: Zugriffe: 4050222.75/Sek
Lesen: Zugriffe: 4384042.00/Sek
Aufräumen: 0.17 sek
Man sieht, bei kleineren Zahlen drückt das drehen auf die Performance, bei höheren verbessert es sie.Auf jedem Fall bringt das drehen eine bessere Speichernutzung und eine schneller Aufräumzeit (im 2. Test 75x fach schneller!)

Viel Spaß damit, Gruß cxAlex:

PS: Währe froh wenn jemand testen könnte ob der x64 - DrehCode funktioniert, hab hier leider keine Möglichkeit das zu testen.

Code: Alles auswählen

; ------------------------------------------------------------------------------------
; Intelligentes Array
; Author: Alexander Aigner
; PB 4.3+ x86/x64
; ------------------------------------------------------------------------------------

Structure IArray_Data ; Daten Element
  *Next.IArray ; Tiefere Ebene
  Value.i ; Eintrag
EndStructure

Structure IArray_Holder ; Array Holder
  Entry.IArray_Data[10]
EndStructure

Structure IArray ; Array Main - Struktur
  IArray.IArray_Holder
EndStructure

Procedure New_IArray() ; Neues Array anlegen
  Protected *IArray = AllocateMemory(SizeOf(IArray))
  ProcedureReturn *IArray
EndProcedure

Procedure TurnVal(Val) ; (x86, x64)
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
    !MOV EAX, dword[p.v_Val]
    !MOV EBX, 10
    !MOV ECX, 0
    !@@:
    !OR EAX, EAX
    !JZ @f
    !IMUL ECX, EBX
    !CDQ
    !IDIV EBX
    !ADD ECX, EDX
    !JMP @b
    !@@:
    !MOV EAX, ECX
    ProcedureReturn
  CompilerElse
    !MOV RAX, qword[p.v_Val]
    !MOV RBX, 10
    !MOV RCX, 0
    !@@:
    !OR RAX, RAX
    !JZ @f
    !IMUL RCX, RBX
    !CDQ
    !IDIV RBX
    !ADD RCX, RDX
    !JMP @b
    !@@:
    !MOV RAX, RCX
    ProcedureReturn
  CompilerEndIf
EndProcedure

Procedure IArray_Set(*IArray.IArray, Index.i, Value.i) ; Eintrag setzen
  Protected *IArrayEntry.IArray_Data, *cur.IArray_Holder
  *cur = @*IArray\IArray
  
  ; Probeweise auskommntieren:
  Index = TurnVal(Index)
  
  ; Element finden
  Repeat
    ; Aktuelles Element
    *IArrayEntry = @*cur\Entry[Index%10]
    Index/10
    If Index ; Weiter in die Tiefe
      If Not *IArrayEntry\Next ; Noch nicht angelegt?
        *IArrayEntry\Next = AllocateMemory(SizeOf(IArray))
      EndIf
      ; eine Ebene tiefer gehen
      *cur = *IArrayEntry\Next\IArray
    EndIf
  Until Not Index
  
  ; Eintrag schreiben
  *IArrayEntry\Value = Value
EndProcedure

Procedure IArray_Get(*IArray.IArray, Index.i)
  Protected *IArrayEntry.IArray_Data, *cur.IArray_Holder
  
  ; Probeweise auskommntieren:
  Index = TurnVal(Index)
  
  ; Element finden ( Siehe oben)
  *cur = @*IArray\IArray
  Repeat
    *IArrayEntry = @*cur\Entry[Index%10]
    Index/10
    If Index
      If Not *IArrayEntry\Next
        ; Eintrag gibts noch nicht
        ProcedureReturn 0
      EndIf
      *cur = *IArrayEntry\Next\IArray
    EndIf
  Until Not Index
  
  ; Wert lesen
  ProcedureReturn *IArrayEntry\Value
EndProcedure

; Freigabe Procedure ist hoch-rekursiv, kann etwas länger dauern...
Procedure _IArray_FreeSub(*IData.IArray_Holder)
  For i = 0 To 9
    If *IData\Entry[i]\Next
      _IArray_FreeSub(@*IData\Entry[i]\Next\IArray)
    EndIf
  Next
  FreeMemory(*IData)
EndProcedure

; Array freigeben
Procedure IArray_Free(*IArray.IArray)
  _IArray_FreeSub(*IArray)
EndProcedure

#SCNT = 10000000
#CNT = 20000000
IArray = New_IArray()

t = ElapsedMilliseconds()
For i = #SCNT To #CNT
  IArray_Set(IArray, i, i*2)
Next
t1 = ElapsedMilliseconds()-t

t = ElapsedMilliseconds()
For i = #SCNT To #CNT
  IArray_Get(IArray, i)
Next
t2 = ElapsedMilliseconds()-t

t = ElapsedMilliseconds()
IArray_Free(IArray)
t3 = ElapsedMilliseconds()-t

MessageRequester("test", "Gesamt: Einträge: " + Str(#CNT-#SCNT) + Chr(13) + "Schreiben & Erstellen: Zugriffe: " + StrF((#CNT-#SCNT)/t1*1000, 2) + "/Sek" + Chr(13) + "Lesen: Zugriffe: " + StrF((#CNT-#SCNT)/t2*1000, 2) + "/Sek" + Chr(13) + "Aufräumen: " + StrF(t3/1000, 2) + " sek")

Verfasst: 03.04.2009 01:42
von ts-soft
x86 hat geschrieben:---------------------------
test
---------------------------
Gesamt: Einträge: 10000000
Schreiben & Erstellen: Zugriffe: 2433090.00/Sek
Lesen: Zugriffe: 2510040.25/Sek
Aufräumen: 0.16 sek
---------------------------
OK
---------------------------
x64 hat geschrieben:---------------------------
test
---------------------------
Gesamt: Einträge: 10000000
Schreiben & Erstellen: Zugriffe: 1464557.75/Sek
Lesen: Zugriffe: 1523693.38/Sek
Aufräumen: 0.16 sek
---------------------------
OK
---------------------------
Hab aber nur getestet obs läuft, ob die Ergebnisse korrekt sind kann ich
nicht sagen, dafür müßte ich erstmal drüber Nachdenken wofür das gut ist :mrgreen:

Verfasst: 06.04.2009 11:16
von coder
Sehr interressant :allright:
Besonders nützlich für die Verwaltung von Network-Clients auf einen Server über die Client-IDs

Verfasst: 06.04.2009 13:01
von Kaeru Gaman
das müßte man in der Verwendbarkeit und Performanz mal mit Hashtables vergleichen...
oder entspricht das einem Hashtabel von der Funktionalität her?
damit kenn ich mich leider zu wenig aus.


(btw: Index, Pl. Indices bzw. Indizes, V. indizieren)

Verfasst: 06.04.2009 15:25
von coder
Naja, im Prinzip macht es ja das gleiche wie eine HashTable...
Ich versteh bloß noch nicht so richt, wie das hier funktioniert...

Verfasst: 06.04.2009 18:40
von Josef Sniatecki
Sehr interessant. Habe bisher nur mit Strings sowas ähnliches wie
ein intelligentes Array gemacht: http://www.purebasic.fr/german/viewtopic.php?t=17083

Habe also für den Zugriff auf ein Item namens "ABC" immer "A","B","C"
gemacht.
Mit Zahlen ist das natürlich ein wenig anders (und schwerer).

Verfasst: 06.04.2009 19:06
von edel
Als Vergleich mal eine einfache stl map
---------------------------
test
---------------------------
Gesamt: Einträge: 10000000
Schreiben & Erstellen: Zugriffe: 1868809.63/Sek
Lesen: Zugriffe: 4006410.25/Sek
Aufräumen: 0.00 sek
---------------------------
OK
---------------------------

Verfasst: 07.04.2009 08:25
von Froggerprogger
Das Verfahren könnte ggf. noch nicht unwesentlich beschleunigt werden, wenn nicht das Dezimalsystem verwendet wird, sondern bspw. das Hexadezimalsystem, also wenn jede Ebene in 16 Unterebenen verzweigen kann. Dann spart man sich die ganzen Divisionen mit 10 und kann stattdessen einfach mit "ID & $F" bzw. "ID >> 4" verzweigen.

Trotzdem denke ich mal, dass eine auf Zahlen optimierte Hashtable noch deutlich schneller ist.