Wie größere Dateien laden?

Übersicht BlitzMax, BlitzMax NG Allgemein

Gehe zu Seite 1, 2  Weiter

Neue Antwort erstellen

bmurray

Betreff: Wie größere Dateien laden?

BeitragMi, Jun 10, 2020 16:27
Antworten mit Zitat
Benutzer-Profile anzeigen
Sagen wir in meinem Spiel gibt es 200 Länder, etwa 5.000 Regionen (Bundesländer, Kantone, Staaten, etc., also kleinere Verwaltungseinheiten) und 50.000 Städte. Jede Stadt hat 12 Attribute (Name, Land, Region, Einwohnerzahl, Längen- und Breitengrad, etc.). In meinen Programmen hatte ich bisher alles immer in eigenen Dateien gespeichert (also laender.dat, regionen.dat und staedte.dat). Wenn ich diese Dateien nun in meinem Spiel lade, dann dauert das ziemlich lange, etwa 13 Sekunden. Die Dateien sind nicht groß, zusammen etwa 3 MB. Die Laderoutine sind dann so aus:

Code: [AUSKLAPPEN]
 
Local datei:TStream = OpenFile("datenbank\staedte.dat")
Local wstadt:tStadt
   While Eof(datei) <> 1
      wstadt = New tStadt
      wstadt.name = ReadStringBB(datei)
      wstadt.id = ReadInt(datei)
      wstadt.land = ReadInt(datei)
      wstadt.gebiet = ReadInt(datei)
      wstadt.sprache = ReadInt(datei)
      wstadt.attraktivitaet = ReadInt(datei)
      wstadt.einwohner = ReadInt(datei)
      wstadt.einwohnerKat = ReadInt(datei)
      wstadt.breitengrad = ReadFloat(datei)
      wstadt.laengengrad = ReadFloat(datei)
      wstadt.hoehe = ReadInt(datei)
      wstadt.wetter = ReadInt(datei)            
      listeStaedte.AddLast(wstadt)
   Wend
        CloseFile(datei)


Ziemlich simpel, ich weiß. Gibt es einen schnelleren und besseren Weg, größere Daten zu laden?

count-doku

BeitragDo, Jun 11, 2020 7:41
Antworten mit Zitat
Benutzer-Profile anzeigen
Ich habe schon länger nichts mehr mit BlitzMax gemacht (zum Glück), könnte mir aber vorstellen, dass es schneller wäre entweder a) die ganze Datei auf einmal in den RAM zu laden und von dort weiter zu zerstückeln oder b) zumindest immer eine ganze Stadt auf einmal zu laden.

a)
Du benutzt direkt nach dem öffnen der Datei CopyStream (oder ähnliches) und kopierst die komplette Datei in einen RAM Stream oder Bank Stream (natürlich vorher die Bank erstellen oder Speicher reservieren). Danach benutzt du die gleiche Funktion wie jetzt um die Daten aus dem RAM zu lesen (dürfte schneller sein).

b)
Ich weiß nicht, was dein ReadStringBB macht, aber abgesehen von dem vermutlich variable length string am Anfang ist die Größe deiner Stadtinfo ja fest gegeben (4Byte pro Int und Float), also kannst du z.B. erst nur den Namen lesen (ReadString) und dann CopyBytes benutzen um die restlichen Infos in einen RAM Stream oder Bank Stream zu kopieren.

Natürlich kann man a&b auch kombinieren. Dein Ziel sollte es jedenfalls sein, viele kleine Lesezugriffe auf die HDD durch wenige größere abzulösen. Und 3MB kannst du problemlos vollständig in den RAM laden.

bmurray

BeitragDo, Jun 11, 2020 11:08
Antworten mit Zitat
Benutzer-Profile anzeigen
Vielen Dank, ich habe es mir Variante a) probiert. Allerdings habe ich noch nie mit tBank programmiert. Mein erster Versuch sieht so aus:

Code: [AUSKLAPPEN]
Local bank:TBank = CreateBank()
   Local bankstream:TBankStream = CreateBankStream(bank)
   CopyStream(datei, bankstream)

   While Eof(bankstream) <> 1
      
      wstadt = New tStadt
      wstadt.name = ReadStringBB(bankstream)
      wstadt.id = ReadInt(bankstream)
      wstadt.land = ReadInt(bankstream)
      wstadt.gebiet = ReadInt(bankstream)
      wstadt.sprache = ReadInt(bankstream)
      wstadt.attraktivitaet = ReadInt(bankstream)
      wstadt.einwohner = ReadInt(bankstream)
      wstadt.einwohnerKat = ReadInt(bankstream)
      wstadt.breitengrad = ReadFloat(bankstream)
      wstadt.laengengrad = ReadFloat(bankstream)
      wstadt.hoehe = ReadInt(bankstream)
      wstadt.wetter = ReadInt(bankstream)
      stadt:+[wstadt]            
      listeStaedte.AddLast(wstadt)
   Wend


Allerdings funktioniert dann EoF nicht, bzw. er liest die Daten nicht aus. In der Hilfe steht ja, dass man tBankStreams auch dort verwenden könne, wo tStreams erwartet werden, weswegen ich gerade unsicher bin, wie tBankStreams in meinem Fall gelesen werden können.


count-doku hat Folgendes geschrieben:
Ich weiß nicht, was dein ReadStringBB macht
. Der liest einfach nur den String und die Länge des Strings (was ReadString wohl braucht, und mit dem Pendant WriteStringBB gespeichert wird):

Code: [AUSKLAPPEN]
Function ReadStringBB:String(f:TStream)
   Local n:Int = ReadInt(f)
   Return ReadString(f, n)
End Function


count-doku hat Folgendes geschrieben:
Ich habe schon länger nichts mehr mit BlitzMax gemacht (zum Glück)


Darf ich fragen, wieso? Ich als Amateur-Hobby-Programmierer finde BlitzMax genial, gerade weil es so simpel ist. Immerhin konnte ich mein erstes Spiel auf Steam veröffentlichen, mit einer "komplexeren" Sprache hätte ich dies mangels ausreichender Programmierkenntnisse wohl nicht geschafft. Oder gibt es - unabhängig von den eigenen Programmierfähigkeiten - einen triftigen Grund, BlitzMax nicht mehr zu nutzen? (Ich frage deswegen, weil ich mir überlege, C++ zu lernen, bin allerdings nicht sicher, ob sich der Zeitaufwand lohnt, in Anbetracht dessen, dass ich mit BlitzMax eigentlich sehr gut klar komme)

count-doku

BeitragDo, Jun 11, 2020 19:58
Antworten mit Zitat
Benutzer-Profile anzeigen
Zitat:
Allerdings funktioniert dann EoF nicht, bzw. er liest die Daten nicht aus. In der Hilfe steht ja, dass man tBankStreams auch dort verwenden könne, wo tStreams erwartet werden, weswegen ich gerade unsicher bin, wie tBankStreams in meinem Fall gelesen werden können.

Dein Code sieht schon gut aus. Du musst nur bedenken, dass nach CopyStream der Stream Zeiger am Ende vom Stream steht (daher ist EOF sofort true). Du musst also nach dem CopyStream mit SeekStream wieder an den Anfang von der Bank springen.

Zitat:
count-doku hat Folgendes geschrieben:
Zitat:
Ich weiß nicht, was dein ReadStringBB macht

. Der liest einfach nur den String und die Länge des Strings (was ReadString wohl braucht, und mit dem Pendant WriteStringBB gespeichert wird)

Ok.

Zitat:
count-doku hat Folgendes geschrieben:
Zitat:
Ich habe schon länger nichts mehr mit BlitzMax gemacht (zum Glück)

War vielleicht etwas negativ. Im Prinzip ist BlitzMax (wie alle Blitzsprachen) für manche Sachen recht einfach. (3D Grafik bei B3D z.B., OpenGL mit BMax). Aber in anderen Sachen hat es keine Chance gegen Sprachen wie Java, C++ oder C#.
Dein Dateien laden ist ein prima Beispiel. In C# könnte ich die Instanzen der TStadt Klasse mit wenigen Zeilen Code direkt in ein XML oder Json serialisieren oder wieder laden. Ohne mir Gedanken über den Rest zu machen. Wenn es klein sein soll gibts den BinarySerializer, der genau das macht was du gerade per Hand programmierst. Auch ganze Objekte per Netzwerk (mit Verschlüsselung) zu übertragen ist mit Dingen wie Windows Communication Foundation (WCF) sehr einfach und flexibel. Außerdem wäre ich vorsichtig, wie lange die Blitz Sprachen noch laufen (auch wenn da BMax vermutlich besser dran ist).
Für kleinere 2D Spiele, stimme ich dir aber zu, ist BMax immer noch geeignet.

Zurück zu deinem Problem:
Ich habe mal ein Testprogramm geschrieben, wo ich die unterschiedlichen Ansätze probiert habe. (Siehe unten).
Leider dauert alles ca. gleich lang. Ich denke nochmal drüber nach, aber momentan kenne ich auch keinen schnelleren weg.

Meine Testdatei war am Ende ungefähr 28MB groß, und das Laden dauert:
BlitzMax: [AUSKLAPPEN]
'
' Old approach:
' Generating took: 497ms
' Saving took:1246ms
' Loading took: 9086ms
'
' New approach Bank:
' Generating took: 504ms
' Saving took: 1233ms
' Getting file took: 7762ms
' New loading (total) took: 9337ms
'
' New approach Ram
' Generating took: 493ms
' Saving took: 1237ms
' Getting file took: 7632ms
' New loading total took: 8885ms


BlitzMax: [AUSKLAPPEN]
SuperStrict
Framework brl.blitz
Import brl.filesystem
Import brl.stream
Import brl.linkedlist
Import brl.random
Import brl.standardio
Import brl.ramstream

'
' Old approach:
' Generating took: 497ms
' Saving took:1246ms
' Loading took: 9086ms
'
' New approach Bank:
' Generating took: 504ms
' Saving took: 1233ms
' Getting file took: 7762ms
' New loading (total) took: 9337ms
'
' New approach Ram
' Generating took: 493ms
' Saving took: 1237ms
' Getting file took: 7632ms
' New loading total took: 8885ms




' Define TStadt Type for sample data
Type TStadt
Global list:TList = CreateList()

Field name:String
Field id:Int
Field land:Int
Field gebiet:Int
End Type

Local time:Long = MilliSecs()

' Generate 5 sample cities
For Local i:Int = 0 To 1000000
Local stadt:TStadt = New TStadt

stadt.name = "Stadt " + Rand(0, 10000000)
stadt.id = i
stadt.land = i / 2
stadt.gebiet = i * 2

TStadt.list.AddLast(stadt)
Next

Print "Generating took: " + (MilliSecs() - time) + "ms"

time = MilliSecs()
' Save sample data
Local file:TStream = WriteStream(".\testdata.dat")
For Local stadt:TStadt = EachIn TStadt.list
WriteStringBB(file, stadt.name)
file.WriteInt(stadt.id)
file.WriteInt(stadt.land)
file.WriteInt(stadt.gebiet)
Next
file.Close()

Print "Saving took: " + (MilliSecs() - time) + "ms"

' Clear all data in city list
TStadt.list.Clear()

' only use one at a time and always regenerate testdata to not use memory caching
'OldLoad()
'NewLoadBank()
NewLoadRam()



Function NewLoadBank()
Local time:Long = MilliSecs()

Local file:TStream = ReadStream(".\testdata.dat")
Local bank:TBank = CreateBank(file.Size()) ' Init bank in filesize this prevents us from having to do resizing later on (although bank should auto resize)
Local bankStream:TBankStream = CreateBankStream(bank)
CopyStream(file, bankStream) ' This puts us at the end of the stream!
file.Close()

Print "Getting file took: " + (MilliSecs() - time) + "ms"

' So remember to use seek to get to the beginning again
bankStream.Seek(0)

While Not Eof(bankStream)
Local stadt:TStadt = New TStadt

stadt.name = ReadStringBB(bankStream)
stadt.id = bankStream.ReadInt()
stadt.land = bankStream.ReadInt()
stadt.gebiet = bankStream.ReadInt()

TStadt.list.AddLast(stadt)
Wend

bankStream.Close()

Print "New loading total took: " + (MilliSecs() - time) + "ms"
End Function

Function NewLoadRam()
Local time:Long = MilliSecs()

Local file:TStream = ReadStream(".\testdata.dat")
Local buf:Byte[] = New Byte[file.Size()]
Local ramStream:TRamStream = CreateRamStream(buf, file.Size(), 1, 1)
CopyStream(file, ramStream) ' This puts us at the end of the stream!
file.Close()

Print "Getting file took: " + (MilliSecs() - time) + "ms"

' So remember to use seek to get to the beginning again
ramStream.Seek(0)

While Not Eof(ramStream)
Local stadt:TStadt = New TStadt

stadt.name = ReadStringBB(ramStream)
stadt.id = ramStream.ReadInt()
stadt.land = ramStream.ReadInt()
stadt.gebiet = ramStream.ReadInt()

TStadt.list.AddLast(stadt)
Wend

ramStream.Close()

Print "New loading total took: " + (MilliSecs() - time) + "ms"
End Function



Function OldLoad()
Local time:Long = MilliSecs()

Local file:TStream = ReadStream(".\testdata.dat")

While Not Eof(file)
Local stadt:TStadt = New TStadt

stadt.name = ReadStringBB(file)
stadt.id = file.ReadInt()
stadt.land = file.ReadInt()
stadt.gebiet = file.ReadInt()

TStadt.list.AddLast(stadt)
Wend

file.Close()

Print "Old loading took: " + (MilliSecs() - time) + "ms"

End Function



Function ReadStringBB:String(f:TStream)
Local n:Int = f.ReadInt()
Return f.ReadString(n)
End Function
Function WriteStringBB(f:TStream, str:String)
Local n:Int = str.Length
f.WriteInt(n)
f.WriteString(str)
End Function

bmurray

BeitragSa, Jun 13, 2020 9:42
Antworten mit Zitat
Benutzer-Profile anzeigen
count-doku hat Folgendes geschrieben:
Zitat:
Allerdings funktioniert dann EoF nicht, bzw. er liest die Daten nicht aus. In der Hilfe steht ja, dass man tBankStreams auch dort verwenden könne, wo tStreams erwartet werden, weswegen ich gerade unsicher bin, wie tBankStreams in meinem Fall gelesen werden können.

Dein Code sieht schon gut aus. Du musst nur bedenken, dass nach CopyStream der Stream Zeiger am Ende vom Stream steht (daher ist EOF sofort true). Du musst also nach dem CopyStream mit SeekStream wieder an den Anfang von der Bank springen.


Ohhhh, danke dir! Ja, jetzt klappt's!

Zitat:

War vielleicht etwas negativ. Im Prinzip ist BlitzMax (wie alle Blitzsprachen) für manche Sachen recht einfach. (3D Grafik bei B3D z.B., OpenGL mit BMax). Aber in anderen Sachen hat es keine Chance gegen Sprachen wie Java, C++ oder C#.
Dein Dateien laden ist ein prima Beispiel. In C# könnte ich die Instanzen der TStadt Klasse mit wenigen Zeilen Code direkt in ein XML oder Json serialisieren oder wieder laden. Ohne mir Gedanken über den Rest zu machen. Wenn es klein sein soll gibts den BinarySerializer, der genau das macht was du gerade per Hand programmierst. Auch ganze Objekte per Netzwerk (mit Verschlüsselung) zu übertragen ist mit Dingen wie Windows Communication Foundation (WCF) sehr einfach und flexibel. Außerdem wäre ich vorsichtig, wie lange die Blitz Sprachen noch laufen (auch wenn da BMax vermutlich besser dran ist).
Für kleinere 2D Spiele, stimme ich dir aber zu, ist BMax immer noch geeignet.


Meine größte Sorge ist auch, dass BMax auf künftigen Betriebssystemen nicht mehr läuft. Kann man da abschätzen, wie da die Chancen stehen werden? Ich arbeite gerade am Nachfolger meines Spiels, wenn es absehbar ist, dass das mit BMax irgendwann nicht mehr läuft, würde ich früher auf eine modernere Sprache umsteigen (wohl C++).

Zitat:

Zurück zu deinem Problem:
Ich habe mal ein Testprogramm geschrieben, wo ich die unterschiedlichen Ansätze probiert habe. (Siehe unten).
Leider dauert alles ca. gleich lang. Ich denke nochmal drüber nach, aber momentan kenne ich auch keinen schnelleren weg.


Ok, dann muss ich wohl akzeptieren, dass es nicht schneller geht. Vielen Dank für Deine Hilfe!
 

CO2

ehemals "SirMO"

BeitragSa, Jun 13, 2020 14:09
Antworten mit Zitat
Benutzer-Profile anzeigen
Hallöchen,

Ein kurzer Hinweis noch, einfach weil es bisher nicht explizit genannt wurde: Kommen die 13 Sekunden aus einem Debug, oder einem Release-Build? Ich frage mich, weil ich damals, als ich mein Spiel noch in BlitzMax programmiert hatte, eine Tilemap hatte, die unoptimiert gespeichert wurde. Die Datei war etwa 100 MB groß, da dauerte das Laden der Daten auch so 15 bis 20 Sekunden im Debug (Dabei wurden 5000 x 5000 Tile-Objekte zusammengebaut, bestehend jeweils aus mehreren Ints, Strings und einem Stringarray). Im Release-Modus war die ganze Aktion dann schon nach etwa 1 bis 5 Sekunden abgeschlossen. Ich habe den Code gerade nicht zur Hand, der ist auf der Platte von meinem alten PC, aber ich habe das Laden ähnlich wie Du gemacht.

Ich habe die BlitzMax-Syntax nicht mehr auf dem Schirm, aber was macht die Zeile stadt:+[wstadt]? Embarassed Very Happy
mfG, CO²

Sprachen: BlitzMax, C, C++, C#, Java
Hardware: Windows 7 Ultimate 64-Bit, AMX FX-6350 (6x3,9 GHz), 32 GB RAM, Nvidia GeForce GTX 750 Ti

DAK

BeitragSo, Jun 14, 2020 10:11
Antworten mit Zitat
Benutzer-Profile anzeigen
bmurray hat Folgendes geschrieben:

Meine größte Sorge ist auch, dass BMax auf künftigen Betriebssystemen nicht mehr läuft. Kann man da abschätzen, wie da die Chancen stehen werden? Ich arbeite gerade am Nachfolger meines Spiels, wenn es absehbar ist, dass das mit BMax irgendwann nicht mehr läuft, würde ich früher auf eine modernere Sprache umsteigen (wohl C++).


BMax schaut da besser aus als BB. Es verwendet zumindest DirectX 9 und OpenGL 2.1. Beides schon schwer veraltet, aber beides noch akzeptabel unterstützt (im Gegensatz zu dem DirectX 7 von BB).
OpenGL ist generell sehr gut im Supporten von alten Versionen (Open Source sei Dank), da kann man sich noch ein paar Jahre drauf verlassen.

Ein anderer großer Vorteil von BMax über BB ist, dass die Engine nicht fix mit der Sprache verdrahtet ist, und sich dank dem Modulsystem vergleichsweise leicht austauschen lässt. Das heißt, selbst wenn DX9/OGL2.1 nicht mehr unterstützt wird, sollte es machbar sein, BMax weiter zu verwenden.

Solange man BMax hauptsächlich auf Windows verwenden will, ist der Compiler auch noch eine Weile mit Windows kompatibel. Was ich mitbekommen hab, schaut es mit Linux nicht so rosig aus. Mac hab ich keine Ahnung.

BMax NG ist auch gut dabei, die Sprache kompatibel zu halten.

Was BMax eher sterben lässt, ist die mangelnde Community. Für einen Hobbyprogrammierer der hauptsächlich Kleinzeug macht, ist das noch ok. Aber wenn man mal was Größeres oder Komplizierteres vor sich hat, dann ist BMax einfach nicht die richtige Wahl.

Das fängt bei der Menge der Leute die Fragen beantworten können an, und hört bei den Modulen und Bibliotheken auf.

Wenn du eine Frage in BMax hast, kannst du sie im Grunde nur hier stellen und kriegst dann potentiell von 10 ehemaligen BMax-Programmierern eine Antwort alla "Hab ich schon seit 10 Jahren nicht mehr verwendet, aber eventuell könnte es grob so gehen".

Wenn du zu Python/Java/C++/... eine Frage hast, dann stellst du sie auf Stackoverflow, und 5 Minuten später ist deine Frage als Duplikat geschlossen, weil sie schon fünf Mal beantwortet wurde Wink

Was die Bibliotheken angeht: Wenn du was Komplexeres brauchst, dann kannst du mal in alten Forenbeiträgen nach noch nicht toten Links graben und vielleicht kriegst du etwas, was vor 5-10 Jahren geschrieben wurde und wo der Autor nicht mehr auffindbar ist.

Bei Python/Java/C++/... gibt es entweder eine Appstore-Artige Paketverwaltung in der du tausende aktuelle Module mit einem Befehl runterladen und installieren kannst, oder es gibt zumindest auf Github irrsinnig viele Pakete. Wenn man z.B. sowas simples wie HTTPS mit einer modernen TLS-Version auf BlitzMax haben will, kann man schon schnell daran stecken bleiben.

C++ würde ich btw. nur dann lernen, wenn du einen ganz spezifischen Grund dafür hast. Wenn du "einfach so" entwickeln willst, dann würd ich dir eher zu Java oder Python für General Purpose Programming raten, oder zu Unity für Spieleentwicklung.
Gewinner der 6. und der 68. BlitzCodeCompo

Trust

BeitragSo, Jun 14, 2020 11:09
Antworten mit Zitat
Benutzer-Profile anzeigen
Ich würde dir zumindest empfehlen auf Blitzmax NG umzusteigen. Da bekommst du einige Features dazu, die in anderen Sprachen schon lange Standard sind. Methodenüberladung, eine vernünftige Konstruktor Syntax, Interfaces etc. . Dazu ist C++ das Backend, dh. du kannst dir den Output anschauen bzw. leichter Änderungen vornehmen. Besonders beim Erstellen von dlls praktisch.
Gleich auf C++ umzusteigen würde ich vielleicht nicht empfehlen, zumindest nicht als kleiner Indi- /Hobbyentwickler. Würde da wenn eher noch sowas wie Haxe empfehlen, hat mehrere Backends (dadurch Plattform unabhängig), ist beliebt und die Sprache ist recht einfach (ähnelt C#). Oder, wenn es nicht auf eine Sprache beschränkt ist, vielleicht komplett ein Authorensystem verwenden wie Godot oder Unity.

Und ein kleiner Hinweis:
BlitzMax: [AUSKLAPPEN]
While Eof(datei) <> 1

Das geht zwar, ist aber vom Style her nicht schön.
IdR. liefert Eof einen Bool (in Bmax zwar einen Int da es Bool nicht gibt Rolling Eyes ) dh. boolsche Werte gegen eine "magic number" zu Überprüfen ist schlechter Stil... und geht auch nur in BMax da es Bool nicht gibt (alles was nicht 0 ist, ist True)
Besser wäre es entweder auf True/False zu prüfen, besser aber noch:
BlitzMax: [AUSKLAPPEN]
While Not Eof(datei)


Das macht hier vielleicht nicht so viel unterschied, allerdings erleichtert das, wenn man sich an so kleine Codingstyles hält, enorm die Lesbarkeit des Codes. Wenn ich den Code nicht kenne, würde ich mich sofort fragen: "Was für Werte kann Eof noch liefern außer 1? 2, 3 ...5...?" Mit "Not Eof" ist sofort klar: "Es handelt sich um einen Bool"...
Bei "Eof" zwar ohnehin klar dass es sich um einen Bool handelt, aber wenn es eigene Methoden sind ist es nicht mehr sofort ersichtlich.
Es gibt 10 Gruppen von Menschen: diejenigen, die das Binärsystem verstehen, und die anderen.
 

sinjin

BeitragDi, Jun 16, 2020 11:05
Antworten mit Zitat
Benutzer-Profile anzeigen
Vielleicht geht es etwas schneller wenn du statt lauter Ints, mal nen Short oder Byte verwendest. Du hast ja "nur" 200 Länder, warum dann 4 Bytes speichern/laden?
Für Ints habe ich mal nen Mini-Compressor geschrieben, allerdings bei sehr grossen Zahlen verbraucht es Maximal 5 Bytes auf der Platte.
Code: [AUSKLAPPEN]

function readpint%(f:tstream)
  local ret%,a%,c%
  repeat
    a=readbyte(f)
    ret:+(a&$7f) shl c
    c:+7
  until not(a&$80)
  return ret
endfunction

function writepint(f:tstream,src%)
  repeat
    writebyte f,(src&$7f) + ((src shr 7<>0) shl 7)
    src:shr 7
  until not src
endfunction

function skippint(f:tstream)
  repeat
  until not(readbyte(f)&$80)
endfunction

bmurray

BeitragDi, Jun 16, 2020 15:49
Antworten mit Zitat
Benutzer-Profile anzeigen
CO2 hat Folgendes geschrieben:
Hallöchen,

Ein kurzer Hinweis noch, einfach weil es bisher nicht explizit genannt wurde: Kommen die 13 Sekunden aus einem Debug, oder einem Release-Build?


Das war tatsächlich ein Release-Build, aber auf meinem älteren Laptop. Auf meinem Desktop-PC waren es nur noch 4 Sekunden, was ich trotzdem noch zu lang finde.

CO2 hat Folgendes geschrieben:

Ich habe die BlitzMax-Syntax nicht mehr auf dem Schirm, aber was macht die Zeile stadt:+[wstadt]? Embarassed Very Happy


Ups, das sollte eigentlich gar nicht rein. Ich habe einfach ein Array erstellt:
Code: [AUSKLAPPEN]
Global stadt:tstadt[]


in dem alle Städte noch einmal gespeichert werden (also neben der Liste). Über die ID, die jede Stadt hat, kann ich so ganz einfach die betreffende Stadt ansprechen. Etwa mit: "stadt[5].einwohner:+100" oder ähnliches. Die Zeile stadt:+[wstadt] fügt dem Array einfach die aktuell geladene Stadt hinzu.



DAK hat Folgendes geschrieben:
bmurray hat Folgendes geschrieben:

Meine größte Sorge ist auch, dass BMax auf künftigen Betriebssystemen nicht mehr läuft. Kann man da abschätzen, wie da die Chancen stehen werden? Ich arbeite gerade am Nachfolger meines Spiels, wenn es absehbar ist, dass das mit BMax irgendwann nicht mehr läuft, würde ich früher auf eine modernere Sprache umsteigen (wohl C++).


BMax schaut da besser aus als BB. Es verwendet zumindest DirectX 9 und OpenGL 2.1. Beides schon schwer veraltet, aber beides noch akzeptabel unterstützt (im Gegensatz zu dem DirectX 7 von BB).
OpenGL ist generell sehr gut im Supporten von alten Versionen (Open Source sei Dank), da kann man sich noch ein paar Jahre drauf verlassen.

Ein anderer großer Vorteil von BMax über BB ist, dass die Engine nicht fix mit der Sprache verdrahtet ist, und sich dank dem Modulsystem vergleichsweise leicht austauschen lässt. Das heißt, selbst wenn DX9/OGL2.1 nicht mehr unterstützt wird, sollte es machbar sein, BMax weiter zu verwenden.

Solange man BMax hauptsächlich auf Windows verwenden will, ist der Compiler auch noch eine Weile mit Windows kompatibel. Was ich mitbekommen hab, schaut es mit Linux nicht so rosig aus. Mac hab ich keine Ahnung.

BMax NG ist auch gut dabei, die Sprache kompatibel zu halten.

Was BMax eher sterben lässt, ist die mangelnde Community. Für einen Hobbyprogrammierer der hauptsächlich Kleinzeug macht, ist das noch ok. Aber wenn man mal was Größeres oder Komplizierteres vor sich hat, dann ist BMax einfach nicht die richtige Wahl.

Das fängt bei der Menge der Leute die Fragen beantworten können an, und hört bei den Modulen und Bibliotheken auf.

Wenn du eine Frage in BMax hast, kannst du sie im Grunde nur hier stellen und kriegst dann potentiell von 10 ehemaligen BMax-Programmierern eine Antwort alla "Hab ich schon seit 10 Jahren nicht mehr verwendet, aber eventuell könnte es grob so gehen".

Wenn du zu Python/Java/C++/... eine Frage hast, dann stellst du sie auf Stackoverflow, und 5 Minuten später ist deine Frage als Duplikat geschlossen, weil sie schon fünf Mal beantwortet wurde Wink

Was die Bibliotheken angeht: Wenn du was Komplexeres brauchst, dann kannst du mal in alten Forenbeiträgen nach noch nicht toten Links graben und vielleicht kriegst du etwas, was vor 5-10 Jahren geschrieben wurde und wo der Autor nicht mehr auffindbar ist.

Bei Python/Java/C++/... gibt es entweder eine Appstore-Artige Paketverwaltung in der du tausende aktuelle Module mit einem Befehl runterladen und installieren kannst, oder es gibt zumindest auf Github irrsinnig viele Pakete. Wenn man z.B. sowas simples wie HTTPS mit einer modernen TLS-Version auf BlitzMax haben will, kann man schon schnell daran stecken bleiben.

C++ würde ich btw. nur dann lernen, wenn du einen ganz spezifischen Grund dafür hast. Wenn du "einfach so" entwickeln willst, dann würd ich dir eher zu Java oder Python für General Purpose Programming raten, oder zu Unity für Spieleentwicklung.


Vielen Dank für deine Anmerkungen. Ja, ich bin ein Hobbyprogrammierer, ich habe weder Informatik studiert, noch irgendetwas in der Richtung gelernt. BlitzMax gefällt mir wegen OOP und der Einfachheit. Ein Beispiel: Der Befehl Graphics. Ich habe keine Ahnung, wie man etwa eine simple 2D-Anwendung mit C++ programmieren kann (so etwas wie ein simples "Graphics" scheint es nicht zu geben). Ich bin eher der learning-by-doing Typ und in der Konsole programmieren finde ich dermaßen langweilig, dass ich es nicht geschafft habe, bei C++ dran zu bleiben.

Wie definiert man eigentlich ein größeres Projekt? Mein Game hat jetzt etwas mehr als 100.000 Zeilen Code. Ich weiß, es hängt nicht nur an der Zeilenanzahl, aber trotzdem: BlitzMax scheint mir - im Moment - für meine Zwecke ausreichend, und da höre ich natürlich gerne, dass es wohl noch einige Zeit auf modernen Rechnern laufen wird.

Trust hat Folgendes geschrieben:
Ich würde dir zumindest empfehlen auf Blitzmax NG umzusteigen.

Vielen Dank für den Hinweis. Kennst du eine gute IDE für NG? Falls ich richtig liege, funktioniert Blide nicht mit der NG-Version, aber Blide gefällt mir eigentlich ganz gut.

Danke wegen EoF. Werd ich beherzigen.

sinjin hat Folgendes geschrieben:
Vielleicht geht es etwas schneller wenn du statt lauter Ints, mal nen Short oder Byte verwendest. Du hast ja "nur" 200 Länder, warum dann 4 Bytes speichern/laden?
Für Ints habe ich mal nen Mini-Compressor geschrieben, allerdings bei sehr grossen Zahlen verbraucht es Maximal 5 Bytes auf der Platte.


Ja, Shorts könnte ich natürlich machen und werde ich wohl auch. Die Floats könnte man auch in Ints umwandeln und dann einfach "neu deklarieren" (also 12345 in einem Zwischenschritt laden, aber der Variable dann 123,45 zuweisen, oder so). Als ich aber mit Shorts programmiert habe, waren da so viele Fehler drin (da sie ja nicht in den Minusbereich gehen). Das hat mich jedes Mal wahnsinnig gemacht, wenn ich einen BUg nicht gefunden habe, und dann am Ende gemerkt habe, dass es an den Shorts lag.

Interessant, dein Compressor, den schau ich mir mal genauer an, danke

DAK

BeitragDi, Jun 16, 2020 18:08
Antworten mit Zitat
Benutzer-Profile anzeigen
Zitat:
Vielen Dank für deine Anmerkungen. Ja, ich bin ein Hobbyprogrammierer, ich habe weder Informatik studiert, noch irgendetwas in der Richtung gelernt. BlitzMax gefällt mir wegen OOP und der Einfachheit. Ein Beispiel: Der Befehl Graphics. Ich habe keine Ahnung, wie man etwa eine simple 2D-Anwendung mit C++ programmieren kann (so etwas wie ein simples "Graphics" scheint es nicht zu geben). Ich bin eher der learning-by-doing Typ und in der Konsole programmieren finde ich dermaßen langweilig, dass ich es nicht geschafft habe, bei C++ dran zu bleiben.

Wie definiert man eigentlich ein größeres Projekt? Mein Game hat jetzt etwas mehr als 100.000 Zeilen Code. Ich weiß, es hängt nicht nur an der Zeilenanzahl, aber trotzdem: BlitzMax scheint mir - im Moment - für meine Zwecke ausreichend, und da höre ich natürlich gerne, dass es wohl noch einige Zeit auf modernen Rechnern laufen wird.


Ja, C++ ist nur für professionelle Entwicklung wirklich sinnvoll und dort auch nur wo man entweder tiefen Hardware-Access (z.B. für Treiber oder Engines schreiben) oder extreme Performance braucht.

BlitzMax ist deutlich besser für größere Projekte geeignet als BB. Eben auch wegen der Modularisierung, besserer Typisierung usw.
100k Zeilen sind definitiv schon ein größeres Projekt. Da du (nehme ich an) alleine dran bist, ist es aber noch überschaubarer.
Was dir andere Sprachen da helfen (z.B. Java oder Python) ist, dass es für vieles einfach fertige Bibliotheken bzw. mehr Features in der Standarbibliothek gibt.

Für dein aktuelles Problem könntest du z.B. in Java oder Python einfach den eingebauten Serializer verwenden, der genau das sehr effizient und schnell machen kann. Java hilft dir u.A. sehr mit Multithreading (etwas was hingegen Python kaum kann). Wenn du also mehr Zeug gleichzeitig machen willst, dann ist Java die passende Sprache.

Wenn es um bessere Datenstrukturen oder Datenbanken geht (bei der Menge Daten wäre das eventuell schon zu überlegen), dann bieten dir beide Sprachen sehr viel Hilfe.

Weder Java noch Python haben von Haus aus groß was an Grafikengines eingebaut, aber beide haben genügend einfaches und tolles zum Installieren. Hab z.B. vor vielen Jahren die JMonkey-Engine unter Java verwendet, damit kann man sehr einfach sehr schöne Sachen machen. Schatten? Dynamisches Wasser? Physik? Alles kein Thema und mit ein paar Zeilen gemacht. Und es gibt inzwischen schon bei weitem Besseres.
Unter Python hab ich die 2D-Engine PyGame verwendet. Außer dass man sie erst installieren muss, ist die nicht komplizierter als Blitz.

Dazu kommen mit Netbeans, IntelliJ, PyCharm usw. IDEs, die weit mehr können und weit komfortabler sind als alles was es für Blitz-Sprachen je gegeben hat.
Gewinner der 6. und der 68. BlitzCodeCompo

Trust

BeitragMi, Jun 17, 2020 13:13
Antworten mit Zitat
Benutzer-Profile anzeigen
Zitat:
Kennst du eine gute IDE für NG? Falls ich richtig liege, funktioniert Blide nicht mit der NG-Version, aber Blide gefällt mir eigentlich ganz gut.


Ja, Blide ist auch alt und wird nicht mehr weiterentwickelt.
Würde dir da VS Code mit BlitzMax Extension empfehlen.
Ich entwickle mittlerweile nur noch in VS Code (Python, C#, C, JS, Lua, GO, BlitzMax ...) und bin echt zufrieden.

Und zu dem Problem mit den Ladezeiten:
Der Flaschenhals ist hier die Stringkopiererei der Städtenamen und die TList, diese ist in BMax nicht die schnellste.

Versuche das ganze mal mit arrays. Bei den Strings ist das Problem, dass ja intern für jeden string nochmals ein array angelegt wird mit länge n wobei n=str.Length.


Alle Tests wurden im debugmodus mit einer einer Städteanzahl von 1000000 durchgeführt.

Test mit TList ganz normal:
Code: [AUSKLAPPEN]
Generating took: 1998ms
Saving took: 4875ms
Getting file took: 99ms
New loading total took: 5097ms


Test mit array:
Code: [AUSKLAPPEN]
Generating took: 1084ms
Saving took: 4276ms
Getting file took: 61ms
New loading total took: 4080ms


Test mit array ohne Städtenamen
Code: [AUSKLAPPEN]
Generating took: 214ms
Saving took: 2508ms
Getting file took: 22ms
New loading total took: 2442ms


Code ist nicht viel neues außer das TList mit einem array ausgetauscht wurde:
BlitzMax: [AUSKLAPPEN]
SuperStrict
Framework brl.blitz
Import brl.filesystem
Import brl.stream
Import brl.linkedlist
Import brl.random
Import brl.standardio
Import brl.ramstream


' Define TStadt Type for sample data
Type TStadt
Global list:TStadt[] = New TStadt[1000000]

Field name:String
Field id:Int
Field land:Int
Field gebiet:Int
End Type

Local time:Long = MilliSecs()

' Generate sample cities
For Local i:Int = 0 Until 1000000
Local stadt:TStadt = New TStadt

stadt.name = "Stadt " + Rand(0, 10000000)
stadt.id = i
stadt.land = i / 2
stadt.gebiet = i * 2
TStadt.list[i] = stadt
Next

Print "Generating took: " + (MilliSecs() - time) + "ms"

time = MilliSecs()
' Save sample data
Local file:TStream = WriteStream(".\testdata.dat")
For Local i:Int = 0 Until TStadt.list.Length
WriteStringBB(file, TStadt.list[i].name)
file.WriteInt(TStadt.list[i].id)
file.WriteInt(TStadt.list[i].land)
file.WriteInt(TStadt.list[i].gebiet)
Next
file.Close()

Print "Saving took: " + (MilliSecs() - time) + "ms"

NewLoadRam()

Function NewLoadRam()
Local time:Long = MilliSecs()

Local file:TStream = ReadStream(".\testdata.dat")
Local buf:Byte[] = New Byte[file.Size()]
Local ramStream:TRamStream = CreateRamStream(buf, file.Size(), 1, 1)
CopyStream(file, ramStream) ' This puts us at the end of the stream!
file.Close()

Print "Getting file took: " + (MilliSecs() - time) + "ms"

' So remember to use seek to get to the beginning again
ramStream.Seek(0)

Local i:Int = 0

While Not Eof(ramStream)
Local stadt:TStadt = New TStadt

stadt.name = ReadStringBB(ramStream)
stadt.id = ramStream.ReadInt()
stadt.land = ramStream.ReadInt()
stadt.gebiet = ramStream.ReadInt()

TStadt.list[i] = stadt
i :+ 1
Wend

ramStream.Close()

Print "New loading total took: " + (MilliSecs() - time) + "ms"
End Function


Function ReadStringBB:String(f:TStream)
Local n:Int = f.ReadInt()
Return f.ReadString(n)
End Function
Function WriteStringBB(f:TStream, str:String)
Local n:Int = str.Length
f.WriteInt(n)
f.WriteString(str)
End Function
Es gibt 10 Gruppen von Menschen: diejenigen, die das Binärsystem verstehen, und die anderen.

bmurray

BeitragSa, Jun 20, 2020 18:51
Antworten mit Zitat
Benutzer-Profile anzeigen
So, ich habe jetzt Python, Pygame und Visual Studio Code ausprobiert... Ich bin fassungslos! Es fühlt sich an wie eine Zeitreise vom Mittelalter in die Neuzeit (ok, vielleicht nicht ganz so krass), aber ich absolut fasziniert. Die IDE ist ja der Hammer, es ist alles viel "smoother" als mit Blide, Python gefällt mir und die Pygame-Bibliothek ist genau das richtige für mich. Ich kannte Python zwar, aber habe mich nie mit beschäftigt. Ein großer Fehler, wie ich jetzt sehe. Vielen Dank an alle, die mir das empfohlen haben!!! Dass man allerdings Variablen nicht deklarieren kann (oder muss?) irritiert mich etwas. Wie erstelle ich globals, die ich nicht sofort, aber eben später brauche? Naja, ich hab noch nicht alle Tutorials durch, das find ich schon raus.

Trust hat Folgendes geschrieben:
Der Flaschenhals ist hier die Stringkopiererei der Städtenamen und die TList, diese ist in BMax nicht die schnellste.


Danke für Deine Test. Stimmt, Strings brauchen natürlich länger, aber ich denke, auf die kann ich nicht verzichten. Dass Listen so langsam sind, war mir gar nicht bewusst. Schade, ich finde Listen sehr bequem.
 

bonajkukoj

BeitragSo, Jun 21, 2020 0:14
Antworten mit Zitat
Benutzer-Profile anzeigen
bmurray hat Folgendes geschrieben:
Wie erstelle ich globals, die ich nicht sofort, aber eben später brauche?


Du kannst, wenn du eine Variable zum Beispiel im normalen Modul-Scope deklariert hast und du sie dann später innerhalb einer Funktion brauchst, dort mit der Zeile
Code: [AUSKLAPPEN]
Global variablenName
die vorher definierte Variable quasi global machen, bzw eben auf diese außerhalb definierte Variable verweisen.
 

sinjin

BeitragMi, März 24, 2021 21:28
Antworten mit Zitat
Benutzer-Profile anzeigen
Da hätte ich mal Fragen.
1. Hast du es schneller bekommen? Wenn ja wie?
2. Oder bist du umgestiegen auf ne andere Programmiersprache?
3. Ist Blitzmax denn nicht gleich C? Ohne den C-Compiler läuft ja nix, bzw kann man die Module nicht kompilieren. Kann man die neueste Version von MinGW benutzen? (das ist doch der C-Compiler oder?) Ich meine, Blitz ist doch nur ein Oberbau von C und Asssembler.

DAK

BeitragDo, Apr 08, 2021 13:01
Antworten mit Zitat
Benutzer-Profile anzeigen
sinjin hat Folgendes geschrieben:
3. Ist Blitzmax denn nicht gleich C? Ohne den C-Compiler läuft ja nix, bzw kann man die Module nicht kompilieren. Kann man die neueste Version von MinGW benutzen? (das ist doch der C-Compiler oder?) Ich meine, Blitz ist doch nur ein Oberbau von C und Asssembler.


Das stimmt leider nur zum Teil.

Erstmal, BlitzMax kompiliert direkt in Assembler, im Gegensatz zu den klassischen Blitz-Sprachen (BB, B+, B3D) die den Umweg nach C gemacht haben.

Allerdings ist der Compiler nur ein Teil der Performance.

Eine Programmiersprache, die direkt kompiliert ist (im Vergleich zu einer interpretierten Programmiersprache) ist beim Ausführen von direkten Statements schneller.

Nehmen wir z.B. den folgenden (absichtlich extrem simplen) Code:

Code: [AUSKLAPPEN]
x = y + 1


Was der Assembler hierfür tun muss, ist y aus dem Speicher holen (Register, Cache oder RAM, je nach dem wo es aktuell liegt), dann die Rechnung y+1 ausführen und nach x schreiben.

Je nach dem wie es genau rennt, sind das eine Handvoll Assemblerzeilen (also Befehle die der Prozessor tatsächlich abholt).

Bei einer interpretierten Sprache hat man ein Programm, welches zur Laufzeit den Code interpretiert und übersetzt. Je nach Interpreter hat man hier einen Overhead von vielleicht 5-10x. Eventuell hat man einen Just-in-Time-Compiler, der den Overhead wieder gegen 0 drückt.

Das ist das einzige bisschen Performance, wo eine kompilierte Sprache einen Vorteil hat.

Vergleicht man eine schnelle kompilierte Sprache (z.B. C) mit einer schnellen interpretierten Sprache (z.B. Java), dann ist C (mit höchstem Optimierungsgrad) rund doppelt so schnell wie Java (https://medium.com/swlh/a-perf...3890545f6d).


Allerdings gehört zur Performance noch viel mehr als nur die Ausführungsgeschwindigkeit simpler Befehle. Es kommt hier meist auf die Algorithmik an.


Als simpler Vergleich: Ein Porsche ist schneller als ein Traktor, ohne Frage. Aber wenn ich den Porsche in Einzelteilen daliegen hab, und keine Ahnung wie ich den richtig zusammensetze, dann ist am Ende oft der fertig zusammengebaute Traktor schneller.

Das hier ist ein sehr gutes Beispiel für schlechte Algorithmik. Einer der größten Performance-Engpässe in dem aktuellen Problem ist die TList, die sehr langsam ist, da sie als Linked List implementiert ist. Ich erklär hier mal nicht den Unterschied zwischen verschiedenen Datenstrukturen, das sprengt hier den Raum. Aber es gibt andere Datenstrukturen, die viel effizienter sind. Z.B. würde eine ArrayList viel schneller sein.

Oder eventuell wäre ein Set oder eine Map oder eine noch spezialisiertere Datenstruktur schneller.

Ein Beispiel dazu:

In der Arbeit habe ich letztens eine Funktion machen müssen, die Code in einer Konfigurationsdatei optimiert. Dazu muss überprüft werden, ob ein Element aus der aktuellen Zeile in irgendeiner anderen Zeile vorkommt.

Die originale Implementierung hat jede Zeile mit jeder Zeile verglichen und für 10 000 Zeilen 100 Millionen Vergleiche gebraucht. Das hat mehrere Minuten gebraucht.

Die optimierte Lösung hat zuerst in einem Durchlauf alle Verwendungen von Variablen rausgesucht und die in ein Set gepackt (eine optimierte Datenstruktur in der jeder Wert nur ein Mal vorkommen kann, und bei der es besonders schnell geht, zu überprüfen ob ein Wert in einem Set drinnen ist).
Damit war klar, welche Variablen von anderen Zeilen verwendet wurden. Für 10 000 Zeilen waren somit nur mehr 10 000 Schritte nötig, was Millisekunden gebraucht hat.

Ein Wechsel von Java auf C hätte vielleicht die Ausführungsgeschwindigkeit verdoppelt. Eine Optimierung des Algorithmus hat bei 10k Zeilen die Ausführungsgeschwindigkeit um einen Faktor 10 0000 verbessert. Und bei mehr Zeilen wäre dieser Faktor passend angestiegen.



Das ist, wo der wirkliche Performance-Unterschied zwischen BMax und großen Sprachen wie C, Java, Python, ... her kommt. BMax gibt einem eine Linked List, eine Map, vielleicht noch ein Set und das wars. Größere Sprachen geben einem entweder durch die Standardbibliothek oder durch andere Bibliotheken extrem viel gut geschriebenen Code und gute, optimierte Datenstrukturen.



Hab z.B. mal auf die Schnelle dieses Sample in Python geschrieben:

Code: [AUSKLAPPEN]
import json
import time

def generate(count):
   st = time.time()
   staedte = [ {
      "name": "Das ist ein Testname",
      "id": x,
      "land": 200,
      "gebiet": 5000,
      "sprache": 100,
      "attraktivitaet": 100,
      "einwohner": 100,
      "einwohnerKat": 100,
      "breitengrad": 10.2042,
      "laengengrad": 10.2042,
      "hoehe": 100,
      "wetter": 100,
   } for x in range(count) ]
   with open("testdump.json", "w") as f:
      f.write(json.dumps(staedte))
   print("Took "+str(time.time()-st)+"s")

def load():
   st = time.time()
   with open("testdump.json", "r") as f:
      json.loads(f.read())
   print("Took "+str(time.time()-st)+"s")

generate(50000)
load()


Das generiert 50000 Städte, schreibt sie als JSON-Datei auf die Platte und lädt sie wieder. Zuerst mal, das Serialisieren und Deserialisieren sind jeweils zwei Zeilen Code. Fertig.
Zum Anderen: Trotzdem dass diese JSON-Datenstruktur alles Andere als optimiert ist, und deswegen 12 MB Speicherplatz braucht (statt den 3MB aus dem Originalpost), und trotzdem, dass ich mit Python eine saulangsame interpretierte Sprache verwende, braucht das Serialisieren und Deserialisieren inklusive Plattenzugriff jeweils gerade mal 0.2s.


Das ist der Unterschied, den die Standardbibliothek einer Programmiersprache macht. Wie schnell ein einzelner Befehl abgearbeitet wird, ist annähernd belanglos, besonders für einen Hobbyprogrammierer, der Systemfunktionen benutzt und sie nicht schreibt.
Gewinner der 6. und der 68. BlitzCodeCompo
 

sinjin

BeitragMo, Apr 12, 2021 13:32
Antworten mit Zitat
Benutzer-Profile anzeigen
Ich habe die Stelle in meinem Programm gefunden welche so langsam ist. Es lag zwar nicht direkt an Disk-Operationen aber war damit verknüpft. Es sind die Strings, man kann ja leider nicht MYSTRING[5]=ASC"a" schreiben, also hab ich eine Funktion in Asm geschrieben die genau das macht.
Kann man irgendwie Speicher für einen String anfordern? Bisher nutze ich meine Funktion Charmul, zumindest wächst der Speicher damit exponentiell. Und ist klar das wenn sich eine Stringlänge ändert, er entweder Speicher vom System anfordern oder freigeben muss, das dauert natürlich Zeit, auch wenns nur 2 Byte pro Zeichen sind.

Code: [AUSKLAPPEN]

import "qreplace.s"
extern
  function qreplace(str$,ch%,pos%) 'str[pos]=ch
endextern

function writeutf(f:tstream,src$) 'write header first $fffe
  for local a%=0 until src.length
    writeshort f,src[a]
  next
endfunction

function readutf$(f:tstream) 'read header first $fffe
  local sz%=(f.size()-f.pos()) shr 1
  local ret$ =charmul(" ",sz) 'allocate memory
  for local a%=0 until sz
'    ret:+chr(readshort(f)) 'slow
    qreplace ret,readshort(f),a
  next
  return ret
endfunction

function charmul$(ch$,mul%)
  if not mul then return ""
  local a%=1
  while (a+a<mul)
    ch:+ch
    a:+a
  wend
  return ch+ch[..mul-a]
endfunction

'qreplace.s
format MS COFF
section "code" code
;in esp+4 outstr
;in esp+8 inchar
;in esp+12 pos
public _qreplace
_qreplace:
  mov cx,[esp+8] ;cx=inchar

  mov eax,[esp+12] ;eax=pos*2
  add eax,eax

  add eax,[esp+4]
  mov [eax+12],cx ;outstr[eax]=inchar
ret

Thunder

BeitragDi, Apr 13, 2021 13:45
Antworten mit Zitat
Benutzer-Profile anzeigen
Schau Mal in blitz_string.c und in blitz_string.h. In der Header-Datei ist BBString definiert und enthält kein capacity-Attribut, daher kannst du davon ausgehen, dass es nicht möglich ist, für einen String im Vorhinein mehr Speicher anzufordern, als er tatsächlich momentan braucht.

Aber, wenn du viel String-manipulierst, ist es vielleicht besser wenn du dir eigene Funktionen schreibst und auf einem eigenen Typen mit Short-Array für die Daten, Länge und Kapazität arbeitest. Den könntest du mit bbStringFromShorts bei Bedarf zu einem BBString konvertieren (ich denke der Aufruf in BlitzMax funktioniert einfach über einen Cast: String(short_array)).

Nur sei dir bewusst, dass z.B. deine qreplace-Funktion nur mit Zeichen funktioniert, die auf genau 2 Byte in UTF-16 gemappt werden. Ich denke das ist der Grund, warum Zuweisungen in der Form nicht möglich sind.

Edit: noch was. Dieses qreplace ist natürlich unglaublich ineffizient wenn es mehrmals nacheinander aufgerufen wird, weil jedes mal push, push, push, call, <function body>, ret. Wenn du mehrere Zeichen hintereinander replacet, dann sollte qreplace ein short array nehmen. Und wenn es mehrere zeichen sind, aber nicht hintereinander, würde ich es auch versuchen mit short array + index array, damit alle Ersetzungen in einem Call zu qreplace erledigt werden können.

Einige der größten Optimierungsgewinne sind herausoptimierte Funktionsaufrufe in Schleifen. Aber soweit ich weiß kann das der BlitzMax-Compiler gar nicht und selbst wenn, könnte er es nicht, weil die Assembler-Funktion extern ist.
Meine Sachen: https://bitbucket.org/chtisgit https://github.com/chtisgit
 

sinjin

BeitragSa, Apr 17, 2021 20:31
Antworten mit Zitat
Benutzer-Profile anzeigen
Ok, kein Capacity-Field, irgendwo steht bestimmt wieviel Speicher ein String einnimmt, die Länge ist ja auch irgendwo. Dasselbe wie mit Tsound, irgendwo sind ja die Sampledaten, mit Asm geht ALLES was so ein Computer halt kann.
Und ob ich jetzt evtl Megabytes als Short einlese und dann nen String draus mache, oder ob ich halt 3 Pushes habe...wirklich? Es geht ja schnell genug so, und der Code ist einfach. Wenn man so will, und so habe ich früher unter DOS Asm gecodet, ich habe versucht so viele der Push Befehle zu minimieren, das ging recht einfach, wenn eine Funktion halt 3 Werte braucht, die auf den Stack pusht und später eh einen davon in z.B. DX wieder reinlädt (vom Stack natürlich), dann habe ich bevor ich die Funktion aufrufe den Wert gleich in DX geschrieben und dann den Call gemacht. Das würde auch ein Compiler können aber die kennen halt nur die Pushes, dann müsste man so einen Compiler fast von Grund auf neu Programmieren. Damals hatte ich noch viel mehr Ideen, somit könnte man, solange man die Module hat, die EXE direkt als Sourcecode benutzen, halt Assemblieren und Disassemblieren. Dann vergleicht er den Code mit meiner Bibliothek und kann dann wieder alle Variablen als Text darstellen, ohne grosse Compilerinformationen in der Exe. Aber ist bestimmt super schwer so was zu programmieren. Ich wollte mal meinen eigenen Compiler in Asm schreiben, das gibt man ganz schnell auf Smile Grafik hin oder her, überhaupt einen Compiler zu schreiben ist noch eine grössere Herausforderung wie ich finde!

Hier ein Beispiel (wie gesagt, alter Code):

;in bx - interruptnr
;out ax:dx - proc
; es - 0
<- diese 3 Zeilen sehe ich als Compiler Information an, also demnach wüsste der Compiler, bzw er merkt sich halt alles was mit den Prozessorvariablen passiert
proc sys16_getintvec
mov es,[word ptr nil] ;es=0
shl bx,2

mov dx,[es:bx]
mov ax,[es:bx+2]
retcode
endp


Also verstehste? Ich will einen Hoch-Assembler haben. Dann kann ich genauso mem[0]=sys16_getintvec(5) schreiben und dann normal weiter arbeiten. Nur das ich den Vorteil hätte mal mittendrin mov ax,dx zu schreiben. Hört sich kompliziert an, ist es auch!

Thunder

BeitragSo, Apr 18, 2021 20:01
Antworten mit Zitat
Benutzer-Profile anzeigen
Bzgl Hoch-Assembler: ich glaube der Microsoft Macro Assembler war in der Art gedacht. Auch beim flat assembler kann man Macros definieren um Code zu generieren, dabei ist das User Manual hilfreich.
Ursprünglich musste sicher Mal einer einen Compiler in Assembler schreiben (Bootstrapping), aber wie du sagst ist das schwer. Der flat assembler ist (zwar kein Compiler, aber) in Assembler geschrieben und wird üblicherweise als eines der ersten Programme auf selbst-programmierte Betriebssysteme für x86 portiert, weil es so einfach ist.

Was deine Annotationen angeht im Assemblercode für die Funktionsaufrufe, glaube ich dass das Gang und Gäbe war damals. Heute auf amd64 ist es besser, weil die ersten 4 Parameter in Registern übergeben werden statt auf dem Stack. Dennoch kann ein Compiler da sogar mehr machen, weil er den ganzen Funktionsaufruf inlinen kann. Dann ersparst du dir sogar das call und ret. C++ Compiler können das mittlerweile sehr gut.

So gut, dass ich z.B. ein ganzes Programm schreibe mit einer Klasse, mehreren Methoden, mehreren Funktionen, eine main-Funktion, und wenn ich richtig optimiere (alle Funktionen static), dann optimiert der Compiler alles weg bis auf die main-Funktion.
Beispiel: https://godbolt.org/z/h5sE7e86f
Meine Sachen: https://bitbucket.org/chtisgit https://github.com/chtisgit

Gehe zu Seite 1, 2  Weiter

Neue Antwort erstellen


Übersicht BlitzMax, BlitzMax NG Allgemein

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group