Was ist für euch guter Programmcode?



  • Gregor@Home schrieb:

    Das ist Code, bei dem ich mir eigentlich keine besonders große Mühe gemacht habe. Ich halte es also nicht für besonders guten Code. Es fehlen auch Kommentare usw.. Ich bin mal gespannt, was da für Verbesserungsvorschläge kommen, falls dazu noch einer Lust hat.

    Naja, um es nochmal anders auszudrücken: Ich habe da einen Algorithmus implementiert, der IMHO nicht so ganz leicht zu implementieren ist. Es war schwer für mich, den Code überhaupt zu produzieren, so dass ich nicht so auf "guten Stil" geachtet habe. Das ist sicherlich eine Frage der Erfahrung, aber mich interessiert, ob es euch bei schwierigen Algorithmen ähnlich geht:

    Wird bei euch der Stil bei schwierigen Programmieraufgaben schlechter oder produziert ihr unabhängig von der Komplexität der Aufgabe immer gleichbleibend guten Code?



  • Gregor@Home schrieb:

    Wird bei euch der Stil bei schwierigen Programmieraufgaben schlechter oder produziert ihr unabhängig von der Komplexität der Aufgabe immer gleichbleibend guten Code?

    der wird zunächst signifikant schlechter. auch mal schlecht, daß ich den überblick verliere und löschen muss. hab gerade wieder tagelang so ne phase gehabt. jetzt geht's langsam wieder.



  • volkard schrieb:

    der wird zunächst signifikant schlechter. auch mal schlecht, daß ich den überblick verliere und löschen muss. hab gerade wieder tagelang so ne phase gehabt. jetzt geht's langsam wieder.

    Führst du dann Refactorings durch, bis die Qualität des Codes der deines restlichen Codes entspricht?



  • Optimizer schrieb:

    IMHO: Viel zu lange Funktionen mit zu vielen lokalen Variablen. Da sollte man versuchen, besser aufzuteilen. Zwing mich jetzt aber nicht, darüber nachzudenken, wo. 🤡

    😃

    Ich habe mal angefangen, da ein bischen was aufzuteilen, aber irgendwie habe ich das Gefühl, dass das so nicht wirklich etwas wird. Ich habe plötzlich Methoden, die unglaublich viele Parameter brauchen und die Länge der Klasse hat sich auch mal eben um 30 Zeilen erhöht.

    da ist der Code:

    /*
     * LineCaster.java
     *
     * Created on 8. März 2005, 01:25
     */
    
    package jaradap.model.algorithm;
    
    import jaradap.model.algorithm.multiPointProcessor.RaycastProcessor;
    import jaradap.model.data.BasicRaster;
    import jaradap.model.data.StandardRaster;
    import math.linearAlgebra.Vector;
    
    /**
     *
     * @author Gregor
     */
    public class LineCaster
    {
       private final RaycastProcessor pointProcessor;
       private final StandardRaster raster;
       private final int dimension;
    
       /** Creates a new instance of RayCaster */
       public LineCaster(final RaycastProcessor pointProcessor, final StandardRaster raster)
       {
          this.pointProcessor = pointProcessor;
          this.raster = raster;
          dimension = raster.getDimensions();
       }
    
       public float castLine(final Vector startPoint, final Vector endPoint)
       {
          checkCastLineParameters(startPoint, endPoint);
          final int[][] line = clipLine(startPoint, endPoint);
          if (line == null) return 0.0f;
          final int[] intDifferences = new int[dimension];
          final int[] relativePosition = new int[dimension];
          final int[] position = new int[dimension];
          final int[] increments = new int[dimension];
          final int maxDiffIndex = initHelperVariables(intDifferences, relativePosition, position, 
                                                       increments, line);
          final int maxDifference = intDifferences[maxDiffIndex];
          final int maxIncrement = increments[maxDiffIndex];
          pointProcessor.reset();
          processLine(pointProcessor, intDifferences, relativePosition, position, increments, 
                      maxDiffIndex, maxDifference, maxIncrement, line);
          return pointProcessor.getValue();
       }
    
       private final void checkCastLineParameters(final Vector startPoint, final Vector endPoint)
       {
          if (startPoint.getDimension() != dimension) 
             throw new IllegalArgumentException("startPoint has wrong dimension.");
          if (endPoint.getDimension() != dimension) 
             throw new IllegalArgumentException("endPoint has wrong dimension.");
       }
    
       private final int[][] clipLine(final Vector startPoint, final Vector endPoint)
       {
          final int[][] line = new int[2][dimension];
          if(!setFirstPointInRaster(line[0],startPoint,endPoint)) return null;
          if(!setFirstPointInRaster(line[1],endPoint,startPoint)) return null;
          return line;
       }
    
       private final int initHelperVariables(final int[] intDifferences, final int[] relativePosition,
                                             final int[] position, final int[] increments,
                                             final int[][] line)
       {
          int maxDiffIndex = 0;
          for(int i = 0 ; i < dimension ; ++i)
          {
             intDifferences[i] = line[1][i] - line[0][i];
             increments[i] = (intDifferences[i] > 0) ? 1 : -1;
             intDifferences[i] = Math.abs(intDifferences[i]);
             if (intDifferences[i] > intDifferences[maxDiffIndex]) maxDiffIndex = i;
             relativePosition[i] = intDifferences[i] >> 1;
             position[i] = line[0][i];
          }
          return maxDiffIndex;
       }
    
       private final void processLine(final RaycastProcessor pointProcessor,final int[] intDifferences, 
                                      final int[] relativePosition, final int[] position, 
                                      final int[] increments, final int maxDiffIndex, 
                                      final int maxDifference, final int maxIncrement,
                                      final int[][] line)
       {
          for(;position[maxDiffIndex] != line[1][maxDiffIndex] ; position[maxDiffIndex] += maxIncrement)
          {
             if (pointProcessor.addPoint(raster,position)) break;
             for(int i = 0 ; i < dimension ; ++i)
             {
                if (i == maxDiffIndex) continue;
                relativePosition[i] += intDifferences[i];
                if (relativePosition[i] < maxDifference) continue;
                position[i] += increments[i];
                relativePosition[i] -= maxDifference;
             }
          }
          pointProcessor.addPoint(raster,position);
       }
    
       private final boolean setFirstPointInRaster(final int[] target, final Vector firstLinePoint, 
                                                   final Vector lastLinePoint)
       {
          final double[] differences = new double[dimension];
          double fraction = 0.0;
          for (int i = 0 ; i < dimension ; ++i)
          {
             differences[i] = (float)(lastLinePoint.getComponent(i) - firstLinePoint.getComponent(i));
          }
          for (int i = 0 ; i < dimension ; ++i)
          {
             double newFraction = 0.0;
             final double component = firstLinePoint.getComponent(i);
             final double difference = differences[i];
             final double maxValue = raster.getSize(i) - 1;
             if (component < 0.0)
             {
                if (difference <= 0.0) return false;
                newFraction = component / difference;
             }
             else if (component > maxValue)
             {
                if (difference >= 0.0) return false;
                newFraction = (component - maxValue) / difference;
             }
             if (newFraction < fraction) fraction = newFraction;
          }
          for (int i = 0 ; i < dimension ; ++i)
          {
             target[i] = Math.round((float)(firstLinePoint.getComponent(i) - fraction * differences[i]));
             if (target[i] < 0) return false;
             if (target[i] > raster.getSize(i) - 1) return false;
          }
          return true;
       }
    }
    

    Findest du, der Code hat sich verbessert?

    Man könnte das jetzt natürlich noch weiter treiben und zum Beispiel eine neue Klasse einführen, in der die ganzen Hilfsvariablen Member sind. So könnte man sicherlich die vielen Parameter bei den Methodenaufrufen verhindern. Aber letztendlich landet man dann vermutlich irgendwann bei einem Monster mit 1000 Zeilen, wo es auch 100 getan hätten. Zudem dürfte der Code durch solche "Verbesserungen" immer träger/langsamer werden. Ich glaube nicht, dass dieser Weg der Beste ist. Fragt sich nur, wie man den Code ansonsten verbessern sollte, denn der Ausgangscode ist ja immer noch nicht schöner geworden.

    Oder habe ich mich bei der Aufteilung der Methoden einfach nur sehr blöd angestellt? Geht das vielleicht deutlich besser?

    (Ätsch, jetzt mußt du doch denken. 😉 😃 )



  • Irgendwie fehlt dem armen Side ein Kommentar wo was steht bzgl. was LineCaster sein soll 😞 Überhaupt sehen alle Kommentare ziemmlich auto-eclipse-created aus.

    MfG SideWinder



  • SideWinder schrieb:

    Irgendwie fehlt dem armen Side ein Kommentar wo was steht bzgl. was LineCaster sein soll 😞 Überhaupt sehen alle Kommentare ziemmlich auto-eclipse-created aus.

    Ja, das ist sicherlich ein treffender Punkt. Vor Kommentaren habe ich mich in dieser Klasse bisher etwas gedrückt, dabei würden die sicherlich helfen. Vielleicht kann man damit auch innerhalb von Methoden verschiedene Bereiche markieren und kann sich so eine Aufteilung der Methoden sparen.

    Was die Klasse prinzipiell macht, habe ich etwas weiter oben geschrieben:

    Der Code ist letztendlich eine Realisierung eines n-dimensionalen Bresenham-Linienalgorithmus inklusive einer Clipping-Funktion, die dem Liang-Barsky Algorithmus nahe kommt. Es wird allerdings keine Linie gemalt, sondern "analysiert".



  • Ich habe mal mein jämmerliches Englisch aktiviert, um die Klasse halbwegs zu kommentieren. Was sagt ihr dazu:

    /*
     * LineCaster.java
     *
     * Created on 8. März 2005, 01:25
     */
    
    package jaradap.model.algorithm;
    
    import jaradap.model.algorithm.multiPointProcessor.RaycastProcessor;
    import jaradap.model.data.BasicRaster;
    import jaradap.model.data.StandardRaster;
    import math.linearAlgebra.Vector;
    
    /**
     * This class represents Objects that can analyze lines in a given raster. The analysis
     * projects a line into a single float which is given by a RaycastProcessor, the raster and the line. 
     *
     * @author Gregor
     */
    public class LineCaster
    {
       private final RaycastProcessor pointProcessor;
       private final StandardRaster raster;
       private final int dimension;
    
       /** 
        * Creates a new LineCaster with the given parameters.
        *
        * @param pointProcessor This is the RaycastProcessor that will be used to analyze the lines 
        *                       in the method castLine.
        * @param raster This is the raster which represents the environment in that this LineCaster
        *               may analyze lines.
        */
       public LineCaster(final RaycastProcessor pointProcessor, final StandardRaster raster)
       {
          this.pointProcessor = pointProcessor;
          this.raster = raster;
          dimension = raster.getDimensions();
       }
    
       /**
        * This method analyzes the line which is specified by the parameters. The pointProcessor
        * of this LineCaster is used to compute the resulting value, while the raster of this LineCaster
        * is the environment of the analysis.
        *
        * @param startPoint This is the start point of the line.
        * @param endPoint This is the end point of the line.
        * @return The value of the pointProcessor after processing all points of the line inside the raster
        *         will be returned. If the line has no points inside the raster 0 will be returned.
        */
       public float castLine(final Vector startPoint, final Vector endPoint)
       {
          // Check parameters.
          if (startPoint.getDimension() != dimension)
             throw new IllegalArgumentException("startPoint has wrong dimension.");
          if (endPoint.getDimension() != dimension)
             throw new IllegalArgumentException("endPoint has wrong dimension.");
    
          // Clip line: 
          // Resulting line is given by rasterStartPoint and rasterEndPoint.
          final int[] rasterStartPoint = new int[dimension];
          final int[] rasterEndPoint = new int[dimension];
          if(!setFirstPointInRaster(rasterStartPoint,startPoint,endPoint)) return 0.0f;
          if(!setFirstPointInRaster(rasterEndPoint,endPoint,startPoint)) return 0.0f;
    
          // Init helper variables.
          int maxDiffIndex = 0;
          final int[] intDifferences = new int[dimension];
          final int[] relativePosition = new int[dimension];
          final int[] position = new int[dimension];
          final int[] increments = new int[dimension];
          for(int i = 0 ; i < dimension ; ++i)
          {
             intDifferences[i] = rasterEndPoint[i] - rasterStartPoint[i];
             increments[i] = (intDifferences[i] > 0) ? 1 : -1;
             intDifferences[i] = Math.abs(intDifferences[i]);
             if (intDifferences[i] > intDifferences[maxDiffIndex]) maxDiffIndex = i;
             relativePosition[i] = intDifferences[i] >> 1;
             position[i] = rasterStartPoint[i];
          }
          final int maxDifference = intDifferences[maxDiffIndex];
          final int maxIncrement = increments[maxDiffIndex];
    
          // Process line: 
          // 1. Reset pointProcessor.
          // 2. Process Bresenham-Line-Algorithm
          // 3. Return value of pointProcessor
          pointProcessor.reset();
          for(;position[maxDiffIndex] != rasterEndPoint[maxDiffIndex] ; position[maxDiffIndex] += maxIncrement)
          {
             if (pointProcessor.addPoint(raster,position)) break;
             for(int i = 0 ; i < dimension ; ++i)
             {
                if (i == maxDiffIndex) continue;
                relativePosition[i] += intDifferences[i];
                if (relativePosition[i] < maxDifference) continue;
                position[i] += increments[i];
                relativePosition[i] -= maxDifference;
             }
          }
          pointProcessor.addPoint(raster,position);
          return pointProcessor.getValue();
       }
    
       private final boolean setFirstPointInRaster(final int[] target, final Vector firstLinePoint,
                                                   final Vector lastLinePoint)
       {
          // Compute the differences of the line's start- and endpoint.
          final double[] differences = new double[dimension];
          double fraction = 0.0;
          for (int i = 0 ; i < dimension ; ++i)
          {
             differences[i] = (float)(lastLinePoint.getComponent(i) - firstLinePoint.getComponent(i));
          }
    
          // Compute the fraction that represents the point where the line enters the raster.
          for (int i = 0 ; i < dimension ; ++i)
          {
             double newFraction = 0.0;
             final double component = firstLinePoint.getComponent(i);
             final double difference = differences[i];
             final double maxValue = raster.getSize(i) - 1;
             if (component < 0.0)
             {
                if (difference <= 0.0) return false;
                newFraction = component / difference;
             }
             else if (component > maxValue)
             {
                if (difference >= 0.0) return false;
                newFraction = (component - maxValue) / difference;
             }
             if (newFraction < fraction) fraction = newFraction;
          }
    
          // Use the fraction to compute the first point of the line that lies within the raster
          // and check if the point really lies within the raster.
          // The point may not lie within the raster if the line has no point in the raster.
          for (int i = 0 ; i < dimension ; ++i)
          {
             target[i] = Math.round((float)(firstLinePoint.getComponent(i) - fraction * differences[i]));
             if (target[i] < 0) return false;
             if (target[i] > raster.getSize(i) - 1) return false;
          }
          return true;
       }
    }
    


  • Gregor@Home schrieb:

    volkard schrieb:

    der wird zunächst signifikant schlechter. auch mal schlecht, daß ich den überblick verliere und löschen muss. hab gerade wieder tagelang so ne phase gehabt. jetzt geht's langsam wieder.

    Führst du dann Refactorings durch, bis die Qualität des Codes der deines restlichen Codes entspricht?

    eigentlich nicht. oder nicht absichtlich. aber wenn ich mich im code nicht mehr zurechtfinde, wenn ich einfach nix neues mehr dazubauen kann, dann muß ich löschen.
    und ansonsten sind die klassen halt möglichst mit nur einem zweck betraut, und ich kann im prinzip ne häßliche implementierung durch ne hübsche austauschen und ne schnittstellenänderung dauert auch nicht soo lange, der compiler sag ja, wo die aufrufer probleme kriegen.
    zur zeit lösche ich auch mal frühzeitig, wenn ich sehe, daß es häßlich, aber noch nicht grausam, werden wird. aber ich kriege keine kohle dafür.
    ich hab dieses jahr dreimal private erblichkeit gebaut. jedemals sah ich das und dachte mir "irgendwie ist das nicht hübsch", aber egal, es muß ja auch mal weitergehen.
    in allen drei fällen ist der code umgekippt und sehr schlimm geworden. im dritten fall sogar so schlimm, daß ich auch mit angestrengtem nachdenken nicht gerausgefunden habe, warum er funktionierte (er funktionierte!).
    das wird meinen stil beeinflussen und ich werde in zukunft auch für kohle frühzeitiger löschen. die zeit, die man wegen häßlichem code verliert, ist einfach zu groß irgendwie.



  • // Compute the differences of the line's start- and endpoint. 
          final double[] differences = new double[dimension]; 
          for (int i = 0 ; i < dimension ; ++i) 
          { 
             differences[i] = (float)(lastLinePoint.getComponent(i) - firstLinePoint.getComponent(i)); 
          }
    

    was ist das?

    zweck: Compute the differences of the line's start- and endpoint.
    name: computeDiffrerences
    argumente: zwei arrays a und b
    rückgabe: ein array c mit c[i]=a[i]-b[i] für jedes i

    wenn du c++ machen würdest, müßteste du den ganzen kram in so kleine einheiten zerhacken. und dann wieder die kommetare sparen, also
    aus

    // Compute the differences of the line's start- and endpoint. 
          final double[] differences = new double[dimension]; 
          for (int i = 0 ; i < dimension ; ++i) 
          { 
             differences[i] = (float)(lastLinePoint.getComponent(i) - firstLinePoint.getComponent(i)); 
          }
    

    wird

    // Compute the differences of the line's start- and endpoint. 
    final double[] differences=computeDifferences(lasLinePoint,firstLinePoint);
    

    das macht den code zwar nicht sofort kürzer, aber ungemein viel übersichtlicher.

    dann würde man es vielleicht löschen und sich sagen, daß man ja eigentlich mit mehrdomensionalen vektoren rechnet, dann sollte man für so einen wesentlichen begriff auch einen namen haben und ne klasse bauen. wäre für java vielleicht erst recht gut, da kannste computeDifferences ja schlecht in den global namespace stecken. und in deiner klasse hat's eigentlich kaum was zu suchen.



  • Gregor@Home schrieb:

    Optimizer schrieb:

    IMHO: Viel zu lange Funktionen mit zu vielen lokalen Variablen. Da sollte man versuchen, besser aufzuteilen. Zwing mich jetzt aber nicht, darüber nachzudenken, wo. 🤡

    😃

    Ich habe mal angefangen, da ein bischen was aufzuteilen, aber irgendwie habe ich das Gefühl, dass das so nicht wirklich etwas wird. Ich habe plötzlich Methoden, die unglaublich viele Parameter brauchen und die Länge der Klasse hat sich auch mal eben um 30 Zeilen erhöht.

    Entscheidend ist, wieviele zu wartende Zeilen du hast. Du musst eine Zeile mit einer einzigen geschweiften Klammer nicht warten und darfst sie deshalb nicht dazuzählen. Ich weiß nicht, wer das eingeführt hat, aber die Anzahl LOC sind kein Maß für gar nichts. Ich finde es jetzt schöner anzusehen, das kann man aber bestimmt noch weiter zerhacken. castLine() ist noch viel zu lange, volkard hat auch ein schönes Beispiel gebracht. Ich mache, wenn ich die Muße habe, lauter maximal 10-Zeiler, das ist enorm übersichtlich, weil der halbe Code nur noch aus Funktionsaufrufen besteht und damit fast wie ein englischer Text gelesen werden kann.
    Dass das Programm deswegen langsamer wird, glaube ich nicht, so schrottig sind die JIT-Compiler heute eigentlich nicht mehr.

    Eine Funktion, die so anfängt:

    // Clip line:
          // Resulting line is given by rasterStartPoint and rasterEndPoint.
          final int[] rasterStartPoint = new int[dimension];
          final int[] rasterEndPoint = new int[dimension];
          if(!setFirstPointInRaster(rasterStartPoint,startPoint,endPoint)) return 0.0f;
          if(!setFirstPointInRaster(rasterEndPoint,endPoint,startPoint)) return 0.0f;
    
          // Init helper variables.
          int maxDiffIndex = 0;
          final int[] intDifferences = new int[dimension];
          final int[] relativePosition = new int[dimension];
          final int[] position = new int[dimension];
          final int[] increments = new int[dimension];
    

    Tut mir weh, wenn ich keinen Plan hab, was sie macht und ich sie warten muss. Wenn ich sie nur benutzen würde, wäre es mir egal.

    Außerdem iterierst du verdammt oft über alle Dimensionen, diese Dinger kann man alle auslagern in ein forAllDimensions(FunctionObject). Kann sein, dass du bei sowas viele Parameter bräuchtest. Ich kenne leider die näheren Umstände nicht so genau, aber evtl. kannst du ein paar Daten sinnvoll zu einer nested class zusammenfassen, die auch gleich ihre Methoden zum Bearbeiten ihrer Daten mitbringt.

    EDIT: Offtopic: @volkard: Bei deinem Kurs ist diese Seite kaputt.



  • die zeit, die man wegen häßlichem code verliert, ist einfach zu groß irgendwie.

    👍 außerdem macht code schreiben mehr Spaß als ihn zu debuggen. Das hat vor längerer Zeit sogar mein Chef kapiert: Als das Implementieren von neuer Funktionalität nach dem Redesign plötlich 5 mal so schnell ging wie vorher hat er eingesehen, das sich die Kosten für 3 Wochen Redesign nach kürzester Zeit amortisieren.
    Leider wird in der Praxis IMO zu spät damit angefangen, weil sich Redesign immer nach viel Arbeit ohne für den User sichtbaren Nutzen anhört....



  • Optimizer schrieb:

    Dass das Programm deswegen langsamer wird, glaube ich nicht, so schrottig sind die JIT-Compiler heute eigentlich nicht mehr.

    Dann muss ich dich jetzt vermutlich mal in die Realität zurückholen. 😃 Ich habe natürlich gemessen und alleine die simple Aufspaltung der Methoden, die ich da oben gemaht habe, hat das Programm an der entsprechenden Stelle 20-30% langsamer gemacht. Der JIT-Compiler beherrscht in der Theorie zwar lauter tolle Sachen, das heißt aber noch lange nicht, dass davon auch etwas in der Praxis ankommt.



  • Bleibt zu klären, ob es in einer realistisch umfangreichen Anwendung so zum Tragen kommt. Du kannst auch einfach schönen Code schreiben und warten, bis der JIT-Compiler den Unterschied auf 0% drückt.
    Wenn es natürlich wirklich etwas ist, was du hier und jetzt bis zum letzten optimieren und solche Kompromisse eingehen musst, gibt's nicht mehr viel zu sagen, wenn du so gemessen hast. Aber dann bist du im falschen Thread gelandet. 😉



  • Gregor@Home schrieb:

    Optimizer schrieb:

    Dass das Programm deswegen langsamer wird, glaube ich nicht, so schrottig sind die JIT-Compiler heute eigentlich nicht mehr.

    Dann muss ich dich jetzt vermutlich mal in die Realität zurückholen. 😃 Ich habe natürlich gemessen und alleine die simple Aufspaltung der Methoden, die ich da oben gemaht habe, hat das Programm an der entsprechenden Stelle 20-30% langsamer gemacht. Der JIT-Compiler beherrscht in der Theorie zwar lauter tolle Sachen, das heißt aber noch lange nicht, dass davon auch etwas in der Praxis ankommt.

    ich würde vorschlagen, du spaltest trotzdem bin ins kleinste fitzelchen. und mißt viel. das übergeben von arrays und dergleichen muß in java eigentlich sehr schnell sein. evtl bringt es was, ne eigene klasse für den n-dimensionalen vector zu machen. so mal in der hoffnung, daß java schneller auf attributen ackern kann, als auf übergebenen argumenten. und überall final rein, damit er besser inlined.
    und dann hoffe ich, daß zum schluss nur ein- oder zwei knackpunkte sind, die du entgegen dem guten stil per hand inlinest, den rest aber gefitzelt halten kannst.
    viel glück. *g*

    vielleicht bringrt es was, nen einfachen präprozessor zu bauen.



  • Optimizer schrieb:

    Bleibt zu klären, ob es in einer realistisch umfangreichen Anwendung so zum Tragen kommt. Du kannst auch einfach schönen Code schreiben und warten, bis der JIT-Compiler den Unterschied auf 0% drückt.

    so geh ich in c++ gerne vor. dauert zwar vom wunsch bis zur realisierung durch die compilerbauer 4 jahre oder mehr, aber irgendwie kommt's dann doch.



  • Optimizer schrieb:

    Bleibt zu klären, ob es in einer realistisch umfangreichen Anwendung so zum Tragen kommt. Du kannst auch einfach schönen Code schreiben und warten, bis der JIT-Compiler den Unterschied auf 0% drückt.
    Wenn es natürlich wirklich etwas ist, was du hier und jetzt bis zum letzten optimieren und solche Kompromisse eingehen musst, gibt's nicht mehr viel zu sagen, wenn du so gemessen hast. Aber dann bist du im falschen Thread gelandet. 😉

    Dann bin ich wohl im falschen Thread gelandet. 😃 Dieses Programmstück dürfte ruhig um einen Faktor 50 oder mehr schneller sein. Mein Programm würde davon enorm profitieren. Und trotzdem stellt eigentlich die erste Variante, die ich da gepostet hatte, schon einen Kompromiss dar. Ich hatte vorher schonmal die ganzen lokalen Hilfsvariablen zu Membern gemacht, was durchaus auch 20% Performance gebracht hatte, bin davon aber wieder abgekommen, weil es letztendlich einer Parallelisierung im Weg steht, die ich durchführen möchte, wenn ich mir irgendwann mal ein Mehrkernsystem zulegen sollte. Ich habe da also eigentlich schon die "Premature Optimizations" entfernt, die mich in der Wartung des Codes wirklich behindern.

    PS: Du siehst es also auch so, dass man manchmal Kompromisse bezüglich der Qualität eingehen muss? Man hört ja oft, dass ein gutes Design und ein guter Stil auch zu schnellem Code führt. Offensichtlich ist das nur eingeschränkt gültig und manchmal sind die Ziele "guter Stil" und "Performance" nicht so leicht in Einklang zu bringen.



  • volkard schrieb:

    und überall final rein, damit er besser inlined.

    Leider ist es nur ein Gerücht, dass final bezüglich der Performance hilft. Wenn man von Konstanten primitiver Typen absieht, dann hat final keinerlei Auswirkungen auf die Performance. Normalerweise ist "final" etwas, das nur mit dem Design zu tun hat und teilweise überlebt es nichtmal bis in den Bytecode (Zum Beispiel bei Methodenparametern).

    Wenn ich irgendwo eine Methode als "private final" deklariere, dann ist das "final" da eigentlich auch überflüssig. Das steckt praktisch schon im "private" drin. Der Jitter ist beim Inlining auch nicht auf das "final" angewiesen. ...naja, aber wie du schon sagst: Ich warte mal 4 Jahre, vielleicht nutzt das der Jitter oder der Bytecode-Compiler ja irgendwann mal.



  • Gregor@Home schrieb:

    Dieses Programmstück dürfte ruhig um einen Faktor 50 oder mehr schneller sein.

    Öhm, naja: "Mit einem anderen Programmstück zusammen.". Und zwar das, was sich im "pointProcessor" verbirgt.



  • Worauf wartest du genau? Mir fällt kein Szenario ein, indem final ein Hinweis für den JIT-Compiler sein könnte, eine Methode nicht virtuell aufzurufen. Er kennt alle geladenen Klassen (weiß also welche Methode bis zu welcher Ableitung redefiniert wird), den Referenztyp und weiß daher IMHO genau, ob es klar ist, welche Methode das ist, oder nicht. Könnte ein final irgendwo noch ein Mehr an Informationen bringen?

    PS: Du siehst es also auch so, dass man manchmal Kompromisse bezüglich der Qualität eingehen muss? Man hört ja oft, dass ein gutes Design und ein guter Stil auch zu schnellem Code führt. Offensichtlich ist das nur eingeschränkt gültig und manchmal sind die Ziele "guter Stil" und "Performance" nicht so leicht in Einklang zu bringen.

    Ich denke, gutes Design macht das Programm nicht von sich aus schneller, aber kann das Optimieren auf höherer Ebene vereinfachen. Ich weiß nicht, ob ich auch Kompromisse beim Design eingehen würde, bisher ist die Optimierung mit meiner bescheidenen Mathematik immer schon auf der Algorithmen-Ebene zu früh zum Stillstand gekommen. Da bringt's dann wohl auch nichts mehr, auch noch zusätzlich das Design zu zerstören, wenn noch so viel Potenzial brach liegt.



  • Gregor@Home schrieb:

    Du siehst es also auch so, dass man manchmal Kompromisse bezüglich der Qualität eingehen muss? Man hört ja oft, dass ein gutes Design und ein guter Stil auch zu schnellem Code führt. Offensichtlich ist das nur eingeschränkt gültig und manchmal sind die Ziele "guter Stil" und "Performance" nicht so leicht in Einklang zu bringen.

    ich erinnere mich an meine basic-zeit.
    auf dem zx81 (nur double-variablen und strings) schrieb man in schleifen pi statt 3 als obergrenze, weil es nur ein token war (und ein byte verbrauchte) und kein double-literal. man schreib auch "sqrt pi" statt 1, sind ja nur 2 bytes so. mit 1k ram ist code-size einfach was wichtiges.
    auf dem 64-er benutze man next für rücksprunge.
    also (mal ohne abbruchbedingung)

    7810 for i=0 to 1 step 0
    7820 print "volkard ist doof"
    7830 next
    

    statt

    7810 print "volkard ist doof"
    7820 goto 7810

    denn gote hat von anfang bis zum fund die verkettete liste der zeilen durchsucht, bis die zeilennummer gefunden wurde. also o(vorherigeZeilenAnzahl). next hingegen hat die adresse des for-befehls gespeichert, also o(1).
    dann viel qbasic. da ist nix passiert. es wurde nur immer bessew als basic v.2.
    in visual-basic lebt ein großes verbrechen bis heute. man will die anzahl der zeilen einer datei wissen. mit ner schleife ienlesen und zählen ist lahm. man liest besser die gesamte datei in einen string, macht ein replace(inhalt,chr(13),""), also ersetzt alle \n durch nix, und berechnet die differenz der größen der strings vorher und nachher.
    dann irgendwann c. aber noch c als makro-assembler. der compiler hat so toll optimiert, wie der hund meines nachbarn. also immer strcpy statt schleife, immer dies und das, was der compilerbauer liefert, statt ner simplen schleife. wüsten herumgecaste, daß es klappt.
    dann kam c++. keine änderung. bei c++ bin ich geblieben, aber die compiler sind viel besser geworden.
    bin jetzt seit +5 jahren bei nem stil, wo ich code von vor 5 jahren flüssig lesen kann. den frühen c-code von mir konnte ich nach 6 monaten nicht im geringsten lesen. hab gar nix gerafft. obwohl ich den algo genau kannte und der source nur über 3 bildschirme ging. nix.

    es ist irgendwie schlimm, wenn die performance bestimmt, *wie* du etwas hinschreiben musst. toll wäre, wenn du einfach das schreiben dürftes, was dur für am besten lesbar hälst, und der compiler macht zuverlässig optimalen code draus.

    ich ertappe mich auch immer wieder bei meinen ausflügen php (perl hat zwar das gleiche problem, aber da mogle ich nie), wie ich schönen code zu häßlichem mache, weil der häßliche signifikant schneller ist.


Anmelden zum Antworten