Servus,
Ich programmiere gerade ein sehr "hochfrequentes" Programm.
Ich würde gerne wissen wieviel Takte bzw. Instruktionen für eine
if-Abfrage und eine hochzählende for-Schleife benötigt werden.
Hier die Codeschnipsel:
1
if(abl2_A<=0.005&&abl2_B<=0.005){
abl2_A und abl2_B sind vom Typ double.
Und die for-Schleife:
Methodisch:
Schau dir das Assemblerlisting an und zähle laut Datenblatt die
benötigten Takte zusammen.
Praktisch:
Double und schnell beißt sich ganz arg.
:-)
Diddi schrieb:> Ich programmiere gerade ein sehr "hochfrequentes" Programm.> Ich würde gerne wissen wieviel Takte bzw. Instruktionen für eine> if-Abfrage und eine hochzählende for-Schleife benötigt werden.
Die Zahl der Taktzyklen der for-Schleife dürfte im Vergleich zu den
floating-point-Berechnungen auch nicht mehr weiter auffallen.
Diddi schrieb:> Achso ich habe vergessen zu erwähnen, dass ich auf einem AVR mega8 mit> 16Mhz programmiere.> lg> DiddiPeter Dannegger schrieb:> Lads in AVR-Studio, setzte Brechpunkte davor und dahinter und laß Dir> den Zyklencounter anzeigen.>>> Peter
Wenn er das auf der Hardware packt .... Respekt! Geht das eigentlich
auch im Simulator?
Matthias Lipinsky schrieb:> Also ich meine, eine Variable mit plus_eins, oder lieber zwei> Hilfszähler..?
Am besten der Vorschlag von Jörg. Dann kann der Compiler sich drum
kümmern, das möglichst effizient zu machen. Deine Schleifen werden beide
im Bestfall vom Compiler automatisch durch memmove ersetzt, im
schlechtesten Fall behindern sie die Optimierung.
Matthias Lipinsky schrieb:> Mich würde mal interessieren, was sinnvoller/effektiver ist:
Davon abgesehen, dass das zweite wegen eines fehlenden Semikolons
nicht compiliert und nur ein Element weniger als das erste kopiert,
erzeugt GCC ansonsten identischen Code.
Nur aus Interesse:
Warum ist die memmove Methode schneller?
Die wird doch intern auch mit irgend einer Art Schleife arbeiten,
zusätzlich kommt noch der Funktionsaufruf und die sizeoff-Berechnung.
Klärt mich auf :-)
Masl schrieb:> Warum ist die memmove Methode schneller?
Handoptimiert. Die sizeof() Rechnung ist kein Drama, mit 17 lässt sich
recht effizient multiplizieren (16+1).
Masl schrieb:> Warum ist die memmove Methode schneller?
Sie ist nicht per se schneller, hat aber die Chance, handoptimierten
Code zu benutzen, und sie ist halt standardisiert. Solange du nicht
mit -ffreestanding arbeitest, darf dabei der Compiler auch statt der
Bibliotheksfunktion eine eigene Implementierung liefern, falls er der
Meinung ist, damit günstiger zu fahren.
Im vorliegenden Fall ist der Compiler mit dem expliziten C-Code
tatsächlich gar nicht so schlecht.
Masl schrieb:> Nur aus Interesse:> Warum ist die memmove Methode schneller?
Ist sie nicht notwendigerweise
> Die wird doch intern auch mit irgend einer Art Schleife arbeiten,> zusätzlich kommt noch der Funktionsaufruf und die sizeoff-Berechnung.>> Klärt mich auf :-)
Weil sie vom Compilerhersteller auf deine µC, dein Board, deinen
speziellen Computer angepasst werden kann. Wenn dein Rechner über einen
DMA Kanal verfügt, könnte der Compiler auch soweit gehen, den Transfer
gar nicht über die CPU abzuarbeiten, sondern über DMA oder irgendwelche
anderen Mechanismen, die du mit einer simplen Schleife nicht aktivieren
kannst.
Karl Heinz Buchegger schrieb:> Wenn dein Rechner über einen DMA Kanal verfügt,> könnte der Compiler auch soweit gehen, den Transfer> gar nicht über die CPU abzuarbeiten, sondern über DMA oder irgendwelche> anderen Mechanismen, die du mit einer simplen Schleife nicht aktivieren> kannst.
Es gibt Compiler, die memmove auf DMA abbilden??? Schwer vorzustellen.
Johann L. schrieb:> Es gibt Compiler, die memmove auf DMA abbilden??? Schwer vorzustellen.
Zumindest könnte eine Bibliotheksfunktion das problemlos tun.
Johann L. schrieb:>> gar nicht über die CPU abzuarbeiten, sondern über DMA oder irgendwelche>> anderen Mechanismen, die du mit einer simplen Schleife nicht aktivieren>> kannst.>> Es gibt Compiler, die memmove auf DMA abbilden??? Schwer vorzustellen.
Warum soll das schwer vorstellbar sein?
memmove ist eine Bibliotheksfunktion mit definierter Funktionalität.
Wenn der Hersteller einen Weg findet, das schneller als mit einer
CPU-Schleife zu machen, dann kann er das tun.
Johann L. schrieb:> Es gibt Compiler, die memmove auf DMA abbilden??? Schwer vorzustellen.
Ausserdem nicht lohnend, weil eher langsamer als mit der CPU. Die
üblichen DMA-Controller von Mikrocontrollern sind darauf nicht
optimiert, auch wenn es funktionieren mag. Allenfalls als
Background-Geschubse von langsamem externen Speicher kann das Sinn
ergeben.
Jörg Wunsch schrieb:> Johann L. schrieb:>> Es gibt Compiler, die memmove auf DMA abbilden??? Schwer vorzustellen.>> Zumindest könnte eine Bibliotheksfunktion das problemlos tun.
Es geht weniger darum, ob sie es prinzipiell tun kann oder nicht,
sondern ob es zum Standard passt, wenn die Implementierung damit
beginnt, Hardware auf diese Weise zu verwenden und zu programmieren.
Johann L. schrieb:> Es geht weniger darum, ob sie es prinzipiell tun kann oder nicht,> sondern ob es zum Standard passt, wenn die Implementierung damit> beginnt, Hardware auf diese Weise zu verwenden und zu programmieren.
Johann, jetzt muss ich mich aber wundern.
Du weißt doch am allerbesten, dass der C-Standard so gut wie nichts über
Hardware aussagt.
Aber: anderes Beispiel
Im Z80 gab es die Instruktionen LDIR, LDDR, etc. Ihre Funktion war
prinzipiell die eines Block-Shifts. HL, DE, BC sind CPU-Register
ein LDIR machte (in relaxter Assembler / C Mischnotation)
1: *(DE) = *(HL)
HL++
DE++
BC--
if BC == 0
goto 1
wieviele Takte pro transferiertem Byte notwendig waren weiß ich nicht
mehr. Es waren auf jeden Fall wesentlich weniger, als wie wenn man das
selber ausprogrammiert.
Ob heutige Compiler eine Schleife
for( i = 0; i < 100; i++ )
dest[i] = src[i]
auf einem Z80 in
LD DE, #dest
LD HL, #src
LD BC, 100 * sizeof(*dest)
LDIR
übersetzen würden, kann ich nicht sagen. Wohl auch deswegen, weil es
kaum noch Z80 C-Compiler gibt. Aber in einem memmove ist das eine
leichte Übung.
Anderes Beispiel:
Die (alten) EZ-USB Chips hatten einen 8051er Befehlssatz, dort war das
Speicher-Umkopieren über eine Schleife zu langsam für die erwünschte
USB-Performance.
Deshalb gabs als Erweiterung die autopointer/autodata Register.
(siehe Datenblattschnippsel).
Der Compiler kannte die nicht und konnte die folglich auch nicht
selbständing für die Optimierung einsetzen, aber innerhalb der lib
konnte man die problemlos in memmove, memcpy, memset, strlen usw.
verwursten.
Karl Heinz Buchegger schrieb:> Johann L. schrieb:>>> Es geht weniger darum, ob sie es prinzipiell tun kann oder nicht,>> sondern ob es zum Standard passt, wenn die Implementierung damit>> beginnt, Hardware auf diese Weise zu verwenden und zu programmieren.>> Johann, jetzt muss ich mich aber wundern.> Du weißt doch am allerbesten, dass der C-Standard so gut wie nichts über> Hardware aussagt.
Der C-Standard ist eine Sache. Aber was bringt ein Compiler, der das,
was nicht durch den Standard abgedeckt ist, zu 100% ausnutzt?
Ein Programmierer, der ein C Programm schreibt, braucht 3 Dinge:
1) Einen Sprachstandard, der Syntax und Semantik beschreibt.
2) Ein Compiler, der sich daran hält.
3) Ein Compiler, der "vernünftigen" Code erzeugt.
Je mehr Unwärbarkeiten des Standards ein Compiler ausschöpft, desto
weniger wird 3) erfüllt sein. Der Compiler wäre dann zwar immer noch
standardkonform, aber niemand würde ihn einsetzen, da witzlos.
Wem würde solch ein Compiler nutzen?
Beispiel: "volatile" wird durch die Implementierung definiert, mithin
auch die Semantik von (mit der üblichen Definition von POTRC):
>> PORTC = 0;
Zunächst wird 0 nach PORTC geschrieben. Weil "PORTC = 0" ein Ausdruck
ist, der auszuwerten ist und PORTC volatile ist, wird PORTC wieder
zurückgelesen, analog zu
>> (void) PORTC;
oder
>> char a = PORTC = 0;
Das alles wäre durch den Standard abgedeckt. Aber würde irgendjemand das
wollen? Ich behaupte: Nein.
Stell dir einfach vor, PORTC ist ein Latch.
Ergo: Den Standard zu erfüllen ist nicht alles. Wenn ein Compiler dabei
rauskommt, der praktisch unbenutzbar ist, ist der Compiler zu 100%
unbrauchbar und keinem gedient.
Nehmen wir also mal an, die Verwendung einer DMA sei transparent, d.h.
deren Verwendung ist ausserhalb der abstrakten Maschine nicht
wahrnehmbar. Dies bedeutet insbesondere, daß eine memmove Operation
atomar ablaufen muss.
Ich stelle meine Frage also etwas anders: Ist eine DMA in memmove
vorstellbar, so daß deren Verwendung nicht aus der abstrakten Maschine
"entkommt" und zusätzlich 3) erfüllt bleibt?
Johann L. schrieb:> Ich stelle meine Frage also etwas anders: Ist eine DMA in memmove> vorstellbar, so daß deren Verwendung nicht aus der abstrakten Maschine> "entkommt" und zusätzlich 3) erfüllt bleibt?
Atomare Ausführung aus Sicht der virtuellen Maschine ist nicht in jedem
Fall zwingend. Die Nebenläufigkeit von DMA ist so lange kein Problem,
wie der Compiler an passender Stelle auf dessen Beendigung wartet. Er
könnte also den Transfer starten und mit anderem Code weiter machen, bis
irgendwann einer der beiden Speicherbereiche benötigt wird oder er dies
zumindest in Betracht ziehen muss.
Solange währenddessen nur lokale Daten ohne erforderliche Adresse
verarbeitet werden ist das kein Problem, da es keine Überschneidung
geben kann. Nur bei globalen Daten, und solchen deren Adresse irgendwo
vorkommt, wird es komplizierter. Da besteht eine gewisse Verwandschaft
zur Handhabung von potentiellem Aliasing bei Pointern.
Das ist im Grunde nicht viel anders als anno 8086, als der 8087
Koprozessor seine Speicheroperationen nebenläufig durchführte und erst
der WAIT-Befehl wieder für Ordnung sorgte (vgl. Warten auf Ende vom DMA,
plus Barrier-Befehl).
Allerdings ist das natürlich eine sehr akademische Diskussion. Denn wer
einigermassen bei Verstand ist, der programmiert so etwas explizit,
statt es in einem Compiler einzubauen. Heutige und denen ähnliche
Architekturen vorausgesetzt.
Und bei jenen Zwergen wie dem erwähten 51er, bei dem es sich ggf. lohnen
kann weil die programmierte Version viel langsamer ist, ist eine rein
atomare Version, also mit sofortigem Warten auf Beendigung, auch schon
sinnvoll. Nebenläufigkeit erwartet man in solchem Kontext wohl nicht
wirklich.
Das ist aber bei DMA auch nicht anders als bei CPU-Registern. Kaputt
machen gilt nicht. Wenn DMA grad aktiv, dann verboten, andernfalls
vorherigen Zustand sichern und später wiederherstellen.
Der Unterschied ist aber, daß ein Compiler den Worst-Case annehmen muss,
während eine Applikation die DMA etc. verwendet viel implizites Wissen
über deren Verwendung hat, welches einem Compiler nicht zur Verfügung
steht.
OK, OK.
Ich gestehs dir zu: DMA war vielleicht wirklich kein sinnvoller Begriff.
Nichts desto trotz ändert das ja nix an der Aussage: memmove macht die
Dinge nicht schlechter als eine selbst geschriebene Schleife, kann aber
schneller sein.
Diddi schrieb:> Servus,> Ich programmiere gerade ein sehr "hochfrequentes" Programm.> Ich würde gerne wissen wieviel Takte bzw. Instruktionen für eine> if-Abfrage und eine hochzählende for-Schleife benötigt werden.> Hier die Codeschnipsel:>> [...] double [...]Diddi schrieb:> ATmega8 mit 16Mhz
*hüstel*
Du verwendest also double auf einem ATmega8 und machst dir nen Kopf um
eine if-Abfrage?
Bevor du anfängst an der if-Abfrage zu knaupen solltest du dir
überlegen, *wo* die Zeit für dein "hochfrequentes" Programm denn
verloren geht.
Erst profilen. Dann optimiern. Und zwar das, was dir die Ressource
frisst.
Wenn etwas im Code zB 90% der Resource braucht und das 10% besser wird,
bringt das 9× mehr, als etwas das nur 1% braucht um 99% zu verbessern!
Danke für die vielen Antworten.
Ich habe mein Programm mal analysiert mit der Stop Watch stelle aber
keinerlei Unterschied zwischen double und float fesst. Float(4 Byte)
müsste doch deutlich schneller sein wie double(8 Byte). Dies ist
allerdings nicht der Fall. Leider kann ich nicht auf die Fließkomma
Berechnungen verzichten.
Diddi schrieb:> Ich habe mein Programm mal analysiert mit der Stop Watch stelle aber> keinerlei Unterschied zwischen double und float fesst. Float(4 Byte)> müsste doch deutlich schneller sein wie double(8 Byte).
Das liegt daran, daß auf einem AVR double auch nur 4 Byte groß ist (auch
wenn das eigentlich nicht ISO-konform ist).
> Leider kann ich nicht auf die Fließkomma
Berechnungen verzichten.
Ich gehe mal davon aus, dass du wirklich die Genauigkeit von "double"
brauchst. Da dein AVR-Compiler gar kein "double" kann, solltest du zu
einem anderen Prozessor greifen bei dem ein Compiler für "double"
verfügbar ist.
Floh schrieb:> Schau dir das Assemblerlisting an und zähle laut Datenblatt die> benötigten Takte zusammen.
Wenn man nicht ganz so masochistisch veranlagt ist, kann man auch die
Stop-Watch in AVR-Studio benutzen und dort im Simulator die Anzahl der
benötigten Taktzyklen ablesen.
Diddi schrieb:> allerdings nicht der Fall. Leider kann ich nicht auf die Fließkomma> Berechnungen verzichten.
Das kann schon sein. Kann aber auch nicht sein.
Wenn du das Zeug mal herzeigst, kannn sich wer überlegen, ob ihm eine
Alternative einfällt.