Abfrage auf Null oder "Nicht"!?

Gast #2766182
Lesenswert?

Hallo!

Kurze Frage:
Welche Codeausführung ist schneller und warum?
1
unsigned char i;
2

3
...
4

5
void Variante1()
6
{
7
  if(i == 0)
8
   {
9
     i = 1;
10
     /* Code ausführen */
11
     i = 0;
12
   }
13
}
14

15

16
void Variante2()
17
{
18
  if(!i)
19
   {
20
     i = 1;
21
     /* Code ausführen */
22
     i = 0;
23
   }
24
}
25

26

27
void Variante3()
28
{
29
  if(i != 0)
30
   {
31
     i = 1;
32
     /* Code ausführen */
33
     i = 0;
34
   }
35
}
36

37

38
void Variante4()
39
{
40
  if(i > 0)
41
   {
42
     i = 1;
43
     /* Code ausführen */
44
     i = 0;
45
   }
46
}
47

48

49
void Variante5()
50
{
51
  if(i)
52
   {
53
     return;
54
   }
55

56
  i = 1;
57
  /* Code ausführen */
58
  i = 0;
59
}
60

61
void Variante6()
62
{
63
  if(i == 1)
64
   {
65
     return;
66
   }
67

68
  i = 1;
69
  /* Code ausführen */
70
  i = 0;
71
}

Die jeweilige Funktion VarianteX, kann durch Interrupts mehrfach 
aufgerufen werden. Des Wegen die Sperre.

Danke.

Gruß
Mure
#2766193
Lesenswert?

Mure schrieb:
> Welche Codeausführung ist schneller und warum?

Wie wäre es mit einfach ausprobieren?
Ich würde allerdings wetten, dass keine Variante irgendwie schneller 
wäre, und die meisten (zumindest 1-4) sogar absolut identischen Code 
erzeugen.

Mure schrieb:
> Die jeweilige Funktion VarianteX, kann durch Interrupts mehrfach
> aufgerufen werden. Des Wegen die Sperre.

Da hilft dir deine Sperre aber nicht wirklich. Was, wenn der Interrupt 
zwischen dem "if" und dem "i = 1;" auftritt?
Gast #2766215
Lesenswert?

Stefan Ernst schrieb:
> Was, wenn der Interrupt
> zwischen dem "if" und dem "i = 1;" auftritt?

Äh. Gute Frage! - Nächste Frage! Wie löse ich das Problem?

Stefan Ernst schrieb:
> Edit: Allerdings machen Variante 3 und 4 was anderes.

Nochmal Äh! - Richtig erkannt. Falsch von mir getippt!

void Variante3()
{
  if(i != 1)
   {
     i = 1;
     /* Code ausführen */
     i = 0;
   }
}


void Variante4()
{
  if(i > 0)
   {
     return;
   }

  i = 1;
  /* Code ausführen */
  i = 0;
}

Doof schrieb:
> Du hast in diesem Zusammenhang hoffentlich an einen Cache-Flush gedacht,
> oder?

Ein was!? - Nö! Bin wohl doofer als du...
Gast #2766217
Lesenswert?

Stefan Ernst schrieb:
>> Variante 1 und Variante 2 machen verschiende dinge
>
> Nö. "!i" und "i == 0" sind identisch.

Das stimmt schon, aber es trotzdem Varianten dabei, die nicht dasselbe 
tun.

Beispiele:

> void Variante4()
> {
>   if(i > 0)
>    {
>      i = 1;
>      /* Code ausführen */
>      i = 0;
>    }
> }
hier wird bei i>0, also auch bei i==1 der Code ausgeführt,
und

> void Variante6()
> {
>   if(i == 1)
>    {
>      return;
>    }
>
>   i = 1;
>   /* Code ausführen */
>   i = 0;
> }

hier kommt bei i==1 'return' und der Code wird nicht ausgeführt.
#2766221
Lesenswert?

Es dürften alle gleich sein, so lange der Instruktionssatz 
Vergleichsoperationen für Grösser-/Kleiner-/Gleich hat.

Achja: Deine Sperre ist keine gute Implementation für das Problem. 
Einerseits kann, wie bereits gesagt wurde, der Interrupt genau in die 
Änderung von i reinfunken. Klingt unwahscheinlich, wird in der Praxis 
aber sicher passieren und kaum zu findende Fehler auslösen. Andererseits 
weist die Sperre darauf hin, dass deine Interruptroutine lang im 
Verhältnis zum Aufrufinterval ist. Alternativ könntest du auch ein Flag 
setzen, das dann in einer Hauptschleife ausgewertet und abgearbeitet 
wird. Oder zumindest die Sperre sauber implementieren, Stichwort: 
http://de.wikipedia.org/wiki/Semaphor_%28Informatik%29
Gast #2766235
Lesenswert?

Dann müsste man noch i als volatile definieren, dass auch bei der 
if-Abfrage diese neu eingelesen werden muss:
1
volatile unsigned char i;
2
void Variante7()
3
{
4
 i++;
5
 if(i == 1)
6
  {
7
    /* Code ausführen */
8
  }
9
 i--;
10

11
 return;
12
}
Gast #2766242
Lesenswert?

Mure schrieb:
> Dann müsste man noch i als volatile definieren

Solange i++ nicht mit einer atomaren Operation errechnet werden kann, 
nutzt volatile überhaupt nichts, denn dann kann da immer noch ein 
Interrupt dazwischenfunken.

Es nutzt nichts, da muust du die Doku zu deinem Compiler und deinem 
Prozessor lesen, und dafür sorgen, daß i++ oder i=1, oder was auch 
immer, nicht unterbrochen werden kann.

Oliver
Gast #2766269
Lesenswert?

Du bräuchtest eine Konstruktion wie diese
1
void funktion(...)
2
{
3
  Disable_Interrupts();
4
  if (lock == 0)
5
  {
6
    lock = 1;
7
    Enable_Interrupts();
8
    // Dein Code
9
    lock = 0;
10
    return;
11
  }
12
  Enable_Interrupts();
13
  // return
14
}

Setzt voraus, daß die Zuweisung
1
    lock = 0;
atomar abläuft.
Gast #2766282
Lesenswert?

P. M. schrieb:
> Kann man recht einfach auch selber schreiben.

Wenn man weiß wie's geht. Assembler kann ich leider nicht ...


P. M. schrieb:
> der mache die Zuweisung
> an i einfach atomar, indem du Interrupts kurz ausschaltest.

Quasi so was:
1
#define EnterMutex(_myMutex)   _DINT(); /* Disable Global interrupt */ \
2
                               _myMutex++; \
3
                               if(_myMutex != 1){ \
4
                                  goto _LeaveMutex; \
5
                               } \
6
                               _EINT(); /* Enable Global interrupt */\
7

8
#define LeaveMutex(_myMutex)   _DINT(); /* Disable Global interrupt */ \
9
                               _myMutex--; \
10
                               _EINT(); /* Enable Global interrupt */\
11
                                                
12

13
unsigned char i;
14
void Variante7()
15
{
16
  EnterMutex(i);
17
  /* Code ausführen */
18
  LeaveMutex(i);
19

20
 return;
21
}

Aber streng genommen müsste ich den Zustand von Global Interrupt Flag 
abfragen, bevor ich es setze. Ich glaube es ist möglich, das die Mutex 
betreten wird (Main-Loop), wenn der globale Interrupt deaktiviert ist.
Gast #2766288
Lesenswert?

Mure schrieb:
> Bronco schrieb:
>> Du bräuchtest eine Konstruktion wie diese ...
>
> Sieht eigentlich wie meine aus.

Ah! - Da fehlt das Label!
1
#define LeaveMutex(_myMutex) _LeaveMutex: 
2
                                _DINT(); /* Disable Global interrupt */ \
3
                                _myMutex--; \
4
                                _EINT(); /* Enable Global interrupt */\
Gast #2767066
Lesenswert?

Zurück zum ursprünglichen Problem:
Richtig schlau bin ich jetzt aber noch immer nicht!

Wie macht man jetzt am Besten eine Mutex für den MSP?
Und zwar so, dass ich diese als Funktion oder Makro habe und in beliebig 
vielen Codeabschnitten einsetzen kann?!?

Gruß
Mure
Gast #2767121
Lesenswert?

Mure schrieb:
> Sven P. schrieb:
>>> Eine Zeile, ein Befehl, alles atomar.
>> Das ist aber gelogen...
>
> Det habe ich mich grad auch gefragt.

Hey Mure,

falls Du das wirklich nicht weißt:
Atomar bedeutet, daß es für die CPU ein ununterbrechbarer Vorgang ist.
Das sind z.B. einzelne Maschinen/Assembler-Befehle.
C-Code wird vom C-Compiler in Maschinen-Code umgewandelt.
D.h. ein C-Befehl ergibt oft in mehrere Maschinen-Befehle und ist 
damit nicht atomar!

Bsp:
8-Bit Microcontroller macht
1
uint32_t x:
2
x++;
Das x++ erzeugt in vier Assembler-Befehle, weil die CPU pro Befehl nur 
jeweils 8 der 32Bit berechnen kann.

So ist es ein sehr typischer Fehler, auf Variablen aus dem Interrupt 
heraus zuzugreifen, dabei solche Befehlsblöcke zu unterbrechnen und 
damit den Variableninhalt zu zerstören.

Falls Du IAR verwendest: lies mal "Monitor"-Funktionen in der Doku nach.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren