Datei Bitweise einlesen - sehr langsam

Übersicht BlitzBasic Allgemein

Neue Antwort erstellen

Trust

Betreff: Datei Bitweise einlesen - sehr langsam

BeitragSa, Apr 21, 2012 20:04
Antworten mit Zitat
Benutzer-Profile anzeigen
Hallo,

ich habe folgende Funktion zu Testzwecken geschrieben die mir eine Datei Bitweise einlesen soll, nur leider ist diese extrem langsam.
Dies liegt sicher an den BB Funktionen "Bin$" und "Readbyte"...
Gibt es da bessere bzw. schnellere alternativen?

Die Funktion:
Code: [AUSKLAPPEN]
Function ReadFileToBits$(inData$)
   Local file = ReadFile(inData$)
   Local bitString$
   While Not Eof(file)
      bitString$ = bitString$ + Right$(Bin$(ReadByte(file)), 8)
   Wend
   CloseFile file
   Return bitString$
End Function
Es gibt 10 Gruppen von Menschen: diejenigen, die das Binärsystem verstehen, und die anderen.

Eingeproggt

BeitragSa, Apr 21, 2012 20:11
Antworten mit Zitat
Benutzer-Profile anzeigen
Eine geringe Verbesserung könnte man dadurch erzielen, indem man ReadInt statt ReadByte verwendet, denn dann kann man sich Right sparen. Wie das aussieht wenn die Dateigröße nicht genau durch 4 teilbar ist weiß ich grad nicht, aber damit lässt sich wunderbar überleiten zu weiterer Optimierung: Wo man nämlich wissen müsste, was du genau erreichen möchtest? Was für Dateien sind es? Wozu brauchst du die Binärdarstellung? etc?

mfG, Christoph.
Gewinner des BCC 18, 33 und 65 sowie MiniBCC 9

BladeRunner

Moderator

BeitragSa, Apr 21, 2012 20:13
Antworten mit Zitat
Benutzer-Profile anzeigen
Verwende zum einen Readbytes statt ReadByte, denn der Byteweise Zugriff bremst dein System enorm.
Zum anderen: ist es wirklich notwendig die gesamte Datei komplett als Einzelbitstream zu haben. Die Strings fressen ja enormst Platz. Das scheint mir sehr ineffizient.
Zu Diensten, Bürger.
Intel T2300, 2.5GB DDR 533, Mobility Radeon X1600 Win XP Home SP3
Intel T8400, 4GB DDR3, Nvidia GF9700M GTS Win 7/64
B3D BMax MaxGUI

Stolzer Gewinner des BAC#48, #52 & #92

Eingeproggt

BeitragSa, Apr 21, 2012 20:26
Antworten mit Zitat
Benutzer-Profile anzeigen
Habs mir genauer angesehen, das Problem ist überhaupt das Hantieren mit Strings bei größeren Dateien. Wenn bei jedem Byte einer sagen wir 50kb großen Datei der String im Speicher herum kopiert werden muss, sind das am Ende 50.000 Zeichen die sinnlos herum geschoben werden.

Mein Ansatz:

BlitzBasic: [AUSKLAPPEN]
Dim bits(0)
Global bitsSize

Function ReadFileToBits1$(inData$)
Local file = ReadFile(inData$)
Local bitString$
While Not Eof(file)
bitString$ = bitString$ + Right$(Bin$(ReadByte(file)), 8)
Wend
CloseFile file
Return bitString$
End Function

Function ReadFileToBits2(inData$)
Local file = ReadFile(inData$)
If file=0 Then
bitsSize=0
Return
EndIf
bitsSize=FileSize(inData$)*8
Dim bits(bitsSize)
index=0
While Not Eof(file)
byte=ReadByte(file)
;Etwas unschön, in Schleife wäre schöner, aber egal erstmal...
bits(index)=(byte And 128)>0
bits(index+1)=(byte And 64)>0
bits(index+2)=(byte And 32)>0
bits(index+3)=(byte And 16)>0
bits(index+4)=(byte And 8)>0
bits(index+5)=(byte And 4)>0
bits(index+6)=(byte And 2)>0
bits(index+7)=(byte And 1)>0

index=index+8
Wend
CloseFile file
End Function

time=MilliSecs()
debug$=ReadFileToBits1("test.bb")

DebugLog "Zeit 1: "+(MilliSecs()-time)
DebugLog debug$

time=MilliSecs()
ReadFileToBits2("test.bb")

DebugLog "Zeit 2: "+(MilliSecs()-time)
For i=0 To bitsSize-1
DebugLog bits(i)
Next

WaitKey()
End


Achtung, Erklärungen:
1.) Der Ansatz ist wesentlich schneller. Bei einer 51kb großen Testdatei hat deine Funktion ~2sek gebraucht - meine 30ms.
2.) Dafür ist es unschön, dass das Ergebnis in einer globalen Variable / einem Array steckt... geht mit Blitz fürchte ich nicht besser. Dafür kann man bequem darauf zugreifen.
3.) Das Ausgeben der Bits in meinem Ansatz ist extrem lahm und schon bei 1kb Dateien kaum erträglich... Das liegt aber nicht an mir sondern am Debuglog - ich finde das Speichern im Array hat nur Vorteile. Der nach meienm Verständnis 4mal so hoch geschätzte (Im Array sind Ints, im String sind Bytes) RAM-Verbrauch sollte das Wet sein.

mfG, Christoph.
Gewinner des BCC 18, 33 und 65 sowie MiniBCC 9

Trust

BeitragSa, Apr 21, 2012 20:32
Antworten mit Zitat
Benutzer-Profile anzeigen
Die Binärdarstellung wird benötigt, um die Datei später mit einer Funktion zu komprimieren, welche ich noch nicht geschrieben habe.

Ich möchte Mediadateien - hauptsächlich Bilder, schnell komprimieren, bzw. eine speicherarme Textdarstellung aus diesen Dateien erstellen.
Um diese Textdarstellung dann einfach per copy&paste ins Programm kopieren zu können, welche mir dann wieder diese Datei daraus erstellt.

@BladeRunner
Muss ich für Readbytes aber dann nicht vorher schon die genaue Anzahl an Bytes wissen die ich einlesen will?
Es gibt 10 Gruppen von Menschen: diejenigen, die das Binärsystem verstehen, und die anderen.
  • Zuletzt bearbeitet von Trust am So, Apr 22, 2012 0:12, insgesamt 2-mal bearbeitet

BladeRunner

Moderator

BeitragSa, Apr 21, 2012 20:39
Antworten mit Zitat
Benutzer-Profile anzeigen
Du kannst ja Blockweise einlesen, sagen wir Kilobyteweise. Wenn nicht genug eingelesen werden kann gibt dir Readbytes dass ja zurück.
Zu Diensten, Bürger.
Intel T2300, 2.5GB DDR 533, Mobility Radeon X1600 Win XP Home SP3
Intel T8400, 4GB DDR3, Nvidia GF9700M GTS Win 7/64
B3D BMax MaxGUI

Stolzer Gewinner des BAC#48, #52 & #92

Trust

BeitragSo, Apr 22, 2012 14:15
Antworten mit Zitat
Benutzer-Profile anzeigen
Ok habe nun alle Vorschläge ausführlich getestet und bin zu dem Entschluss gekommen, dass es sinnvoller ist, die Datei Byteweise in eine Bank einzulesen und ggf. im nachhinein die Bankeinträge in 8bit streams umzuwandeln.
Der Geschwindigkeitsvorteil im Gegensatz zum Bitweise einlesen ist enorm. Das Bitweise einlesen braucht bei einer ca 1.8 MB grossen Datei ca. 2319 ms, und diese dann wieder Bitweise zu schreiben, dauert ca. 11098 ms!
Im gegenzug dauert das Byteweise einlesen der 1.8 mb grossen Datei 1 ms, und schreiben 34 ms.

Für die wo es interessiert hier die Funktionen:


Bitweise einlesen und schreiben:
BlitzBasic: [AUSKLAPPEN]
Dim BitFunctions_Bit(0)  ; Muss im Hauptprogramm angelegt werden

Function ReadFileToBits(inData$)
Local file = ReadFile(inData$)
fSize = FileSize(inData$)
Dim BitFunctions_Bit((fSize*8)+1)
BitFunctions_Bit(0) = fSize ; im ersten Eintrag wird die Arraygrösse gespeichert
index = 1
While Not Eof(file)
byte=ReadByte(file)
BitFunctions_Bit(index)=(byte And 128)>0
BitFunctions_Bit(index+1)=(byte And 64)>0
BitFunctions_Bit(index+2)=(byte And 32)>0
BitFunctions_Bit(index+3)=(byte And 16)>0
BitFunctions_Bit(index+4)=(byte And 8)>0
BitFunctions_Bit(index+5)=(byte And 4)>0
BitFunctions_Bit(index+6)=(byte And 2)>0
BitFunctions_Bit(index+7)=(byte And 1)>0

index=index+8
Wend
CloseFile file
End Function

Function WriteFileToBits(path$)
Local file = WriteFile(path$)
i = 1
For j = 1 To BitFunctions_Bit(0)
bitStr$ = Str(BitFunctions_Bit(i)) + Str(BitFunctions_Bit(i+1)) + Str(BitFunctions_Bit(i+2)) + Str(BitFunctions_Bit(i+3)) + Str(BitFunctions_Bit(i+4)) + Str(BitFunctions_Bit(i+5)) + Str(BitFunctions_Bit(i+6)) + Str(BitFunctions_Bit(i+7))
i = i + 8
If Len(bitStr$) = 8
WriteByte file, Bit8ToByte(bitStr$)
bitStr$ =""
EndIf
Next
CloseFile file
End Function


Function Bit8ToByte(bits8$)
Local myByte
Local bit
For i = 1 To 8
bit = Mid$(bits8$, 8-(i-1), 1)
myByte = myByte + bit*((2 ^ i) / 2)
Next
Return myByte
End Function



Hier die Functionen zum Byteweise einlesen und schreiben:
BlitzBasic: [AUSKLAPPEN]
Global BitFunctions_Bank  ; Im Hauptprogramm anlegen




;Datei Byteweise einlesen, konnte die Datei nicht gefunden werden wird eine 0 zurückgegeben ansonsten 1
Function ReadFileToBytes(inData$, path$ = "")
If FileExist(inData$) = True
Local file = ReadFile(path$+inData$)
Local fSize = FileSize(path$+inData$)

;Eine Bank erstellen die so groß ist wie die Datei,
; + 4 Bytes(Integer), um dort die Info über Dateigröße ablegen zu können
; + 4 Bytes(1 Byte pro Zeichen), um die Dateiendung der Datei zu speichern
; Dateiendungen sind idR. nicht grösser als 4 zeichen (4 Zeichen * 1Byte )
BitFunctions_Bank = CreateBank(fSize+4+4)

;Die Dateigrösse an den Anfang der Bank schreiben
PokeInt BitFunctions_Bank, 0, fSize

;Danach die Dateiendung in die Bank schreiben
; Da es Poke/Peek-String loglischerweise nicht gibt, wird die Zeichenkette in 4 Bytewerte aufgeteilt 4 * 1 Bytes = 4byte
; und mit PokeByte in die Bankpositionen 4 - 7 geschreiben

;Dateiendung ermitteln
Local fExt$ = GetFileExtention$(inData$)

;Den String fExt$ in 4 Byteteile aufteilen
Local fExtLength = Len(fExt$)
Local fExtByte1
Local fExtByte2
Local fExtByte3
Local fExtByte4
If fExtLength >= 1 fExtByte1 = Asc(Mid$(fExt$,1,1)) : Else fExtByte1 = Asc(" ")
If fExtLength >= 2 fExtByte2 = Asc(Mid$(fExt$,2,1)) : Else fExtByte2 = Asc(" ")
If fExtLength >= 3 fExtByte3 = Asc(Mid$(fExt$,3,1)) : Else fExtByte3 = Asc(" ")
If fExtLength = 4 fExtByte4 = Asc(Mid$(fExt$,4,1)) : Else fExtByte4 = Asc(" ")

;Und in die Bank schreiben
PokeByte BitFunctions_Bank, 4, fExtByte1
PokeByte BitFunctions_Bank, 5, fExtByte2
PokeByte BitFunctions_Bank, 6, fExtByte3
PokeByte BitFunctions_Bank, 7, fExtByte4

; Die Bank mit der Datei füllen, aber erst ab dem 8. Byte mit dem füllen beginnen
Local amountOfBytes = ReadBytes(BitFunctions_Bank, file, 8, fSize)

Return 1
Else
Return 0
EndIf
End Function



;Datei Byteweise Schreiben fName$ ohne Dateiendung
Function WriteFileToBytes(fName$, path$ = "")
; Die Dateigrösse auslesen
Local fSize = PeekInt(BitFunctions_Bank, 0)
; Die Dateiendung auslesen
Local fExtByte1 = PeekByte(BitFunctions_Bank, 4)
Local fExtByte2 = PeekByte(BitFunctions_Bank, 5)
Local fExtByte3 = PeekByte(BitFunctions_Bank, 6)
Local fExtByte5 = PeekByte(BitFunctions_Bank, 7)
; Überprüfen ob das Zeichen <> " " ist
Local fExt$ = ""
If Chr$(fExtByte1) <> " " fExt$ = fExt$ + Chr$(fExtByte1)
If Chr$(fExtByte2) <> " " fExt$ = fExt$ + Chr$(fExtByte2)
If Chr$(fExtByte3) <> " " fExt$ = fExt$ + Chr$(fExtByte3)
If Chr$(fExtByte4) <> " " fExt$ = fExt$ + Chr$(fExtByte4)


Local file = WriteFile(path$+fName$+"."+fExt$)
WriteBytes(BitFunctions_Bank, file, 8, fSize)
CloseFile file
End Function


; Überprüfung ob Datei existiert
Function FileExist(file$)
Local rFile = ReadFile(file$)
If rFile = 0
Return 0
Else
CloseFile rFile
Return 1
EndIf
End Function


;Dateiendung ermitteln
Function GetFileExtention$(fileName$)
Local fExt$ = Right$(fileName$, 4)
For i = 1 To 3
If Instr(fExt$, ".") <> 0 Then fExt$ = Right$(fileName$, 4-i)
Next
Return fExt$
End Function


; Wandelt einen 8Bit stream in ein Byte um
Function Bit8ToByte(bits8$)
Local myByte
Local bit
For i = 1 To 8
bit = Mid$(bits8$, 8-(i-1), 1)
myByte = myByte + bit*((2 ^ i) / 2)
Next
Return myByte
End Function


; Mit dieser funktion könnte man einen Byte-Bankeintrag in einen 8Bit stream umwandeln
Function ByteToBit(byte)
Return Int(Right$(Bin$(Str(byte)), 8))
End Function



Die Bank könnte man auch Lokal anlegen und als Parameter an die Funktion WriteFileToBytes() übergeben.



Und wer Strings in Bits umwandeln möchte und umgekehrt:
BlitzBasic: [AUSKLAPPEN]
Function StringToBits$(inString$)
Local bitString$
For i = 1 To Len(inString$)
bitString$ = bitString$ + Right$(Bin$(Asc(Mid$(inString$, i, 1))), 8)
Next
Return bitString$
End Function


Function BitsToString$(bitString$)
Local txtString$
For i = 1 To Len(bitString$) Step 8
bits$ = Mid$(bitString$, i, 8)
txtString$ = txtString$ + Chr$(Bit8ToByte(bits$))
Next
Return txtString$
End Function

Function Bit8ToByte(bits8$)
Local myByte
Local bit
For i = 1 To 8
bit = Mid$(bits8$, 8-(i-1), 1)
myByte = myByte + bit*((2 ^ i) / 2)
Next
Return myByte
End Function





Danke an alle für die Hilfe!
Es gibt 10 Gruppen von Menschen: diejenigen, die das Binärsystem verstehen, und die anderen.

Neue Antwort erstellen


Übersicht BlitzBasic Allgemein

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group