Hallo!
Auf einem Embedded-System mit einem DOS-kompatiblen Betriebssystem muss
ich eine 2MB große Hex-Datei einlesen und diese binär im Speicher
ablegen.
Dieser Vorgang braucht gerade ca. 40 Sekunden wobei der Großteil in
meiner Hex2bin Funktion verbracht wird (lasse ich die Funktion weg, dann
dauert das reine Einlesen der Hex-Datei 10 Sekunden).
Ich arbeite dort bereits mit zwei Lookup-Tablellen da der klassische Weg
über x - '0' und zwei if-Abfragen viel länger dauert.
Meine Frage ist nun ob jemand eine Idee oder einen x86 Assembler-Code
hat um einen Hex-String (Länge bekannt) in die binäre Form zu
übertragen.
Das Einlesen der Daten erfolgt bereits in einem Puffer der der
Blockgröße des Speichermediums entspricht.
Grüße,
Bernhard
Welchen Compiler verwendest Du? Bei modernen C-Compilern bringt es meist
nichts, wenn man die Funktion selbst in Assembler programmiert. Wenn es
ein uralter C-Compiler ist, dann könnte es sehr viel bringen, diese eine
Funktion selbst in Assembler zu programmieren.
Kleiner Nachtrag:
Der einfachste Weg bezüglich Assembler wäre, diese Funktion vom GCC mit
-O3 compilieren und sich das Ergebnis als Assembler Listing ausgeben zu
lassen. Vorrausgesetzt Du verwendest GCC nicht schon als C-Compiler.
Bernhard L. R. schrieb:> muss ich eine 2MB große Hex-Datei einlesen
Irgendwie kann ich mir nicht vorstellen, daß das Hex2Bin langsamer sein
soll als das IO zum Einlesen der Daten. Eigentlich sollte das Einlesen
mindestens 10-100 mal langsamer sein.
Ich behaupte mal daß dass das Problem irgendwo anders steckt. Und wenn
es deine lookup Funktion ist.
Ich schließe mich Udo's Vermutung an.
Kann es sein dass der Zielspeicher sehr langsam ist? Durch unglückliches
Programmieren gepaart mit einem unglücklichen Compiler kriegst du viele
Zugriffe darauf zusammen. Auch in einem Beispiel mit der lookup-Tabelle:
High-Byte schreiben, Wert wieder lesen, verodern, nochmal schreiben.
Wie schnell ist es, wenn du testweise das hex2bin weglässt und einfach
nur den Speicher mit Nullen füllst?
Gib mal ein paar Details zu Platform, prozessor, taktfrequenz und
COmpiler bekannt.
Moin,
sind da irgendwelche anderen Tasks aktiv?
Und was genau macht die Routine? Was passiert während des Ablaufes
tatsächlich?
Welchje Interrupts werden ausgelöst?
Von wo wird eingelesen: HDD, seriell oder was?
Gruss
Robert
Ich schliesse mich an.
Das durchackern von 2MB Charactern und umkodieren kann keine 30 Sekunden
dauern. Nicht, wenn das reine Einlesen nur 10 Sekunden braucht.
Da ist irgendwas anderes im Spiel.
> lasse ich die Funktion weg, dann dauert das reine Einlesen der Hex-Datei 10
Sekunden
Wie, genau, hast du das getestet?
Hast du den Aufruf der Funktion entfernt oder hast du den Inhalt der
Funktion auskommentiert? Gibt es eine Chance, dass der Compiler daraus
einen Nutzen gezogen hat, dass du die Funktion still gelegt hast und dir
den ganzen weiteren Rest weiter wegoptimiert hat, als dir lieb ist?
Grund der Frage: Deine Zahlen sind nicht plausibel. In einem Programm
ist der eigentliche I/O bei weitem das Langsamste. So langsam kann
eine Hex-Bin Wandlung gar nicht sein, dass File-Lesen schneller abläuft.
sparen.
Jeder halbwegs vernünftige Compiler hat eine Datenflussanalyse und
entscheidet selbst, welche Variablen er in Register legt und welche
nicht. Derartige Compiler ignorieren eine 'register' Angabe ganz
einfach. 'register' stammt aus der Steinzeit des Compilerbaus, kurz
nachdem wir von den Bäumen runtergekommen sind.
Und wenn dein Compiler das nicht kann, dann wird es Zeit mal über einen
etwas neuereren Compiler nachzudenken.
Amateur schrieb:> Zu DOS-Zeiten war Speicher teuer. Möglicherweise sind die Lese-> und> Schreibroutinen daran schuld indem sie nur Häppchenweise lesen.
Und ich dachte, Bernhard hat gesagt, daß das Lesen wesentlich schneller
geht als seine hex2bin()-Routine. Wenn du jetzt vermutest, daß die Lese-
und Schreibroutinen schuld wären, passt das doch nicht zusammen, oder?
ich schrieb:> Wenn du jetzt vermutest, daß die Lese-> und Schreibroutinen schuld wären, passt das doch nicht zusammen, oder?
Da sich Bernhard nicht mehr gemeldet hat und nicht klar ist wie er das
gemessen und ggf Mist gemessen hat kann der Fehler fast überall stecken.
Evt. auch in seiner Methode die Zeit für den IO zu ermitteln.
Wenn Du heute 2MByte Speicher benötigst, so kannst Du sie, praktisch
ohne die Zeiger zu überprüfen, reservieren.
Zu DOS-Zeiten hätte die Reservierung von 2MByte praktisch immer einen
Fehler ergeben.
Lesen und Schreiben.
Ich kenne die heutzutage verwendeten Algorithmen nicht. Stell man sich
aber vor:
Kleines Häppchen lesen.
Ein bisschen grübeln.
Kleines Häppchen schreiben.
So fällt hier beim Lesen die ganze Kopfpositionierung an und Später, da
der Kopf falsch steht, beim Schreiben das gleiche. Und das pro Häppchen.
R. A. schrieb:> "length" als Schleifenzähler lässt sich noch einsparen:
Ist aber je nach Datentyp des Pointers und der Zählvariable, der
Registerbreite etc. evt. sogar langsamer.
Wobei ich auch diese Optimierung dem Compiler überlassen würde.
Den tatsächlichen Unterschied dieser Optimierung wird man nicht merken
und nur schwer messen können.
Konrad S. schrieb:> Bernhard L. R. schrieb:>> dauert das reine Einlesen der Hex-Datei 10 Sekunden>> Für 2MB dauert auch das schon verdächtig lang.
Du weisst ja nicht woher er das liest (Commodore VC1541 inkl. 2x Disc
wechseln :-)
ich schrieb:> Davis schrieb:>> Was heißt "... 2MB große Hex-Datei einlesen ..."?>> Lochkartenstapel? Kassettenrecorder?>> Geht's noch ein bisschen irrationaler?
Auch von Diskette sind 10 Sekunden schon sportlich. Da muss man schon
recht schnell beim Diskettenwechsel sein.
Ich vermute uralt Compiler, der kaum optimiert. Optimiere mal per Hand
einen der beiden *bptr Zugriffe weg, mit einer Hilfsvariablen. bptr
müsste ein far Pointer sein, Zugriffe damit waren unter DOS teuer.
Ach ja: Diese Optimierung macht bei halbwegs aktuellen Compilern
keinerlei Sinn ;-)
Bernhard L. R. schrieb:> static void hex2bin(char *hex, unsigned char *bin, int length)> {> register char *hptr = hex;> register unsigned char *bptr = bin;
register char b;
>> if (length)> do {> b = h2b_look_high[*hptr++];> b |= h2b_look_low[*hptr++];
*bptr = b;
bptr++;
> } while (--length);> }
Ich möchte auf eines hinweisen.
Der TO sprach im Eröffnungsposting von "DOS-kompatibel", nicht von DOS
an sich. D.h. für mich: wir sollten nicht von DOS weg spekulieren.
DOS-kompatibel war nicht so selten und beschränkte sich meistens darauf,
dass vom BDOS eine funktional gleichwertige Schnittstelle bereitgestellt
wurde.
klausr schrieb:> Ich vermute uralt Compiler, der kaum optimiert. Optimiere mal per Hand> einen der beiden *bptr Zugriffe weg, mit einer Hilfsvariablen. bptr> müsste ein far Pointer sein, Zugriffe damit waren unter DOS teuer.
Selbst dann.
Die Diskrepanz zum um längen aufwändigeren I/O bleibt. Die von ihm
genannten Zahlen machen keinen Sinn. Wenn die stimmen würden, dann würde
es sich anbieten, den Hauptspeicher minimal auszuführen und statt dessen
spanabhebende Datenverarbeitung zu betreiben, indem man die Maschine bis
zum Exzess pagen lässt. Das widerspricht aber allen Grundsätzen, die man
in den letzten 60 Jahren entdeckt hat.
Eine so kurze Schleife sollte, selbst bei nicht existenter Optimierung,
im Cache des Prozessors laufen. Dadurch sollten sich auch praktisch alle
Optimierungsversuche erledigen.
Amateur schrieb:> Eine so kurze Schleife sollte, selbst bei nicht existenter Optimierung,> im Cache des Prozessors laufen. Dadurch sollten sich auch praktisch alle> Optimierungsversuche erledigen.
Wer sagt denn, dass der verwendete Prozessor einen Cache hat? Ich
vermute schlimmstes... 8088?!?
Bernhard L. R. schrieb:> Auf einem Embedded-System mit einem DOS-kompatiblen Betriebssystem
Hm... CP/M war (in gewissen Grenzen) auch DOS kompatibel...
Hallo und guten Abend!
Bin etwas überrascht über die Anzahl der Antworten. War tagsüber nicht
online :)
Hier ein paar Facts um die meisten Unklarheiten hoffentlich zu
beseitigen:
Betriebssystem: Realtime DOS
Compiler: Paradigm (Borland C++ 5.02 Nachfolger/Abart)
CPU: 80186, 24 bit Adressraum, 8MB Arbeitsspeicher
Clock: 100MHz
Speichermedium: SD-Karte (über Hardware SPI angebunden)
Wenn ich in der Schleife, die die Datei einliest, die Hex2bin Funktion
auskommentiere, läuft das Programm wirklich in 10 Sekunden durch.
Ich habe noch nicht getestet was passiert wenn man nur in den
Zielspeicher Nullen schreibt.
Danke auch für den Tipp mit den teuren Far-Zugriffen. Ich werde in dem
Zusammenhang den Weg über eine lokale Variable testen.
Ergebnisse morgen :)
Danke und viele Grüße,
Bernhard
Bernhard L. R. schrieb:> Wenn ich in der Schleife, die die Datei einliest, die Hex2bin Funktion> auskommentiere, läuft das Programm wirklich in 10 Sekunden durch.
Zeig doch bitte mal Code.
Wenn du damit die Laufzweit drückst, dann hat höchst wahrscheinlich der
Optimizer zugeschlagen und dir ausserdem noch einen Haufen anderes zeug
stillgelegt. Würde mich nicht wundern, wenn dadurch zb überhaupt nicht
mehr gelesen wird. Wozu von einem File lesen, wenn dann mit dem
gelesenen sowieso nichts mehr passiert.
Leute,
ich würde vorschlagen, der TO macht das in 64K Blöcken und nicht per
Pointer und benutzt eine lokale Variable, um dort den Hi und den Lo Teil
zusammenzusetzen.
So ein x86er im Realmode kann nämlich nicht mehr als 64K ansprechen,
ohne zwischendurch am zuständigen Segmentregister herumzufummeln - und
das kostet ja auch ein bissel Zeit.
Wir reden doch vermutlich von einem 16 Bit Prozessor - ODER?
W.S.
W.S. schrieb:> Leute,> ich würde vorschlagen, der TO macht das in 64K Blöcken und nicht per> Pointer und benutzt eine lokale Variable, um dort den Hi und den Lo Teil> zusammenzusetzen.>> So ein x86er im Realmode kann nämlich nicht mehr als 64K ansprechen,> ohne zwischendurch am zuständigen Segmentregister herumzufummeln - und> das kostet ja auch ein bissel Zeit.
Ich geb dir in der Sache recht.
Das erklärt aber alles nicht, dass ein bischen unkompliziertes
Rumfummeln im Speicher 3mal so lange dauert, wie den Speicher erst mal
aus einem externen Medium mit all seinen Unzulänglichkeiten zu füllen.
Seine Zeiten könnten ein starkes Indiz dafür sein, dass da noch
irgendetwas anderes im Gange ist, an das er zur Zeit überhaupt nicht
denkt. 30 Sekunden um 1 MB Bytes aus 2MB Text zu generieren ist viel zu
lang für einen 100Mhz Rechner. Selbst wenn das Bearbeiten eines Bytes
aus 2 Char 100 Takte benötigt, ist der Vorgang in 1 Sekunde
abgeschlossen. Und mit 100 Takten für
1
do{
2
*bptr=h2b_look_high[*hptr++];
3
*bptr|=h2b_look_low[*hptr++];
4
bptr++;
5
}while(--length);
hab ich, denke ich, mehr als extrem großzügig dem Compiler ein völliges
Versagen in der Codegenerierung zugestanden.
Karl Heinz schrieb:> 30 Sekunden um 1 MB Bytes aus 2MB Text zu generieren ist viel zu> lang
Ja, sehe ich auch so. Wenn das irgendein embedded 80186 Zeugs ist, dann
käme mir der Verdacht, daß der Krempel vielleicht im proteced mode
läuft, das Setup dafür aber nicht stimmt und es deshalb bei jedem
Zugriff nen Fault gibt, der aber kommentarlos vom Rest des Systems
ignoriert wird, nachdem es sich herausgestellt hat, daß der Zugriff
harmlos ist. Jaja, ist alles nur reine Spekulation meinerseits.
W.S.
80186, 100MHz???, 24bit???
Der ist eigentlich nur ein ( marginal verbesserter, ala NEC V20..50)
8086. EIN MegaByte Adressraum.
Je nach Speicher-Modell (Large, Huge), ist der mit den Beispielcode
lange Beschäftigt.
Der kann aber XLAT und damit "schnelles" TableLookup.