Forum: Compiler & IDEs itoa langsam


von Florian_unbekannto (Gast)


Lesenswert?

Hi,

nachdem ich jetzt das problem mit der Multiplikation gelöst habe, 
wolltre ich Fragen ob es einen asm Code für die itoa Funkrion gibt. Ich 
finde diese Funktion braucht recht lange. Warscheinlich wegen der 
Division. Gibt es da was effektivieres?

: Verschoben durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

itoa der AVR-LibC steht bereits in asm und verwendet eine optimierte[tm] 
Division.

Ditto for utoa, ltoa und ultoa.

Oder meinst du eine andere libc?

von Florian_unbekannto (Gast)


Lesenswert?

also wenn ich die ganz normale itoa funktion in c benutze ist die schon 
das absolute maximum was rauszuholen ist?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Du kannst es weiter optimieren wenn du die Funktion nur für eine 
bestimmte Bases (radix) brauchst und dein µC multiplizieren kann.

Warum stört die Geschwindigkeit, ist doch eh nur für die Ausgabe. 
Humans are slooow devices.

von Karl H. (kbuchegg)


Lesenswert?

Florian_unbekannto schrieb:
> Hi,
>
> nachdem ich jetzt das problem mit der Multiplikation gelöst habe,
> wolltre ich Fragen ob es einen asm Code für die itoa Funkrion gibt. Ich
> finde diese Funktion braucht recht lange. Warscheinlich wegen der
> Division. Gibt es da was effektivieres?

Sukzessive Subtraktion.
Das Prinzip ist einfache.
Wie oft kannst du 10000 subtrahieren, bis das Ergebnis unter 10000 
fällt? Das ist die Zahl die an die Zehntausenderstelle ausgegeben werden 
muss.

Mit dem Rest geht es dann weiter:
WIe oft kann 1000 subtrahiert werden?

usw. usw.

Das ganze wird speziell dann effektiv, wenn du den Zahlenbereich von 
vorne herein begrenzen kannst. Um zb Zahlen im Bereich bis 10000 
auszugeben, benötigt man nur 3 Stellen, entsprechend einfacher wird dann 
die Umwandlung
1
void deztoa( uint8_t number, char* ascii )
2
{
3
  uint8_t i;
4
5
  i = 0;
6
  while( number > 1000 )
7
  {
8
    i++;
9
    number -= 1000;
10
  }
11
  ascii[0] = i + '0';
12
13
  i = 0;
14
  while( number > 100 )
15
  {
16
    i++;
17
    number -= 100;
18
  }
19
  ascii[1] = i + '0';
20
21
  ascii[2] = number + '0';
22
23
  ascii[3] = '\0';
24
}

Im Prinzip kann man das natürlich auch mit größeren Zahlenbereichen 
machen, speziell wenn die ansonsten notwendigen Divisionen bei größeren 
Bitbreiten schon aufwändig werden.


Aber:
Ist das wirklich dein Bottleneck?
derartige Umwandlungen sind normalerweise nur notwendig, wenn es gilt 
für den Benutzer etwas wo hinzuschreiben. Ein Vorgang der aus Sicht des 
µC nur alle heiligen Zeiten notwendig ist und bei dem es ziemlich egal 
ist, wie lang er benötigt.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Hier ist übrigens die aktuelle Quelle. Wenn du das signifikant schneller 
hinbekommst gewinnst du nen Keks!

http://svn.savannah.nongnu.org/viewvc/trunk/avr-libc/libc/misc/utoa_ncheck.S?revision=2298&root=avr-libc&view=markup

von Florian_unbekannto (Gast)


Lesenswert?

ja, weil er das oft machen muss...dann addieren sich die 500 taktzyklen 
schon.

Also ich brauche nur basis 10 und muss Werte von 0 bis 10000 Wandeln.

von Karl H. (kbuchegg)


Lesenswert?

Florian_unbekannto schrieb:
> ja, weil er das oft machen muss...dann addieren sich die 500 taktzyklen
> schon.

Ah geh.
Ist doch völlig uninteressant.
WIe oft in der Sekunde hast du denn den Fall, dass du eine Zahl wandeln 
musst? 3 mal - 4 mal?
Pro Sekunde 1500 Taktzyklen bei 10 Millionen zur Verfügung stehenden 
Taktzyklen - interessiert doch keinen wirklich. Am allerewenigsten den 
µC.



Du optimierst im Moment an Dingen rum, die du nicht optimieren musst. 
Vergeudete Zeit.
Kümmere dich lieber darum, dass der Rest deines Programmes sauber und 
wartbar ist. Da haben alle mehr davon.

von Falk B. (falk)


Lesenswert?

@ Florian_unbekannto (Gast)

>nachdem ich jetzt das problem mit der Multiplikation gelöst habe,

Welches?

>wolltre ich Fragen ob es einen asm Code für die itoa Funkrion gibt.

Brauchst du den?

>Ich finde diese Funktion braucht recht lange.

Hast du es GEMESSEN? Im Simulator?

>Warscheinlich wegen der Division.

Die braucht hat etwas Zeit.

>Gibt es da was effektivieres?

Warum? Wieviel tausend Zahlen musst du pro Sekunde erzeugen? Wer soll 
die alle lesen? Wenn du nicht mehrere Megabyte an Textdatein mit Daten 
erzeugen willst, ist die Funktin für ein paar Ausgaben auf dem Terminal 
oder LCD mehr als schnell genug.

von Karl H. (kbuchegg)


Lesenswert?

Johann L. schrieb:
> Hier ist übrigens die aktuelle Quelle. Wenn du das signifikant schneller
> hinbekommst gewinnst du nen Keks!
>
> 
http://svn.savannah.nongnu.org/viewvc/trunk/avr-libc/libc/misc/utoa_ncheck.S?revision=2298&root=avr-libc&view=markup


:-)
Ist genau die Subtraktionsmethode, die ich weiter oben angerissen habe.

von Georg G. (df2au)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Um zb Zahlen im Bereich bis 10000
> auszugeben, benötigt man nur 3 Stellen

irgendwie blicke ich durch dein Beispiel nicht durch... 10000 sind für 
mich 4 Stellen. Und ich vermisse die 10er. Oder bin ich mal wieder mit 
Blindheit geschlagen?

von Florian_unbekannto (Gast)


Lesenswert?

ja gut, vielleicht habt ihr ja recht. Nur habe Angst dass ich nachher 
total überlastet mit(also der Xmega;-))...Die komplette ADC Wandlung 
benötigt 1500 Taktzyklen..Und das mal vierzig oder so...

von Karl H. (kbuchegg)


Lesenswert?

Georg G. schrieb:
> Karl Heinz Buchegger schrieb:
>> Um zb Zahlen im Bereich bis 10000
>> auszugeben, benötigt man nur 3 Stellen
>
> irgendwie blicke ich durch dein Beispiel nicht durch... 10000 sind für
> mich 4 Stellen.

Ähm. Moment
(Finger rauskram)
Jep, du hast recht. Zu schnell getippt.

> Und ich vermisse die 10er. Oder bin ich mal wieder mit
> Blindheit geschlagen?

Auch hier: zu schnell getippt und auf senden gedrückt.

(OK, nimm den Code als Prinzipskizze mit 'kleinen' Fehlern)

von Karl H. (kbuchegg)


Lesenswert?

Florian_unbekannto schrieb:
> ja gut, vielleicht habt ihr ja recht. Nur habe Angst dass ich nachher
> total überlastet mit(also der Xmega;-))...Die komplette ADC Wandlung
> benötigt 1500 Taktzyklen..

Was braucht da 1500 Taktzyklen?

> Und das mal vierzig oder so...

40 mal 1500 macht 60000, also 60 tausend.

Wie schnell taktet dein XMega?
Bei angenommenen 10Mhz und einem durchschnittlichen 
Befehl/Zyklenverhätlnis von sagen wir mal 1.5 (nicht alle Befehle 
brauchen 1 Taktzyklus), bedeutet das rund 100 Durchgänge pro Sekunde 
durch alle 40 ADC!

Kannst du wirklich so schnell schauen, dass du 40 Messerte die sich 100 
mal in der Sekunde ändern noch überblicken bzw. überwachen kannst?

von Purzel H. (hacky)


Lesenswert?

Die integer-zu-BCD Wandlung muss man ja nur fuer den Benutzer machen, 
und der kann mehr als 3-4mal pro Sekunde gar nicht verarbeiten. Das 
waeren dann ein paar hundert zyklen.

von Florian_unbekannto (Gast)


Lesenswert?

Fettes Danke Karl Heinz!

bin jetzt von 1550 Zyklen auf 300 runter! Sehr Geil.

hatte folgendes 9.987.750 war mein ausgangswert. Das stand für 99,87 
Grad Celcius. Vorer musste ich durch 10000 teilen und dann itoa 
ausführen. Folge: 1550 Taktzyklen . mit dem Substrahieren geht es in 300 
mit super wenig Code!

Da bekommt man 4000 Takte geschenkt, nur weil man einen besseren Algo 
hat. :-D

von Florian_unbekannto (Gast)


Lesenswert?

Florian_unbekannto schrieb:
> Fettes Danke Karl Heinz!
>
> bin jetzt von 1550 Zyklen auf 300 runter! Sehr Geil.
>
> hatte folgendes 9.987.750 war mein ausgangswert. Das stand für 99,87
> Grad Celcius. Vorer musste ich durch 10000 teilen und dann itoa
> ausführen. Folge: 1550 Taktzyklen . mit dem Substrahieren geht es in 300
> mit super wenig Code!
>
> Da bekommt man 1000*40 Takte geschenkt, nur weil man einen besseren Algo
> hat. :-D

von Wolfgang (Gast)


Lesenswert?

Florian_unbekannto schrieb:
> hatte folgendes 9.987.750 war mein ausgangswert. Das stand für 99,87
> Grad Celcius

Was ist des das für eine geile Temperaturmessung auf 10µK?

von Karl H. (kbuchegg)


Lesenswert?

Florian_unbekannto schrieb:
> Fettes Danke Karl Heinz!
>
> bin jetzt von 1550 Zyklen auf 300 runter! Sehr Geil.
>
> hatte folgendes 9.987.750 war mein ausgangswert. Das stand für 99,87
> Grad Celcius. Vorer musste ich durch 10000 teilen und dann itoa
> ausführen. Folge: 1550 Taktzyklen . mit dem Substrahieren geht es in 300
> mit super wenig Code!
>
> Da bekommt man 4000 Takte geschenkt, nur weil man einen besseren Algo
> hat. :-D

Da stimmt was nicht.
Das in itoa integrierte Verfahren unterscheidet sich nur marginal von 
dem in C ausprogrammierten. Einziger Unterschied: es ist von Hand in 
Assembler ausoptimiert worden. itoa bzw utoa müsste eigentlich schneller 
sein als die händische Version.

von Florian_unbekannto (Gast)


Lesenswert?

du hast doch selbst geschrieben, das es sdchneller ist. Außerdem sind 
die Verfahren anders. ich spare mir das dividieren durch 10000. Dann 
fängt dein verfahren mit dem subtrahieren bei der größten stelle an. das 
heißt ich muss muss nur die ersten vier stellwn machen. das itoa 
verfahren arbeitet mit modulo und das fängt hinten an. also erst einer 
dann zehner und so weiter. deshalb bin ich gezwungen erst zu dividieren.

von Walter S. (avatar)


Lesenswert?

Florian_unbekannto schrieb:
> deshalb bin ich gezwungen erst zu dividieren.

niemand zwingt dich, du kannst auch itoa machen und das Komma 
entsprechend setzen

von Florian_unbekannto (Gast)


Lesenswert?

Hier ist mal der Code, vielleicht seht ihr dann was ich meine...
Mit positiven ADC Werten geht es. negative geht leider noch nicht...
1
void deztoa( int32_t snumber, char* ascii )
2
{
3
  uint8_t i;
4
  uint32_t number;
5
  
6
  if( snumber < 0 ) {
7
      ascii[0] = '-';
8
      number = ( (unsigned)-(snumber+1) ) + 1; 
9
    }
10
  else { 
11
      number = (uint32_t)snumber;
12
    }
13
14
  i = 0;
15
  while( number > 1000000 )
16
  {
17
    i++;
18
    number -= 1000000;
19
  }
20
  ascii[1] = i + '0';
21
22
  i = 0;
23
  while( number > 100000 )
24
  {
25
    i++;
26
    number -= 100000;
27
  }
28
  ascii[2] = i + '0';
29
  i = 0;
30
  while( number > 10000 )
31
  {
32
    i++;
33
    number -= 10000;
34
  }
35
  ascii[3] = i + '0';
36
  i = 0;
37
  while( number > 1000 )
38
  {
39
    i++;
40
    number -= 1000;
41
  }
42
  ascii[4] = i + '0';
43
44
  ascii[5] = '\0';
45
}
46
47
48
int main(void)
49
{  
50
51
    adc=6209;
52
    newadc = (int32_t)adc*1585; //irgendwas mit 9 Mio.
53
    deztoa(newadc,&string2);
54
}

von Amateur (Gast)


Lesenswert?

Geh' einfach mal davon aus, dass die Leute, die die Bibliotheken 
schreiben recht fit und kompetent sind.

Allzu viel Optimierung ist da nicht drin.

... aber, wenn Du vorher schon weist, dass Du nicht alles davon 
brauchst, kannst Du vielleicht noch etwas an Geschwindigkeit 
herausholen, indem Du sie sozusagen abspeckst.

Die Programmierer der Bibliotheken sind gezwungen Funktionen zu 
schreiben, die alles können und jeden möglichen Fehler entdecken müssen. 
Eine Menge Notausgänge haben und womöglich sogar Fehlermeldungen 
generieren können müssen.

Aber wie auch schon ober erwähnt: Konvertieren tut man entweder am 
Anfang (rein) oder am Ende (raus). Musst Du zwischendurch Konvertieren, 
so ist oft in der Programmstruktur was in die Hose gegangen.

Auch wenn Du 1000 Messwerte pro Sekunde produzierst, so gibt selten 
einen Grund diese auch in ein lesbares Format zu bringen. Auge und Kopf 
sind auf jeden Fall zu langsam - sieht aber toll aus.

von Karl H. (kbuchegg)


Lesenswert?

>
1
>  if( snumber < 0 ) {
2
>       ascii[0] = '-';
3
>       number = ( (unsigned)-(snumber+1) ) + 1; 
4
>     }
5
>   else { 
6
>       number = (uint32_t)snumber;
7
>     }

du trickst dich selber aus.
1
  if( snumber < 0 ) {
2
      ascii[0] = '-';
3
      number = -snumber;
4
    }
5
  else { 
6
      number = (uint32_t)snumber;
7
    }

kurze Zwischenfrage: was steht in ascii[0], wenn die Zahl nicht negativ 
ist?

von Florian_unbekannto (Gast)


Lesenswert?

Ja das mit dem weglassen hat es meiner meinug nach gebracht.

Jetzt geht es wie gewünscht! Danke. Habe noch im else ein ascii[0]='+'; 
ergänzt.
Der Code ist jedenfalls meiner Meinung nach ideal. Gerade bei dem 
häufigen Problem ADC Werte in lesbare Form zu bringen. Ich finde jemand 
könnte das mal hier ergänzen. Immerhin ist das echt so am 
effizientesten.

http://www.mikrocontroller.net/articles/Festkommaarithmetik

Die 1585 sind nämlich der Korrekturfaktor genauso wie im Artikel 
beschrieben..

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Karl Heinz Buchegger schrieb:

> Da stimmt was nicht.
> Das in itoa integrierte Verfahren unterscheidet sich nur marginal
> von dem in C ausprogrammierten.

Schau dir die Verfahren nochmals an; sie unterscheiden sich deutlich!

Deine C-Variante beginnt mit der höchstwertigen Ziffer während itoa mit 
der kleinsten Ziffer beginnt und am Ende ein strrev macht.

von Florian_unbekannto (Gast)


Lesenswert?

Johann L. schrieb:
> Deine C-Variante beginnt mit der höchstwertigen Ziffer während itoa mit
> der kleinsten Ziffer beginnt und am Ende ein strrev macht.

genau das meinte ich johann! und das bringt den Vorteil! mich 
interssieren die letzten 5 Stellen ja garnicht. Also einer und zehne, 
hunderter...etc. Nur die Großen.

Also für meinen Zweck ist das Verfahren super!

von Josef D. (jogedua)


Lesenswert?

Florian_unbekannto schrieb:
> while( number > 1000 )

Wenn du einen Wettbewerb im Takte sparen gewinnen möchtest, könntest du 
ab hier mit einer uint16_t weiter rechnen.

von Karl H. (kbuchegg)


Lesenswert?

Johann L. schrieb:
>
> Schau dir die Verfahren nochmals an; sie unterscheiden sich deutlich!
>
> Deine C-Variante beginnt mit der höchstwertigen Ziffer während itoa mit
> der kleinsten Ziffer beginnt und am Ende ein strrev macht.

Nochmal nachgesehen.
Ja, da war der Schnellschuss zu schnell. Ich hab nur den sub gesehen und 
geschlossen. Da hat der TO schon recht. In diesem itoa wird ein 
Divisionsverfahren benutzt.

von Florian_unbekannto (Gast)


Lesenswert?

TO?

guter Tipp Josef, bring im Worstcase auch nochmal 50 Takte;-)

von Markus W. (Firma: guloshop.de) (m-w)


Lesenswert?

Zuerst sorry, dass ich diesen Thread vom letzten Jahr ausgrabe, aber das 
Thema ist nach wie vor aktuell.

Karl Heinz schrieb:
> Du optimierst im Moment an Dingen rum, die du nicht optimieren musst.
> Vergeudete Zeit.

Genau das Gleiche habe ich auch gedacht, als ich damals den Thread 
verfolgt hab.
Inzwischen ist mir aber selber ein solches Problem über den Weg 
gelaufen:
Messwerte sollen Terminal-lesbar in ASCII rausgeschleudert werden. Auf 
dem Terminal werden sie live mitverfolgt und geloggt. Es kommt also 
schon etwas auf die Geschwindigkeit an.

Nach einiger Optimiererei kam ich zu diesem Ergebnis:
1
#define UTOA(v,d) \
2
  /* returns a single ASCII digit of the number v */ \
3
  /* v: number (in) / remainder (out), */ \
4
  /* d: unsigned divisor (in, e.g. 100000U, 10000U...) */ \
5
  ( v<2U*d? (v<1U*d? '0': (v-= d, '1')): v<6U*d? (v<4U*d? \
6
    (v<3U*d? (v-= 2U*d, '2'): (v-= 3U*d, '3')): \
7
    (v<5U*d? (v-= 4U*d, '4'): (v-= 5U*d, '5')) ): \
8
  v<8U*d? (v<7U*d? (v-= 6U*d, '6'): (v-= 7U*d, '7')): \
9
  v<9U*d? (v-= 8U*d, '8'): (v-= 9U*d, '9') )
10
11
// Anwendungsbeispiel für eine 6-stellige Zahl:
12
uint32_t zahl= 123456;
13
char out[7];
14
out[0]= UTOA(zahl,100000U);
15
out[1]= UTOA(zahl,10000U);
16
out[2]= UTOA(zahl,1000U);
17
out[3]= UTOA(zahl,100U);
18
out[4]= UTOA(zahl,10U);
19
out[5]= UTOA(zahl,1U);
20
out[6]= 0;
21
// (Bitte beachten: am Ende hat die Variable zahl den Wert 0.)

Lässt sich daran noch etwas beschleunigen?
Den Tipp, unter 10000 auf eine uint16 umzusteigen werde ich sowieso noch 
berücksichtigen, danke an Josef.

von PittyJ (Gast)


Lesenswert?

Binär übertragen, dann braucht der Mikrocontroller gar nichts tun.
Auf der anderen Seite (pc) sitzt dann ein Quad-Core I7, der kann das 
viel besser.

von Svenska (Gast)


Lesenswert?

Wenn es lesbar sein muss, hexadezimal übertragen. Zweierpotenzen sind 
angenehmer.

von Marian (phiarc) Benutzerseite


Lesenswert?

Svenska schrieb:
> Wenn es lesbar sein muss, hexadezimal übertragen. Zweierpotenzen
> sind
> angenehmer.

Immer Nibbles shiften und maskieren, dann Nibble durch eine Lookuptable 
(oder ein ascii = x + 0x30; if(x>9) ascii += 16; müsste hinhauen).

: Bearbeitet durch User
von Michael (Gast)


Lesenswert?

Markus Weber schrieb:
> Auf
> dem Terminal werden sie live mitverfolgt und geloggt. Es kommt also
> schon etwas auf die Geschwindigkeit an.

PittyJ schrieb:
> Auf der anderen Seite (pc) sitzt dann ein Quad-Core I7, der kann das
> viel besser.

Und hinter dem Terminal sitzt ein Mensch, der schon bei 10Hz Update-Rate 
mit den Augen rollt ...

von Marian (phiarc) Benutzerseite


Lesenswert?

Michael schrieb:
> Und hinter dem Terminal sitzt ein Mensch, der schon bei 10Hz Update-Rate
> mit den Augen rollt ...

Das kommt immer auf die Art der Anzeige an, nech. Ein Bewegtbild mit 10 
Hz ruckelt schon ordentlich und eine z.B. Abgleichsanzeige mit 1 Hz 
Aktualisierungsrate stelle ich mir auch eher uncool vor.

von Axel S. (a-za-z0-9)


Lesenswert?

Markus Weber schrieb:
> Karl Heinz schrieb:
>> Du optimierst im Moment an Dingen rum, die du nicht optimieren musst.
>> Vergeudete Zeit.
>
> Genau das Gleiche habe ich auch gedacht, als ich damals den Thread
> verfolgt hab.
> Inzwischen ist mir aber selber ein solches Problem über den Weg
> gelaufen:
> Messwerte sollen Terminal-lesbar in ASCII rausgeschleudert werden. Auf
> dem Terminal werden sie live mitverfolgt und geloggt. Es kommt also
> schon etwas auf die Geschwindigkeit an.

Kann ich mir ehrlich gesagt überhaupt nicht vorstellen. Der UART ist 
doch schnarchlangsam im Vergleich zur CPU. Da kannst du eine 32-Bit Zahl 
doch dreimal nach ASCII konvertieren, bevor der UART auch nur ein 
Zeichen übertragen hat. Und warum hampelst du da so närrisch mit dem 
Makro herum? Paßt dir irgendwas an ultoa() nicht?


XL

von pasteiner stefan (Gast)


Lesenswert?

verwend doch einfach sprintf geht am einfachsten!

von Axel S. (a-za-z0-9)


Lesenswert?

pasteiner stefan schrieb:
> verwend doch einfach sprintf geht am einfachsten!

sprintf() zieht einen Rattenschwanz an Bibliotheksfunktionen hinter sich 
her. Das kann leicht ein paar KB Flash brauchen. Wenn man es auch 
anderswo verwendet, ist das kein Problem. Sonst ist u(l)toa() dem 
Problem eher angemessen.


XL

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.