Thema Listen vs. String: Wir hatten ja schon einmal einen Thread dazu. Listen scheinen
weniger effektiv im Speicher verarbeitet zu werden, als es bei Strings der Fall ist. Innerhalb
LSL scheint es eher zur Speicherfragmentierung zu kommen, wenn Listen im Spiel
sind.
Listen sind bequem und ich würde sie gerne öfter einsetzen. Aber ich bin misstrauisch
und gehe mit Strings den sichereren Weg. Immer dort, wo viel und sehr lange an einem
Stück (also ohne zwischenzeitlichen Reset) zugegriffen werden muss.
Bei gleichgrossen Offsetabständen ist das simpel.
Listen brauchen manchmal mehr Speicher als Strings, das ist schon richtig. Das liegt daran, dass eben zusätzlich zu den in den Listen abgespeicherten Daten noch die Metadaten der Liste gespeichert werden müssen. Normale Strings dagegen sind im Grunde einfach ein kurzer "Meta-Kopf" und dann einfach verkettete Chars (also letztendlich einfach Zahlen zwischen 0 und 256), die damit jeweils weitere 1byte belegen. Und allenfalls noch ein Zero-Byte als Endmarker.
Beispiel:
Ein 4-Tupel aus 1.001, 1.002, 1.003 und 1.004 kann auf viele verschiedene Arten im Script gespeichert werden. Z.B. im default state als lokale Variable.
Als String sind das 23 Chars, also braucht das ganze 12 byte + 23 byte, also 35 Byte.
Speichert man das in eine Liste sind das 4 floats, die da gespeichert werden müssen, braucht es mehr speicher:
15 byte + (4 x 7byte) = 43 Byte.
Als rotation gespeichert braucht es 39 Byte.
Dafür ist allerdings der Zugriff auf diese Werte dann wiederum ganz unterschiedlich.
Auf Komponenten von Rotationen kann man mit .x .y .z und .s absolut unkompliziert zugreifen (genau wie auch auf Vektoren), da dort einfach floats liegen. Man braucht also nicht mal ein Typecasting.
Der Zugriff auf Listen ist bisschen komplizierter, mit llList2Float braucht man schon eine Funktion dafür, die für jeden Float aufgerufen werden muss.
Mit Strings ist das dann wiederum noch mal komplizierter - hier braucht man dann ein typecasting um aus dem string Floats zu machen. Davor muss allerdings der jeweilige Float aus dem String ausgeschnitten werden - weswegen man den String leider parsen muss, wenn man keine fix formatierten floats speichert. D.h. man muss da den Anfang des Floats finden, den Punkt im Float und dann noch die zugehörigen Nachkommastellen. Einfach weil ein Float ja z.B. 100.03 sein kann oder 1.002 oder 1.04 - oder wie auch immer. Eine Abhilfe hier ist das Arbeiten mit CSV oder besser JSON - aber auch damit ist das Ganze leider immer noch mit mehreren Schritten und Funktionen verbunden.
Da aber jeder Schritt mit einem potenziellen Fehler verbunden ist und auch Rechenzeit kostet halte ich selbst es meist "möglichst einfach". Und setze z.B. gerne Vektoren und Rotationen für Float-Tupel ein, da man auf diese super einfach zugreifen kann. Und sie trotzdem z.B. noch in Strided Lists verarbeiten kann usw.
Ansonsten arbeite ich wenn möglich auch mit Listen, da man mit diesen einfach ziemlich viel machen kann. Und da man aus diesen die Werte ohne Typecasting und loops sehr einfach raus kriegt (was ja auch wieder Speicher und Rechenzeit kostet).
Strings verwende ich eigentlich nur dann, wenn ich wirklich mit Strings arbeiten muss, etwa für die Ausgabe oder die Eingabe per Listener. Denn wenn man auf bestimmte Elemente zugreifen will, dann geht das eben nicht einfach mit einem Index ohne weiteres, man muss da dann schon vorher genau wissen aus wie vielen Chars ein Float z.B. besteht, einfach "Hol mir den Float an Index 3" reicht da nicht. Man muss dann schon wissen wie viele Digits das in der Zahl sind. Und sollte man sich mit dem Index vertun, dann spuckt das Programm nicht mal einen Fehler aus und läuft einfach weiter, was eine massive Fehlerquelle ist.
Und zum Speicher selbst:
Speicherfragmentiereung ist unter Mono in SL kein Thema mehr, da sind Scripte generell kein zusammenhängender Speicherbereich mehr wie noch unter LSO, der Speicher wird dynamisch und generell fragmentiert belegt. Und Scripte können so nicht nur (zumindest kurz, solange kein Stopcode kommt....) mehr als 64kB belegen, sondern beherrschen eben auch Bytecode sharing. D.h. Kopien von kompilierten Scripten auf dem selben Server belegen so für viele Funktionen und Konstanten usw. keinen eigenen Speicher mehr sondern verweisen auf ein bereits in den Speicher geladenes script. So dass für Kopien von Scripten im Wesentlichen nur noch die Speicherbereiche, die sich im Ablauf verändern können, neu belegt werden müssen. Wird allerdings ein Script nicht kopiert sondern auch noch neu kompiliert, dann geht das nicht mehr.