Forum: Mikrocontroller und Digitale Elektronik Phänomen: Rechenzeit Funktionsaufrufe


von ab (Gast)


Lesenswert?

Hallo Zusammen,

bin in den letzten Tagen auf ein Problem gestoßen, das ich mir nicht 
erklären kann. Vielleicht hat jemand von euch eine Idee.

Ich habe Rechenzeitmessungen durchgeführt und es hat sich 
herausgestellt, dass die Funktion beim ersten Aufruf mindestens doppelt 
so lang wie bei den weiteren Aufrufen. Es wird kein Initialisierungscode 
oder ähnliches durchlaufen. Gemessen wurde die Rechenzeit sowohl mit 
internem Timer als auch mit Oszilloskop an einem Pin. Ich habe es an 
zwei Prozessoren (ARM9, Nios) mit zwei Entwicklungsumgebungen probiert. 
Messungen wurden z.T. mit un ohne Cache gemacht. Es ändert sich zwar 
einiges an den Absolutwerten und auch das Verhältnis, allerdings ist der 
erste Aufruf IMMER signifikant länger.

Bisher ist mir so etwas nie aufgefallen, weiß jemand, was der Grund 
dafür ist?

Mfg
Alex

von Hellseher (Gast)


Lesenswert?

In Zeile 12 das if umformulieren .

von B. S. (bestucki)


Lesenswert?

Der Hellseher spricht die richtige Problematik an:
- Welche Programmiersprache?
- Welcher Compiler?
- Welcher Code? (Minimalbeispiel)
- Betrifft es alle Funktionen oder nur die eine?
- Andere besondere Umstände? (z.B. Interrupts die dazwischenfunken)

von Uwe (Gast)


Lesenswert?

NAja die heutigen CPUs sind halt ganz schön komplex und eigenlich nicht 
mehr echtzeitfähig (deterministisch). Da sind halt nicht nur chaes 
sondern auch noch Sprungvorhersage Einheiten drinne. Es könnte halt sein 
das er beim ersten durchlauf merkt an welchen Stellen wieoft, wohin 
verzweigt wurde und er beim zweitenmal da schon weiß.

von Peter II (Gast)


Lesenswert?

Uwe schrieb:
> Es könnte halt sein
> das er beim ersten durchlauf merkt an welchen Stellen wieoft, wohin
> verzweigt wurde und er beim zweitenmal da schon weiß.

nein die Künstliche Intelligenz ist in den aktuellen CPUs noch nicht 
enthalten. Cache könnte zwar eine auswirkung haben, aber die 
Sprungvorhersage ist meines wissens nicht lernfähig.

von Falk B. (falk)


Lesenswert?

@ Uwe (Gast)

>NAja die heutigen CPUs sind halt ganz schön komplex

Ja.

>und eigenlich nicht
>mehr echtzeitfähig (deterministisch).

uhhh, da weden aber zwei völlig verschiedene Sachen zusammengerührt.

Auch komplexeste CPUs arbeiten VOLLKOMMEN deterministisch.

http://de.wikipedia.org/wiki/Determinismus_%28Algorithmus%29

Denn sonst würden sie nicht funktionieren. Das Problem ist jedoch, dass 
die Vorhersage über das Verhalten sehr komplex geworden ist und damit in 
vielen Anwendungen, die exaktes Zeitverhalten fordern, praktisch kaum 
vorhergesagt werden kann, es gibt zuviele Einflußgrößen und 
Übegrangsmöglichkeiten. Deutlich einfachere Automaten sind eben deutlich 
leichter und damit praktisch nutzbar vorhersagbar.

von Jan H. (j_hansen)


Lesenswert?

Peter II schrieb:
> nein die Künstliche Intelligenz ist in den aktuellen CPUs noch nicht
> enthalten. Cache könnte zwar eine auswirkung haben, aber die
> Sprungvorhersage ist meines wissens nicht lernfähig.

Da sagt der Wikipedia-Artikel zur SPrungvorhersage aber etwas anderes.

von ab (Gast)


Lesenswert?

Ups Sorry :-D.. Ich dachte ich hab alle Infos eingefügt.

Programmiersprache: C
Compiler: IAR 6.40 für die Tests auf dem ARM9, Nios II IDE 9.0SP2 GCC 
für den Nios
Betrifft alle Funktionen die ich ausprobiert habe. Also so an die 15.
Interrupts waren beim NIOS komplett aus, auf dem ARM war der 
Funktionsaufruf aus einem Interrupt mit der höchsten Priorität

Im folgenden Beispielcode mit einer Dummy Funktion. ACKh_OFF und ACKh_ON 
setzt den Output.
1
volatile xy;
2
void testfcn(void)
3
{
4
    int i=0;
5
    int k=0;
6
    int array[100];
7
    for(i=0;i<1;i++)
8
    {
9
        xy=i;
10
        array[0]=i;
11
        array[0]=xy;
12
    }
13
        for(k=0;k<1;k++)
14
    {
15
        xy=k;
16
        array[1]=k;
17
        array[1]=xy;
18
    }
19
}
20
21
int main()
22
{ 
23
    int i=0;
24
    ACKh_OFF;
25
    for(i=0;i<50;i++)
26
    {
27
      ACKh_OFF;
28
    }
29
    while (1)
30
    {
31
      ACKh_ON;
32
      testfcn();
33
      ACKh_OFF;
34
    }
35
  return 0;
36
}

Ich hoffe jetzt sinds genügend Infos?

Gruß
Alex

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Peter II schrieb:
> aber die Sprungvorhersage ist meines wissens nicht lernfähig.

Dann besser dein Wissen doch einfach auf: 
http://en.wikipedia.org/wiki/Branch_predictor

von Peter II (Gast)


Lesenswert?

Läubi .. schrieb:
> Dann besser dein Wissen doch einfach auf:
> http://en.wikipedia.org/wiki/Branch_predictor

habe ich gerade nachgeholt. Die Frage ist ob diese CPU denn soetwas 
schon hat. Und das danach eine funktion doppelt so schnell ist nur wegen 
der Sprungvorhersage ist nun doch sehr unrealistisch.

von ab (Gast)


Lesenswert?

Der verwendete NiosII/f hat "Dynamic branch prediction". ARM9 muss ich 
noch prüfen

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Der NIOS-II hat sowas, ARM vermutlich auch, die "Funktion" wird wohl 
komplett wegoptimiert, sodass ausschließlich der Rücksprung in der while 
zählt, und der wird sehr wohl von einem branchpredict profitieren (sogar 
wenn es nur ein sehr simpler 'last-taken' ist und kein statischer).

von Uwe (Gast)


Lesenswert?

> uhhh, da weden aber zwei völlig verschiedene Sachen zusammengerührt.
Ich meinte deterministisches Zeitverhalten

von Der (Gast)


Lesenswert?

Läubi .. schrieb:
> die "Funktion" wird wohl
> komplett wegoptimiert, sodass ausschließlich der Rücksprung in der while
> zählt

Daher sollte die Funktion ein paar volatiles enthalten.

von Peter II (Gast)


Lesenswert?

Der schrieb:
> Daher sollte die Funktion ein paar volatiles enthalten.

hat sie doch

von ab (Gast)


Lesenswert?

volatile ist vorhanden (siehe oben). Funktion wird nicht wegoptimiert. 
Ich habe den Assembler code vom NIOS angeschaut.

von Der (Gast)


Lesenswert?

Peter II schrieb:
> Der schrieb:
>> Daher sollte die Funktion ein paar volatiles enthalten.
>
> hat sie doch

Stimmt, das habe ich übersehen.

von Peter D. (peda)


Lesenswert?

1
volatile xy;
2
void testfcn(void)
3
{
4
    int i=0;
5
    int k=0;
6
    int array[100];
7
    for(i=0;i<1;i++)
8
    {
9
        xy=i;
10
        array[0]=i;
11
        array[0]=xy;
12
    }
13
        for(k=0;k<1;k++)
14
    {
15
        xy=k;
16
        array[1]=k;
17
        array[1]=xy;
18
    }
19
}

Der Compiler sollte das so optimieren:
1
volatile xy;
2
void testfcn(void)
3
{
4
        xy=0;
5
        xy;
6
        xy=0;
7
        xy;
8
}

Da ist also kaum Code übrig und dann ist der Cache laufzeit 
entscheidend.

von Peter II (Gast)


Lesenswert?

nach der optimierung sollte eigentlich gar kein Sprung mehr vorhanden 
sein

> for(i=0;i<1;i++)

wird wohl komplett aufgelöst werden.

von ab (Gast)


Lesenswert?

Ihr habt recht, ich hatte aber auch schon andere Werte > 1 drin.

Was die Cache Laufzeit angeht: da habe ich auch schon Tests dazu 
gemacht. Hatte am Anfang der Funktion eine Schleife die für einige 
Zyklen nichts gemacht hat, dann das Ausgangs-Bit getoggelt, und dann den 
eigentlichen Algorithmus abgearbeitet. So müsste ja der Cache wieder 
gefüllt sein, oder?

Werd mich mal eingehend mit der Branch prediction beschäftien. 
Vielleicht ist das wirklich die Lösung...

Sonnige Grüße aus dem Süden
Alex

von Ulrich (Gast)


Lesenswert?

Eine mögliche Ursache könnten die lokalen Variablen: Das Reeservieren 
von Speicher für die loaklen Variablen kann davon abhängen was vorher an 
Speicher genutzt wurde.

Ohne sich den erzeugten ASM Code anzusehen ist das ganze aber ein 
relativ wildes raten.

von Peter II (Gast)


Lesenswert?

Ulrich schrieb:
> Eine mögliche Ursache könnten die lokalen Variablen: Das Reeservieren
> von Speicher für die loaklen Variablen kann davon abhängen was vorher an
> Speicher genutzt wurde.

lokale Variabel werden doch auf den Stack gelegt, dabei ist egal was 
vorher vorhanden war.

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

ab schrieb:
> So müsste ja

Warum? Man sollte, wenn man irgendwas messen will (und erst recht wenn 
man auf scheinbare Widersprüche trifft) verifizieren.
Die komplette Architektur des NIOS-II ist dokumentiert, auch Caches, 
Branch-Predict und man kann das ganze sogar simulieren lassen... Also 
kein Grund zu spekulieren.

Der schrieb:
> Stimmt, das habe ich übersehen

Ich auch, trotzdem ist die Laufzeit konstant wie Peter Dannegger 
schriebt, vermutlich wird sogar der Funktionsaufruf wegoptimiert, selbst 
bei > 1 kann der Compiler durch loop-unrolling Sprünge vermeiden.

ab schrieb:
> eine Schleife die für einige Zyklen nichts gemacht hat

Wenn wirklich "nichts" gemacht wird, wird auch diese wegoptimiert, auch 
ist hier weniger der Instructioncache interessant sondern der 
Branch-Predict.

von ab (Gast)


Lesenswert?

Ich wollte nur kurz informieren, dass es der (Instruction) Cache war, 
der hier die Unterschiede in der Rechenzeit verursacht hat. Bei komplett 
abgeschaltetem Cache oder Aufruf anderweitiger Funktionen, die zu einer 
Cache-Umwälzung geführt haben, war die Rechenzeit für die 
Funktionsaufrufe gleich (langsam).

Danke für die Unterstützung hier im Forum!

Gruß
Alex

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.