The "C is Efficient" Language Fallacy



  • +fricky schrieb:

    einen grossteil der optimierung macht die java-VM zur laufzeit, nicht der compiler. damit hat Java natürlich sehr viel mehr optimiermöglichkeiten, als programmiersprachen, deren compiler starren maschinencode für bestimmte CPUs erzeugen müssen. http://en.wikipedia.org/wiki/Java_performance#Adaptive_optimization
    🙂

    Das hat doch mit dem Thema nichts zu tun. Es sei denn, du meinst, dass der Jitter zur Laufzeit das Aliasing analysiert und das will ich doch mal schwer bezweifeln.

    Dravere schrieb:

    Bei Java sieht dies allerdings etwas anders aus:

    public void foo(int lhs, int rhs, int val)
    {
      lhs += val;
      rhs += val;
    }
    

    val wird hier unverändert bleiben, egal wie man die Funktion aufruft.

    Das Bespiel verhält sich in C++ exakt wie in Java, da wie schon gesagt int (und selbst Integer - Ausnahmen ahoi!) nicht als Referenzen behandelt werden.

    So, und jetzt mal Aliasing in Java:

    class test{
    
      class X{
        public X(int x){
          this.x = x;
        }
        public int x;
      }
    
      public void foo(X lhs, X rhs, X val){
        lhs.x += val.x;
        rhs.x += val.x;
      }
    
      public test(){
        X lhs = new X(1);
        X rhs = new X(2);
        X val = new X(3);    
    
        foo(lhs, lhs, val);
    
        System.out.println(lhs.x); // 7
      }
    
      public static void main(String args[]){
        test t = new test();
      }
    }
    

    Der Compiler kann nicht wissen, ob hinter den Referenzen die gleichen Objekte stecken.



  • maximAL schrieb:

    Es sei denn, du meinst, dass der Jitter zur Laufzeit das Aliasing analysiert und das will ich doch mal schwer bezweifeln.

    so ähnlich. warum hältst du das für so unwahrscheinlich?

    maximAL schrieb:

    ...
        foo(lhs, lhs, val);
        System.out.println(lhs.x); // 7
    ...
    

    ...
    foo(lhs, rhs, val);
    System.out.println(lhs.x); // 4
    ...
    🙂



  • +fricky schrieb:

    so ähnlich. warum hältst du das für so unwahrscheinlich?

    Weil sich die Referenzen im Programmablauf ständig ändern können und man das Aliasing ständig prüfen müsste, was beim Interpretieren mehr kostet als es bringt und einen Jitter ad absurdum führt (denn kompilieren ist teuer).

    +fricky schrieb:

    foo(lhs, rhs, val);
    System.out.println(lhs.x); // 4

    Ja. Ich wollte damit was zum Ausdruck bringen.



  • maximAL schrieb:

    +fricky schrieb:

    so ähnlich. warum hältst du das für so unwahrscheinlich?

    Weil sich die Referenzen im Programmablauf ständig ändern können und man das Aliasing ständig prüfen müsste, was beim Interpretieren mehr kostet als es bringt und einen Jitter ad absurdum führt (denn kompilieren ist teuer).

    versteh ich nicht. wenn jemand sowas programmiert, dass man über zwei verschiedene referenzen ein objekt verändern kann, dann ist das entweder absicht, oder er hat 'nen fehler gemacht. da muss nix ständig geprüft werden.

    maximAL schrieb:

    +fricky schrieb:

    foo(lhs, rhs, val);
    System.out.println(lhs.x); // 4

    Ja. Ich wollte damit was zum Ausdruck bringen.

    dass man sich vertippen kann?
    🙂



  • Shade Of Mine schrieb:

    asc schrieb:

    Das mag sein, aber für den Anwender erscheint es bei Objekten anders (Wie es konkret umgesetzt wird, interessiert Viele nicht, sofern sie das Ergebnis verstehen).

    ...Das ist Call By Value. Und das ist kein verstecktes detail.

    Wie du vielleicht gelesen hatte, habe ich explizit von Objekten gesprochen. Nehmen wir folgendes Beispiel aus einem Buch:

    import java.awt.*; 
    
    public class InitPoint 
    { 
      static void clear( Point p ) 
      { 
        p.setLocation( 0, 0 ); 
      } 
    
      public static void main( String[] args ) 
      { 
        Point q = new Point( 47, 11 );   // Coordinates (x=47,y=11) 
        clear( q ); 
        System.out.println( q.x );   // 0 
      } 
    }
    

    Rein Optisch sieht es bei der Methode clear für mich wie eine Wertübergabe unter C++ aus. Dennoch kann man Änderungen wie bei einer C++ Referenz machen. Tatsächlich ist es aber eher ein Zeiger der kopiert wird, so das ein new an der Stelle tatsächlich keine Außenwirkung hat.

    Ich persönlich finde das Verhalten (sowohl unter Java als auch C#) verwirrend. Ich weiß nicht mehr ob man in Java eigene Werttypen (was in C# definitiv geht) definieren kann, sehe es aber als Problematisch an, wenn man für einen Parameter erst bei der Betrachtung des Typen weiß, ob eine Änderung an diesem das Original ändert oder nicht.

    Und daher sehe ich hier tatsächlich ein verstecktes Detail.



  • dass man sich vertippen kann?

    Dass hier nicht optimiert werden kann.



  • JustAnotherNoob schrieb:

    dass man sich vertippen kann?

    Dass hier nicht optimiert werden kann.

    das sagst du so. allein schon der compiler könnte z.b. die methode inlinen, dabei feststellen, dass lhs und rhs gleich sind (bei dem tippfehler-beispiel) und einen davon von vorn herein weglassen. optimierungen zur laufzeit sind dazu noch nicht mal nötig.
    🙂



  • das sagst du so. allein schon der compiler könnte z.b. die methode inlinen, dabei feststellen, dass lhs und rhs gleich sind (bei dem tippfehler-beispiel) und einen davon von vorn herein weglassen. optimierungen zur laufzeit sind dazu noch nicht mal nötig.

    Ich ergänze: dass hier nichts optimiert werden kann, was es nicht in C++ auch kann.



  • Shade Of Mine schrieb:

    Java hat sogar nur Call By Value - ein Call By Reference gibt es garnicht 🙄

    Genau das sagte ich doch...

    asc schrieb:

    Rein Optisch sieht es bei der Methode clear für mich wie eine Wertübergabe unter C++ aus. Dennoch kann man Änderungen wie bei einer C++ Referenz machen. Tatsächlich ist es aber eher ein Zeiger der kopiert wird, so das ein new an der Stelle tatsächlich keine Außenwirkung hat.

    Richtig. Trotzdem ein lupenreines call-by-value Beispiel.

    Ich persönlich finde das Verhalten (sowohl unter Java als auch C#) verwirrend. Ich weiß nicht mehr ob man in Java eigene Werttypen (was in C# definitiv geht) definieren kann, sehe es aber als Problematisch an, wenn man für einen Parameter erst bei der Betrachtung des Typen weiß, ob eine Änderung an diesem das Original ändert oder nicht.

    In Java ist das sauber gelöst. Von Primitiven abgesehen (die eh immutable sind) , gibt es keine Wertobjekte.
    In C# ist das anders: http://www.geocities.com/csharpfaq/structs.html



  • asc schrieb:

    Rein Optisch sieht es bei der Methode clear für mich wie eine Wertübergabe unter C++ aus. Dennoch kann man Änderungen wie bei einer C++ Referenz machen.

    du hast zuviel in C programmiert. vielleicht hilfts dir, wenn du dir vorstellst, dass java-referenzen sowas wie pointer sind.

    JustAnotherNoob schrieb:

    das sagst du so. allein schon der compiler könnte z.b. die methode inlinen, dabei feststellen, dass lhs und rhs gleich sind (bei dem tippfehler-beispiel) und einen davon von vorn herein weglassen. optimierungen zur laufzeit sind dazu noch nicht mal nötig.

    Ich ergänze: dass hier nichts optimiert werden kann, was es nicht in C++ auch kann.

    rischtich, allerdings kann die Java-VN noch zur laufzeit optimieren (und tut dies oft auch).
    🙂



  • +fricky schrieb:

    asc schrieb:

    Rein Optisch sieht es bei der Methode clear für mich wie eine Wertübergabe unter C++ aus. Dennoch kann man Änderungen wie bei einer C++ Referenz machen.

    du hast zuviel in C programmiert. vielleicht hilfts dir, wenn du dir vorstellst, dass java-referenzen sowas wie pointer sind.

    JustAnotherNoob schrieb:

    das sagst du so. allein schon der compiler könnte z.b. die methode inlinen, dabei feststellen, dass lhs und rhs gleich sind (bei dem tippfehler-beispiel) und einen davon von vorn herein weglassen. optimierungen zur laufzeit sind dazu noch nicht mal nötig.

    Ich ergänze: dass hier nichts optimiert werden kann, was es nicht in C++ auch kann.

    rischtich, allerdings kann die Java-VN noch zur laufzeit optimieren (und tut dies oft auch).
    🙂

    Das kann ich mit dem VC9 doch so ähnlich auch, da kompilier ich die Anwendung dann führ ich sie aus und anschließend kompilier ich sie erneut, nur dieses mal hat der Kompiler zusätzliche Informationen über das Laufzeitverhalten 🙂

    Die Kritik an den Java Referenzen ist doch unberechtigt, denn weder in C noch in Java kann ich sagen was foo( eineVariable ); tut, abgesehen davon, dass eineVariable nicht geändert wird. Aber wenn eineVariable in C ein Zeiger und in Java ein Objekt (bzw. eine Objektreferenz) ist, dann weiß ich erst nachdem ich nachgeschaut habe was eineVariable ist ob das refenzierte Objekt (möglicherweise) geändert wird.



  • C++-Programmierender schrieb:

    Das kann ich mit dem VC9 doch so ähnlich auch, da kompilier ich die Anwendung dann führ ich sie aus und anschließend kompilier ich sie erneut, nur dieses mal hat der Kompiler zusätzliche Informationen über das Laufzeitverhalten

    klar, das musste nur irgendwie automatisieren, also quelltext, compiler, linker und analysesystem irgendwie zusammenbringen, so dass der code in abhängigkeit von der umgebung immer wieder neu compiliert wird. 'closed world assumptions' würde prof84 jetzt buzzworden.
    🙂



  • asc schrieb:

    Wie du vielleicht gelesen hatte, habe ich explizit von Objekten gesprochen.

    Dann ist fuer dich also ein

    void strcpy(char* trg, char* src) {
      while(*trg++=*src++);
    }
    

    ebenfalls ein call by reference?

    Ne ne ne.
    Du kannst nicht einfach die Sachen definieren wie du willst. Java kennt nur call by value. Weil Objekte eben referenzen sind. Das ist relevant zu wissen. Natuerlich kannst du die objekte die referenziert werden aendern, aber eben nicht die referenzen selber.

    denk einfach mal an:

    public class InitPoint 
    { 
      static void clear( Point p ) 
      { 
        p=new Point( 0, 0 ); 
      } 
    
      public static void main( String[] args ) 
      { 
        Point q = new Point( 47, 11 );   // Coordinates (x=47,y=11) 
        clear( q ); 
        System.out.println( q.x );   // nicht 0 
      } 
    }
    

    Call by reference wuerde hier q auf (0,0) setzen. Aber es ist call by value, ergo passiert garnix. Das ist sehr relevant zu wissen, da du zB keine delete() funktion schreiben kannst die eine referenz auf null setzt.

    es ist also kein verstecktes detail sondern eine klare und einfache design entscheidung gewesen. alles wird by value uebergeben.



  • +fricky schrieb:

    das sagst du so. allein schon der compiler könnte z.b. die methode inlinen, dabei feststellen, dass lhs und rhs gleich sind

    Das kann meistens eben nicht zur Kompilierzeit festgestellt werden.

    +fricky schrieb:

    (bei dem tippfehler-beispiel)

    ...ich geb dir gleich Tippfehler...



  • maximAL schrieb:

    +fricky schrieb:

    das sagst du so. allein schon der compiler könnte z.b. die methode inlinen, dabei feststellen, dass lhs und rhs gleich sind

    Das kann meistens eben nicht zur Kompilierzeit festgestellt werden.

    +fricky schrieb:

    (bei dem tippfehler-beispiel)

    ...ich geb dir gleich Tippfehler...

    Du weißt doch es genügt, wenn der Kompiler Code erzeugt der in 99% der Fälle korrekt ist.



  • maximAL schrieb:

    +fricky schrieb:

    das sagst du so. allein schon der compiler könnte z.b. die methode inlinen, dabei feststellen, dass lhs und rhs gleich sind

    Das kann meistens eben nicht zur Kompilierzeit festgestellt werden.

    das ist sehr einfach festzustellen. du übergibst nämlich zwei mal das gleiche objekt. selbst meine Java-IDE (intellij-idea) hat bei deinem beispiel gemeckert, dass hier was nicht stimmen kann.
    🙂



  • void foo(int* lhs, int* rhs, int* val)
    {
      *lhs += *val;
      *rhs += *val;
    }
    

    OMG, mein Compiler kann diese Zeilen nicht optimieren. Parallelisierung ist nie einfach und die eigentliche Performance holt man nicht aus der Microoptimierung.

    adaptive optimization may take advantage

    Irgendwie stoert mich der Konjunktiv. Was ist denn nun wirklich?



  • knivil schrieb:

    void foo(int* lhs, int* rhs, int* val)
    {
      *lhs += *val;
      *rhs += *val;
    }
    

    OMG, mein Compiler kann diese Zeilen nicht optimieren.

    schreib mal 'restrict' vor die pointer (dein compiler muss aber C99 kennen, z.b. GCC). vielleicht tut er's ja dann.
    🙂



  • knivil schrieb:

    void foo(int* lhs, int* rhs, int* val)
    {
      *lhs += *val;
      *rhs += *val;
    }
    

    OMG, mein Compiler kann diese Zeilen nicht optimieren. Parallelisierung ist nie einfach und die eigentliche Performance holt man nicht aus der Microoptimierung.

    Das Problem ist, dass der Compiler quasi nichts parallelisieren kann. Das ist ein relevantes Problem.



  • schreib mal 'restrict' vor die pointer (dein compiler muss aber C99 kennen, z.b. GCC). vielleicht tut er's ja dann.

    bist du Reporter? 🤡


Anmelden zum Antworten