Sie sind nicht angemeldet.

  • Anmelden

1

26.06.2008, 16:46

MS Excel VBA, kennt sich da wer aus?

Habe mal ne Frage zur Performance und grundsätzlichen Machbarkeit.

Was ist besser, ein (mehrdimensionales) Array oder ein Collection-Object (oder Dictionary?)?

Bekanntlich ist ReDim Preserve Array ja sehr langsam, da dazu das gesamte Array kopiert werden muss. Collection.add ist da viel freundlicher.

Problematisch ist auch, dass ich in einem mehrdimensionalen Array nur die letzte Dimension überhaupt mit ReDim ändern kann, alle vorherigen sind fix.
Jagged Arrays (also Arrays of Arrays) zu verwenden ist etwas unpraktisch, wäre aber prinzipiell eine Option.

Ich brauche die Arrays oder was auch immer als Strukturen für Matrizen, will also viele Matrizenoperationen (zumindest Matrixmultiplikation) damit ausführen.
Wie geht dies überhaupt bei einem Collection-Object? Hat dieses eine fixe Reihenfolge über welche ich iterieren kann oder muss ich die Reihenfolge mit dem Schlüssel ansprechen? Wäre dann eventuell ein Dictionary-Object besser, damit ich die Schlüsselwerte nachträglich verändern kann?

Ich will aus Matrizen einzelne Zeilen herausstreichen oder diese hinzufügen. Bei ersterem ist natürlich die Position wichtig, bei letzterem nicht. Jetzt überlege ich, welche Datenstruktur ich dafür am besten nehmen sollte.
Wie gesagt, eindeutiges ansprechen ist wichtig, immer über den Key würde nerven und unpraktikabel sein (wenn aus der Matrix schon einige Zeilen aus der Mitte herausgestrichen wurden und am Ende neue hinzugekommen sind).

Irgendwelche Ideen oder Vorschläge?

2

04.07.2008, 12:46

So, habe jetzt ein bißchen rumgetestet.

Grundsätzlich iteriert man bei Collections über

Quellcode

1
for each obj in Collection
bei Arrays über den Index mit

Quellcode

1
for Index = LBound(array) to Ubound(array)
Dies ist auch aarays auch richtig schnell. Collection ist da lahmer, Vorteil ist die Bandbreite an Datentypen. Dictionary-Objecte sind hingegen wieder richtig schell.

Zur Geschwindigkeitsmessung habe ich einfach modulweit

Quellcode

1
2
Public Declare Function GetTickCount Lib "kernel32" () As Long
Public start As Long
gesetzt und kann damit in jeder Prozedur mittels

Quellcode

1
2
3
start = GetTickCount
'Code, welcher untersucht werden soll
Debug.Print "time: " & (GetTickCount - start) / 1000 & " s"
die benötige Zeit auslesen.
Meist macht es nur in Schleifen Sinn, dafür muss man etwas rumtesten, dass der Code ca. zwischen 1 und 10 Sekunden läuft. Dann kann man die Performance erhöhen. Natürlich ist diese Methode nicht sehr genau, ist die reine Rechenzeit nie für Performancemessung, aber man bekokommt ganz gute und auch stabile Ergebnisse. Wenn man Lust hat, kann man sich auch ne eigene Klasse für den Timmer mit den 2 Methoden start und stop bauen, aber bei insgesamt 4 zeilen Code halte ich das für unnötig, hinzu könnte der Overhead die Performancemessung verfälschen und ich müsste ihn erst wieder herausrechnen.

Man sollte beispielsweise immer die Maximum-Function selbst definieren mittels

Quellcode

1
2
3
Public Function max(x, y)
  If (x >= y) Then max = x Else max = y
End Function
anstatt

Quellcode

1
2
3
Public Function max(x, y)
  max = WorksheetFunction.Max(x, y)
End Function
zu benutzen. Das ist ca. 50x schneller, also von Größenordnung ln(50)/ln(10) = 1.7. Krass!
Wenn man zudem noch die Funktionen nicht als Variant sondern als explizter Datentyp (Double, Integer, Long) definiert, ist die selbstgeschriebene Function noch einmal 58% schneller - bei der Worksheet.Function gibt es dagegen keinen merklichen Geschwindigkeitsvorteil.

Ansonsten benutze ich für das Proket recht intensiv selbstdefinierte Klassen. Leider braucht man für jede neue Klasse ein neues Klassenmodul. ?( Ist jetzt nicht unbedingt so der Performancebringer, aber für die Lesbarkeit des Codes extrem hilfreich, außerdem denkt es sich so leichter in Problemlösungskategorien.
So ein größeres Projekt ist mal ne nette Erfahrung für einen, der sonst immer nur rein Lösungsorientiert programmiert hatte und deswegen nur prozedural.

Mittels der Klassenlösung kann ich auch viele 1-dimensionale Arrays verwenden. Erstens bringt dies auch wieder Geschwindigkeit, da VBA Excel 1-dim arrays sehr effizient benutzen kann, zum anderen fällt dann das resize problem nicht mehr so ins Gewicht.
Theoretisch könnte ich noch mti CopyMemory oder ähnlichen Dingen direkt an der Speicheradresse arbeiten, aber denke das ist hier nicht notwendig - wäre aber sehr schnell.

So, dass waren mal ein paar Erfahrungen, welche ich in den letzten Tagen zum Thema gemacht habe. Hilft vielleicht dem ein oder anderem.

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »AtroX_Worf« (04.07.2008, 13:04)