Bietet C von Haus aus die Möglichkeit zur Lautzeit ändernder Code zu nutzen?



  • Bsp, habe ich folgenden Code:

    if (x > 0)
    {
      XXXX weiter unten soll i++ sein
    }
    else
    {
      XXXX weiter unten soll i-- sein
    }
    
    while (true)
    {
      XXXX // <- hier soll i++ oder i-- zur Laufzeit rein
    }
    

    Und das sollte möglichst ohne Funktionen gehen.

    Und wenn nein, gibt es andere Programmiersprachen die das können?



  • Selbstmodifizierenden Code kriegst du afaik nicht hin, aber was spricht gegen Variablen:

    int inc;
    if (x > 0)
    {
      inc = 1;
    }
    else
    {
      inc = -1;
    }
    
    while (true)
    {
      i+=inc;
    }
    

  • Mod

    Das ist ein Trick den man mit Assembler ganz leicht hinbekommen kann. Jedoch kann das mit dem immer weiter verbreiteten Execute-Bit in die Hose gehen. Afaik macht das z.B. Quake 3 an einigen Stellen und lauft entsprechend nicht mehr auf modernen Systemen, wenn man den Speicherschutz an hat.

    Wozu soll das gut sein? Fünf Byte Code einsparen?



  • SeppJ schrieb:

    Das ist ein Trick den man mit Assembler ganz leicht hinbekommen kann. Jedoch kann das mit dem immer weiter verbreiteten Execute-Bit in die Hose gehen. Afaik macht das z.B. Quake 3 an einigen Stellen und lauft entsprechend nicht mehr auf modernen Systemen, wenn man den Speicherschutz an hat.

    Wozu soll das gut sein? Fünf Byte Code einsparen?

    Es ist schneller als die Variablenvariante von CStoll.

    Allerdings wußte ich das mit dem Execute Bit nicht, daß es da Probleme gibt.


  • Mod

    Alternierender Code schrieb:

    Es ist schneller als die Variablenvariante von CStoll.

    Sicher?

    Ganz sicher?

    Getestet sicher?

    Auch wenn ich nachgucke?



  • Bietet C von Haus aus die Möglichkeit zur Lautzeit ändernder Code zu nutzen?

    Nein.
    Obwohl ich glaube, dass du dich nicht richtig ausgedrückt hast.
    Im Beispiel ist der Code für "i++" und "i--" definiert, d.h. du brauchst keinen "zur Lautzeit ändernder Code", du brauchst ihn nur zur Laufzeit entsprechend aufrufen, z.B. über Funktionszeiger.



  • Kann man mit etwas Aufwand schon machen:
    --> Sourcecode als Datei(en) bereitstellen
    --> Eigenständiges Programm zum Ändern der Datei(en) schreiben
    --> Geändertes Programm neu kompilieren und bei Erfolg aufrufen
    Frage dazu: 'Wozu soll das gut sein?' 😕
    Möchte ich nicht machen wollen! 😃



  • Alternierender Code schrieb:

    SeppJ schrieb:

    Das ist ein Trick den man mit Assembler ganz leicht hinbekommen kann. Jedoch kann das mit dem immer weiter verbreiteten Execute-Bit in die Hose gehen. Afaik macht das z.B. Quake 3 an einigen Stellen und lauft entsprechend nicht mehr auf modernen Systemen, wenn man den Speicherschutz an hat.

    Wozu soll das gut sein? Fünf Byte Code einsparen?

    Es ist schneller als die Variablenvariante von CStoll.

    Allerdings wußte ich das mit dem Execute Bit nicht, daß es da Probleme gibt.

    Vergiss Self-modifying Code. Das ist auf modernen Systemen nicht mehr angebracht. Ein großes Problem bei modernen Architekturen ist einfach, dass der Zugriff auf den RAM relativ gesehen langsamer wird. Aus dem Grund wird der Maschinencode in großen Blöcken in den Cache geladen und von dort in die Pipeline. Die CPU nimmt dann schon viele Berechnungen vor, bevor der Code ausgeführt wird. Ganz wichtig sind zB die Branch-Predictions oder das umordnen von Anweisungen. Das ganze funktioniert aber nur solange gut, solange sich die CPU darauf verlassen kann, dass der Code nicht während der Ausführung geändert wird. Wenn das passiert, dann muss die ganze Speicherseite neu geladen werden und die Pipeline geflusht werden. Das kostet mittlerweile deutlich mehr Zeit als der Unterschied zwischen einem incrl und addl.



  • Alternierender Code schrieb:

    SeppJ schrieb:

    Das ist ein Trick den man mit Assembler ganz leicht hinbekommen kann. Jedoch kann das mit dem immer weiter verbreiteten Execute-Bit in die Hose gehen. Afaik macht das z.B. Quake 3 an einigen Stellen und lauft entsprechend nicht mehr auf modernen Systemen, wenn man den Speicherschutz an hat.

    Wozu soll das gut sein? Fünf Byte Code einsparen?

    Es ist schneller als die Variablenvariante von CStoll.

    Nur, wenn der Compiler beschissen ist.



  • SeppJ schrieb:

    Alternierender Code schrieb:

    Es ist schneller als die Variablenvariante von CStoll.

    Sicher?

    Ganz sicher?

    Getestet sicher?

    Auch wenn ich nachgucke?

    Ja, ganz sicher und kann ich auch beweisen.

    Denn aus einem i++ oder i-- wird in Assembler z.B. ein
    inc EAX
    oder
    dec EAX

    Also nur 1 Befehl.

    Aus einem variable += andere_variable wird aber eher so etwas:
    MOV EBX EAX // variable in EBX sichern
    MOV EAX andere_variable // wert von andere Variable holen
    ADD EAX EBX // Werte addieren.

    oder
    MOV EBX EAX // variable in EBX sichern
    MOV EAX andere_variable // wert von andere Variable holen
    ADD EAX EBX // Werte addieren.
    SUB EAX EBX

    In einer Schleife sollte das deutlich sichtbar werden und ein Compiler kann nicht zur Laufzeit optimieren.



  • Alternierender Code schrieb:

    oder
    MOV EBX EAX // variable in EBX sichern
    MOV EAX andere_variable // wert von andere Variable holen
    ADD EAX EBX // Werte addieren.
    SUB EAX EBX

    Korrektur, bei -= ohne das ADD.

    MOV EBX andere_variable // wert von andere Variable holen
    SUB EAX EBX // subtrahieren



  • Wutz schrieb:

    Bietet C von Haus aus die Möglichkeit zur Lautzeit ändernder Code zu nutzen?

    Nein.
    Obwohl ich glaube, dass du dich nicht richtig ausgedrückt hast.
    Im Beispiel ist der Code für "i++" und "i--" definiert, d.h. du brauchst keinen "zur Lautzeit ändernder Code", du brauchst ihn nur zur Laufzeit entsprechend aufrufen, z.B. über Funktionszeiger.

    Ja, schon, aber Funktionsaufrufe sind teuer. Was hätte ich da gewonnen?
    Und die while Schleife mehrfach für beide Varianten im Speicher zu halten, braucht mehr Speicherplatz. Gut bei diesem Beispiel nicht die Welt, aber könnte ja sein, das das mal wichtig wird. Z.B. auf µC.



  • Alternierender Code schrieb:

    In einer Schleife sollte das deutlich sichtbar werden und ein Compiler kann nicht zur Laufzeit optimieren.

    Also gerade in einer Schleife dürfte ein Compiler das recht gut optimieren können, indem er die beiden beteiligten Variablen dauerhaft in Register packt und dann nur noch add aufruft.



  • Alternierender Code schrieb:

    Alternierender Code schrieb:

    oder
    MOV EBX EAX // variable in EBX sichern
    MOV EAX andere_variable // wert von andere Variable holen
    ADD EAX EBX // Werte addieren.
    SUB EAX EBX

    Korrektur, bei -= ohne das ADD.

    MOV EBX andere_variable // wert von andere Variable holen
    SUB EAX EBX // subtrahieren

    Wenn es auf Performance ankommt, macht man folgenden:

    if (x > 0)
      goto positiv;
    else
      goto negativ;
    
    positiv:
    while (true)
    {
      XXXX // <- i--
    }
    
    negativ:
    while (true)
    {
      XXXX // <- i++
    }
    

    oder:

    switch (x > 0) {
    case 1:
      while (true)
      {
        XXXX // <- i--
      }
      break;
    default:
      while (true)
      {
        XXXX // <- i++
      }
      break;
    }
    

    Selbstmodifizierender Code bringt keine Vorteile. Ein aktueller Rechner ist voller Mechanismen, die genau selbstmodifizierenden Code verhindern sollen, zum Beispiel mit NX bit.



  • CStoll schrieb:

    Also gerade in einer Schleife dürfte ein Compiler das recht gut optimieren können, indem er die beiden beteiligten Variablen dauerhaft in Register packt und dann nur noch add aufruft.

    Und damit ein Register verbrät das dann für andere Optimierungen fehlt.


Log in to reply