If / Case Switch - Weiche Performance Optimieren

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • If / Case Switch - Weiche Performance Optimieren

    Hallo,

    ich versuche gerade die Stelle mit der größten Rechenlast in meinem Code zu optimieren. Sie wird extrem oft aufgerufen da sie am Ende mehrerer Schleifen steht.
    Im Grunde wird nur ein Wert ausgewertet, von 1 bis 20. Und in jedem einzelnen Fall dann ca 2-4 Rechenoperationen ausgeführt.

    Zuerst hatte ich es als 20 x IF Abfrage gelöst, dachte mir dann jedoch das das nicht Zielführend sein kann, 20x IF Abfrage Funktion plus ca 3 Folgeoperationen,
    macht 23 Operationen pro durchlauf.

    Zweiter Ansatz war es als CASE SWITCH umzusetzen, ich dachte das bringt performance, aber offenbar ist das intern nahezu genauso Rechenintensiv,
    die Zeitersparnis liegt bei vielleicht 20%.

    Jetzt suche ich irgend einen Weg der ähnlich performant wie ein Fiktives JUMP TO währe, so wie früher in QBASIC. Das also nur EINE Operation ausgeführt
    wird die dann direkt an die eine der zwanzig Stellen springt an denen es dann weiter geht. Ende. Würde die Rechenlast von ca 23 auf 4 reduzieren, müsste
    also defakto dann etwa 5x so schnell gehen.

    ?( Hat jemand einen Ansatz oder Tipps wie ich das Umsetzen könnte?

    Mir fällt als Notlösung gerade nur ein das in Mehrere IF´s zu schachteln, also erst Wert ><10, dann jeweils wieder auf zwei 5er Werftenbereiche aufgabeln,
    usw, so käme ich mit ca 1/4 der IF Abfragen aus. Müsste aber doch sicher geschickter umsetzbar sein? :?:
  • Deine Beschreibung hört sich nach Raten und Stochern im Dunkeln an. Kannst Du das Problem nicht mal ein bisschen konkreter (z. B. durch Sourcecode) darstellen? If-Ketten und Switch-Anweisungen lassen sich häufig durch andere, effizientere und übersichtlichere Konstrukte ersetzen. Das hängt jedoch sehr stark vom konkreten Problem ab.
    „Meine Komplikation hatte eine Komplikation.“
  • Sowas lässt sich oft mit einem Funktionspointerarray umsetzen, das ist dann nur noch ein indexierter Speicherlookup. Andererseits gebe ich Macmoonshine Recht: Das klingt etwas nach Stochern im Dunkeln. Ich würde nicht versuchen, schlauer als der Compiler zu sein - der ist normalerweise schon ziemlich gut darin, zu optimieren. Hast Du mal nachgesehen, was der überhaupt an Code rausgibt? Hast Du durch Profiling herausgefunden, ob diese Stelle überhaupt kritisch ist? Wenn es nach diversen Schleifen kommt, klingt es für mich so, als ob die Schleifen selbst bessere Kandidaten sind. Und bist Du sicher, dass die Gesamtstruktur so sein muss? Oft entsteht schlechte Performance nicht durch langsamen Code im Kleinen, sondern durch unnötige Berechnungen im Großen. Kannst Du etwas genauer schildern, was da eigentlich passieren soll?
    Multigrad - 360°-Produktfotografie für den Mac
  • An der Schleifen Struktur lässt sich leider nicht sehr viel ändern. Es geht einfach um eine Computer KI für ein gaaanz primitives Spiel.
    Es ist nur so das es dort sehr viele dieser simplen KI Kreaturen gibt.

    Ich mache an einer Stelle nichts anderes als:
    1) Ich gehe in einer Schleife alle diese Kreaturen durch. Daran lässte sich nichts groß überspringen.
    2) Sie haben schlichte Variablen für Ihr Verhalten, die sich verändern können, diese Stimmen quasi über Ihr verhalten ab,
    das sind mehrere, also muss ich ca 30 mal diese einzelnen Werte abhandeln pro Kreaktur-KI.
    3) Dann sind wir schon bei der genannten "IF" / "CASE" Weiche, da diese unterschiedlichen 20 Varianten jeweils
    unterschiedlich abgehandelt werden, je nachdem ob sie für HUNGER, ANGRIFF, etc. stehen.

    Ich will jetzt an der relevantesten Stelle ansetzen, sprich am Ende der Kette und dort die auszuführenden
    Befehle reduzieren.
    Und ja die Stelle ist Zeit kritisch, was auch auf der Hand liegt, sonst ist nichts derart aufwendiges im Spiel.

    Ich seh übrigens gerade das es immer noch ein GOTO Befehl in Objektive C gibt. War mir gar nicht bewusst.
    Hilft mir aber nichts groß da ich das GOTO an keine Variablen Nummer Koppel kann...

    ein:
    if (a==1) goto jumpmark1;
    if (a==2) goto jumpmark1;
    if (a==2) goto jumpmark1;
    ...
    kostet mich genauso Performance.
    Nur ein:
    goto jumpmark(nummer a);
    Würde mein Problem umgehen.

    Ich geb Euch eben mal die Performance Zeiten der verschiedenen Varianten die ich eben getestet habe:

    Ein 20er Case einmal für die 20 Fälle:
    Performance: 1,55 Sekunden für einen Durchlauf aller KI´s

    Jetzt If weiche <> 16 um bis zu 32 fälle aufzuteilen
    dann halbiere ich die 16 fälle wieder per if auf 8
    die dann auf 4
    die dann auf 2
    und mit dem letzten if dann auf einen konkreten fall.
    Das sind 5ifs bis zur Endklammer.
    Performance: 1,00 Sekunden!

    So sieht die IF Aufteilung übrigens aus. Inhalte der letzten Klammer habe ich zur Übersichtlichkeit
    mal rausgeworfen:



    if ( GEGNERKREATUR[a][motivationsTeil][0]>0)
    {
    // If Gabelungen von 0 bis 31
    if (GEGNERKREATUR[a][motivationsTeil][0]>16)
    {
    if (GEGNERKREATUR[a][motivationsTeil][0]>24)
    {
    if (GEGNERKREATUR[a][motivationsTeil][0]>28)
    {
    if (GEGNERKREATUR[a][motivationsTeil][0]>30)
    {
    if (GEGNERKREATUR[a][motivationsTeil][0]>31)
    {
    // Wert=32
    }
    else
    {
    // Wert=31
    }
    }
    else
    {
    if (GEGNERKREATUR[a][motivationsTeil][0]>29)
    {
    // Wert=30
    }
    else
    {
    // Wert=29
    }
    }
    }
    else
    {
    if (GEGNERKREATUR[a][motivationsTeil][0]>26)
    {
    if (GEGNERKREATUR[a][motivationsTeil][0]>27)
    {
    // Wert=28
    }
    else
    {
    // Wert=27
    }
    }
    else
    {
    if (GEGNERKREATUR[a][motivationsTeil][0]>25)
    {
    // Wert=26
    }
    else
    {
    // Wert=25
    }
    }
    }
    }
    else
    {
    if (GEGNERKREATUR[a][motivationsTeil][0]>20)
    {
    if (GEGNERKREATUR[a][motivationsTeil][0]>22)
    {
    if (GEGNERKREATUR[a][motivationsTeil][0]>23)
    {
    // Wert=24
    }
    else
    {
    // Wert=23
    }
    }
    else
    {
    if (GEGNERKREATUR[a][motivationsTeil][0]>21)
    {
    // Wert=22
    }
    else
    {
    // Wert=21
    }
    }
    }
    else
    {
    if (GEGNERKREATUR[a][motivationsTeil][0]>18)
    {
    if (GEGNERKREATUR[a][motivationsTeil][0]>19)
    {
    // Wert=20
    }
    else
    {
    // Wert=19
    }
    }
    else
    {
    if (GEGNERKREATUR[a][motivationsTeil][0]>17)
    {
    // Wert=18
    }
    else
    {
    // Wert=17
    }
    }
    }
    }
    }
    else
    {
    if (GEGNERKREATUR[a][motivationsTeil][0]>8)
    {
    if (GEGNERKREATUR[a][motivationsTeil][0]>12)
    {
    if (GEGNERKREATUR[a][motivationsTeil][0]>14)
    {
    if (GEGNERKREATUR[a][motivationsTeil][0]>15)
    {
    // Wert=16
    }
    else
    {
    // Wert=15
    }
    }
    else
    {
    if (GEGNERKREATUR[a][motivationsTeil][0]>13)
    {
    // Wert=14
    }
    else
    {
    // Wert=13
    }
    }
    }
    else
    {
    if (GEGNERKREATUR[a][motivationsTeil][0]>10)
    {
    if (GEGNERKREATUR[a][motivationsTeil][0]>11)
    {
    // Wert=12
    }
    else
    {
    // Wert=11
    }
    }
    else
    {
    if (GEGNERKREATUR[a][motivationsTeil][0]>9)
    {
    // Wert=10
    }
    else
    {
    // Wert=9
    }
    }
    }
    }
    else
    {
    if (GEGNERKREATUR[a][motivationsTeil][0]>4)
    {
    if (GEGNERKREATUR[a][motivationsTeil][0]>6)
    {
    if (GEGNERKREATUR[a][motivationsTeil][0]>7)
    {
    // Wert=8
    }
    else
    {
    // Wert=7
    }
    }
    else
    {
    if (GEGNERKREATUR[a][motivationsTeil][0]>5)
    {
    // Wert=6
    }
    else
    {
    // Wert=5
    }
    }
    }
    else
    {
    if (GEGNERKREATUR[a][motivationsTeil][0]>2)
    {
    if (GEGNERKREATUR[a][motivationsTeil][0]>3)
    {
    // Wert=4
    }
    else
    {
    // Wert=3
    }
    }
    else
    {
    if (GEGNERKREATUR[a][motivationsTeil][0]>1)
    {
    // Wert=2
    }
    else
    {
    // Wert=1
    }
    }
    }
    }
    }
    }

    PS: Finde die Option nicht mehr nur Einzelteile des Textes als Quellcode anzuzeigen...?
  • Ein Switch-Case

    Quellcode

    1. ​switch(GEGNERKREATUR[a][motivationsTeil][0]) {
    2. case 1:
    3. ...
    4. }

    würde hier genau das machen, was Du von Deinem goto jumpmark(a) erwartest. Bist Du Dir sicher, dass Dein Programm GEGNERKREATUR[a][motivationsTeil][0] für alle If-Anweisungen nur einmal auswertet? Der Ausdruck dürfte jedenfalls wesentlich mehr Takte als jeder Sprungbefehl verbrauchen.

    BTW: Code-Tag = <>-Button.
    „Meine Komplikation hatte eine Komplikation.“
  • macmoonshine schrieb:

    Ein Switch-Case

    Quellcode

    1. ​switch(GEGNERKREATUR[a][motivationsTeil][0]) {
    2. case 1:
    3. ...
    4. }

    würde hier genau das machen, was Du von Deinem goto jumpmark(a) erwartest. Bist Du Dir sicher, dass Dein Programm GEGNERKREATUR[a][motivationsTeil][0] für alle If-Anweisungen nur einmal auswertet? Der Ausdruck dürfte jedenfalls wesentlich mehr Takte als jeder Sprungbefehl verbrauchen.

    BTW: Code-Tag = <>-Button.

    Was ja daran liegen könnte, dass ein switch-case-Konstrukt nichts anderes als ein gezähmtes goto ist.
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • Schon Tiny-C hat aus einem switch() eine Sprungtabelle gemacht und es würde mich wundern warum das irgendein moderner Compiler anders machen sollte.

    Edit: ist mir gerade noch eingefallen - wenn Du Spiele programmierst wo es auf jeden Taktzyklus ankommt, dann solltest Du mit Bit-Operationen statt if() arbeiten. Und Tabellen die möglichst vieles schon vorab berechnen. Da kann eine 32-Bit-CPU 32 Vergleiche in einem Takt ausführen - wenn man die Daten geschickt codiert und anordnet. Evtl. läßt sich Deine ganze Abfragerei mit den Berechnungen in sagen wir mal 32 Tabellen mit je 1 MByte packen. Die müssen zwar offline erzeugt werden und beim Programmstart geladen, aber beides ist vielleicht vernachlässigbar.

    Beispiel: schnelle CRC-Algorithmen arbeiten nicht wie im Lehrbuch Bit für Bit ab sondern gleich ganze Bytes.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von hns ()

  • // Habe das mal zum nachprobieren als vereinfachten Code dargestellt:
    // Alles was man da optimieren kann kann ich im Originalcode genauso übernehmen.
    // Bisher war IF wie hier gezeigt am langsamsten, minimal Schneller SWITCH CASE, 33% schneller dann IF in Mehrfachschachtelung. Das mit den BIT Operatoren kenne ich noch nicht im Detail und schaue mir gleich mal an. Jemand ein Beispiel zur Hand?

    int Gegner[1000000][20]; // Gegner Nummer, 20 Neigungen
    int GegnerEntscheidung[1000000][20]; // Hier sammelt sich die Einflüsse der verschiedenen "Neigungen"

    // Gegner mit zufälligen werten füllen
    for (int x=0; x<1000000; x++)
    for (int y=0; y<20; y++)
    Gegner[x][y]=[self getRndBetween: 1 and: 20];

    // Entscheidungspool leeren
    for (int x=0; x<1000000; x++)
    for (int y=0; y<20; y++)
    GegnerEntscheidung[x][y]=0;


    // Ab Hier loopen - Zeit Stoppen um Performance zu ermitteln

    // Jetzt alle Gegner durch gehen und deren Neigungen auswerten
    for (int x=0; x<1000000; x++)
    for (int y=0; y<20; y++)
    {
    if (Gegner[x][y]==1)
    {
    // Hier diverse fiktive Einflüsse auf den Entscheidungspool
    GegnerEntscheidung[x][0]++;
    GegnerEntscheidung[x][2]--;
    }
    if (Gegner[x][y]==2)
    {
    // Hier diverse fiktive Einflüsse auf den Entscheidungspool
    GegnerEntscheidung[x][0]+=GegnerEntscheidung[x][2]*2;
    GegnerEntscheidung[x][2]-=10;
    }
    if (Gegner[x][y]==3)
    {
    // Hier diverse fiktive Einflüsse auf den Entscheidungspool
    GegnerEntscheidung[x][0]++;
    GegnerEntscheidung[x][2]--;
    }
    if (Gegner[x][y]==4)
    {
    // Hier diverse fiktive Einflüsse auf den Entscheidungspool
    GegnerEntscheidung[x][0]++;
    GegnerEntscheidung[x][2]--;
    }
    if (Gegner[x][y]==5)
    {
    // Hier diverse fiktive Einflüsse auf den Entscheidungspool
    GegnerEntscheidung[x][0]+=GegnerEntscheidung[x][2]*2;
    GegnerEntscheidung[x][2]-=10;
    }
    if (Gegner[x][y]==6)
    {
    // Hier diverse fiktive Einflüsse auf den Entscheidungspool
    GegnerEntscheidung[x][4]+=14;
    GegnerEntscheidung[x][6]--;
    }
    if (Gegner[x][y]==7)
    {
    // Hier diverse fiktive Einflüsse auf den Entscheidungspool
    GegnerEntscheidung[x][9]++;
    GegnerEntscheidung[x][2]-=125124;
    }
    if (Gegner[x][y]==8)
    {
    // Hier diverse fiktive Einflüsse auf den Entscheidungspool
    GegnerEntscheidung[x][10]+=GegnerEntscheidung[x][12]*2;
    GegnerEntscheidung[x][11]-=10;
    }
    if (Gegner[x][y]==9)
    {
    // Hier diverse fiktive Einflüsse auf den Entscheidungspool
    GegnerEntscheidung[x][2]++;
    GegnerEntscheidung[x][4]--;
    }
    if (Gegner[x][y]==10)
    {
    // Hier diverse fiktive Einflüsse auf den Entscheidungspool
    GegnerEntscheidung[x][4]++;
    GegnerEntscheidung[x][11]--;
    }
    }

    // Zeit wieder stoppen = Performance Zeit festhalten

    // Jetzt irgendwas mit den Entscheidungswerten Anfangen...

    // Dann Entscheidungs Array löschen

    // Nächste Runde / Frame / etc.
  • also
    1. schmeist du da unmöglich viele daten in den stack (das kann nicht funktionieren).
    2. verwende uint8_t oder char oder sonstwas in der art anstatt int da du ja nur 20 werte speichern musst.
    3. werte (bzw adressen) zwischenspeichern!!!
    4. übersetze das ganze mal mit optimization dann ist das schneller.
    5. (eigentlich erstens) macht das wirklich alles so sinn? (was machst du mit 20 millionen gengner ?)

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von gritsch ()

  • Ich kapiere den Code nicht.

    - Du hast 1000000 KI-Figuren. Wenn ich Deine Schilderung richtig verstanden habe, hat jeder von ihnen ein aktuelles Verhalten - also im Wesentlichen eine Zustandsmaschine. Warum hältst Du dann für jede KI-Figur einen ganzen Vektor mit 20 Werten, von dem jeder anscheinend 20 Werde annehmen kann? Wenn ich es richtig verstehe, braucht man nur die äußere Schleife. Oder sind das Gewichte? Dann verstehe ich die if-Kette noch weniger.

    - Es scheint so, als ob das Verhalten jedes Gegners sich immer nur auf die eigenen gegnerEntscheidung-Werte auswirkt, da könnte man den äußeren Index aus der Schleife rausziehen. Eigentlich sollte der Compiler das automatisch tun, aber meine Hand dafür ins Feuer legen würde ich nicht und das Rausziehen könnte den Code lesbarer machen.

    - Wenn Du eine Zustandsmaschine hast, würde ich die einzelnen Zustände eigenständig modellieren und das entsprechende Verhalten dort dranhängen. Das würde den Code erheblich lesbarer machen und Du hättest die ganze Verzweigerei nicht - man lässt das einfach den aktuellen Zustand machen.

    - Brauchst Du die 1000000*20-gegnerEntscheidungsmatrix wirklich? Reicht es nicht aus, wenn jede Figur einmal sein Verhalten abkakelt und entscheidet, in welchen neuen Zustand er geht? Mit einer 20MB-Matrix muss man erstmal etwas Sinnvolles anfangen

    - Eigentlich sollte das kein Performanceproblem sein (siehe die ganzen Kommentare drüber). Wenn doch, würde ich mir Gedanken über einen skalierbareren KI-Ansatz machen. Bei vielen Spielen wird z.B. die Rechenzeit für die KI entfernter Gegner runtergeschraubt, indem sie nicht in jedem Frame denken. Das lässt sich dann wunderbar auf eine brauchbare Framerate justieren.
    Multigrad - 360°-Produktfotografie für den Mac
  • 2. verwende uint8_t oder char oder sonstwas in der art anstatt int da du ja nur 20 werte speichern musst.
    Bringt leider nur minimale unterschiede in der Performance auch wenn ich meine das mehr bringen sollte...

    3. werte (bzw adressen) zwischenspeichern!!!
    Raus aus dem Array in Normale Variablen, in Besonders definierte Variablen?

    4. übersetze das ganze mal mit optimization dann ist das schneller.
    "optimization" ? Kenne nur die Einstellungen in der Build Setting - code optimizing für das ganze Projekt, die steht jetzt auf maximum was speed angeht. Aber da ist der unterschied von none bis max nur ein paar Prozent was die Performance angeht, selbst zu fastest-aggresiv optimizations

    5. (eigentlich erstens) macht das wirklich alles so sinn? (was machst du mit 20 millionen gengner ?)
    Natürlich muss ich noch an der Zahl schrauben ;) Aber ich will das zuerst an die Grenzen bringen, optimieren, und dann die maximale Gegnerzahl umsetzen die Sinn macht. Ist auch eher ein Experiment als ein großes Spielprojekt.

    Zu Mattiks Feedback: Auch vielen Dank,
    wegen Punkt eins: Das war nur ein Schematisches Beispiel, sorry, in den jeweiligen Endpunkten geschieht natürlich wesentlich mehr, order sagen wir wird mal geschehen als eine nackte Gewichtung. Da geschehen logische Operationen und richtiger Code, mal mehr mal weniger je nach Eigenschaft.


    Zu Punkt Zwei:
    Würde es etwas bringen anstelle eines Arrays mit Dimension GEGNER-NR, nur eines für einen Einzelnen zu haben, eines für jeden Gegner
    zu haben / Objekt, etc, und so dann der Reihe nach alle Gegner durchzugehen? Ist doch im Endeffekt das gleiche, bis auf das das
    Array eine Dimension weniger hat. Wobei ich das vorher getestet habe, nachdem ich oft gehört hatte Arrays sind extrem langsam. Hatte
    deshalb einmal Operationen mit INT Variablen, keine Arrays, ausgeführt, und dann das gleiche Spiel nur mit mehrdimensionalen Arrays, und es hat in der Performance keinen signifikanten unterschied gemacht... ??


    Nicht alle Runden/Frames die KI zu berechnen, bzw diese auf die Runden aufzuteilen ist ein Punkt den ich natürlich schon vorgesehen habe, aber Danke für den Tip. Das spart natürlich auch morde performance.

    Wegen Entfernte-KIS-Aussparen: Mein Ansatz ist genau das bewusst nicht zu tun, damit die Welt nicht überall Still steht nur weil der Spieler gerade abwesend ist. Deshalb die immense Anzahl an KI´s und der Versuch das alle zu optimieren.
  • RayJunx schrieb:

    Würde es etwas bringen anstelle eines Arrays mit Dimension GEGNER-NR, nur eines für einen Einzelnen zu haben, eines für jeden Gegner
    zu haben / Objekt, etc, und so dann der Reihe nach alle Gegner durchzugehen? Ist doch im Endeffekt das gleiche, bis auf das das
    Array eine Dimension weniger hat. Wobei ich das vorher getestet habe, nachdem ich oft gehört hatte Arrays sind extrem langsam.


    Arrays in C sind alles andere als langsam...
    Ich denke Du brauchst etwas Hintergrundwissen was der Compiler eigentlich macht und welchen Assemblercode er erzeugt. Außerdem spielt bei deinen Datenmengen eine große Rolle dass sie nicht mehr in den CPU-Cache reinpassen.

    Das in wenigen Worten zu erklären gelingt nicht - das ist schon ein halbes Semester Vertiefungsstudium :)

    Aber ein paar Tips:

    der C-Compiler macht aus

    Quellcode

    1. a[b]
    nichts anderes als

    Quellcode

    1. *(a+b)
    - wobei er den Index ggf. mit der Elementgröße multipliziert.

    D.h. ein Array-Zugriff ist eine Addition (ggf. mit Multiplikation der Elementgrößen).

    Wenn Du ein zweidimensionales Array hast, wird das einfach in eine Addition aus 3 Werten (Basisadresse + zwei Indizes) aufgelöst.
    Mit etwas Glück bildet das der Compiler auf max. 2 oder 3 Assemblerbefehle ab. D.h. mehr kann man so nicht optimieren.

    Allerdings kannst Du etwas anderes machen. Wenn der Index b immer um 1 erhöht wird, dann bedeutet das dass die Adresse um sizeof(*a) erhöht wird.
    Wenn diese Schrittweite zum Cache passt oder eine natürliche Größe ist, dann geht das schon mal schneller als jedesmal neu zu multiplizieren.

    D.h.

    Quellcode

    1. *a++ = 5;
    ist möglicherweise schneller als

    Quellcode

    1. a[i++] = 5;
    .

    Ein guter Compiler kann das aber in manchen Fällen erkennen (wenn Du es nicht zu sehr in Deinem Code versteckst).

    Nochwas: Du könntest versuchen das durch die Vektor-Einheit rechnen zu lassen. Dazu mußt Du Deinen Algorithmus aber vektorisierbar (parallelisierbar) aufschreiben...
  • Ich könnte mir vorstellen das das Ganze deutlich schneller wird wenn er es auf den Heap packt. Also erzeuge dein array mal mit dem huten alten malloc und gibt es am ende auch mit free wieder frei. Wenn Du dann wie von HNS vorgeschlagen mit Pointern drauf zugreifst , statt über indexe und nur uint_8t benutzt wie Grietsch vorschlug, dann sollte das eigentlich kein Problem mehr sein 20MB Speicher im Bruchteile einer Sekunde durchzugehen.

    Gruß

    Claus
    2 Stunden Try & Error erspart 10 Minuten Handbuchlesen.

    Pre-Kaffee-Posts sind mit Vorsicht zu geniessen :)
  • macmoonshine schrieb:

    Amin Negm-Awad schrieb:

    Was ja daran liegen könnte, dass ein switch-case-Konstrukt nichts anderes als ein gezähmtes goto ist.

    +lol+

    ...und es hat in vielen Fällen ein Gerüchle. ;)

    Ich meinte das einigermaßen technisch. Nicht zufällig ist die Syntax für case-Marken ähnlich zu der von goto-Labels. Und der Blockbezug ist ebenfalls, sagen wir einmal, einigermaßen frei. Wie auch das "Durchfallverhalten, was an gotos erinnert, nicht aber an ifs.

    Technologisch ist switch viel näher an goto als an if.

    Quellcode

    1. switch (1)
    2. {
    3. NSLog(@"Hallo");
    4. int b;
    5. case 1:
    6. int c; // Fehler, weil nicht entscheidbar, ob c in default lebend ist.
    7. b = 1;
    8. NSLog(@"1");
    9. default:
    10. NSLog(@"Nicht 1");
    11. break;
    12. }
    Alles anzeigen
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • RayJunx schrieb:

    Zu Punkt Zwei:
    Würde es etwas bringen anstelle eines Arrays mit Dimension GEGNER-NR, nur eines für einen Einzelnen zu haben, eines für jeden Gegner
    zu haben / Objekt, etc, und so dann der Reihe nach alle Gegner durchzugehen? Ist doch im Endeffekt das gleiche, bis auf das das
    Array eine Dimension weniger hat. Wobei ich das vorher getestet habe, nachdem ich oft gehört hatte Arrays sind extrem langsam. Hatte
    deshalb einmal Operationen mit INT Variablen, keine Arrays, ausgeführt, und dann das gleiche Spiel nur mit mehrdimensionalen Arrays, und es hat in der Performance keinen signifikanten unterschied gemacht... ??



    Nee, im einfachsten Fall ändert sich am Speicherlayout gar nichts - nur eine "Loop invariant code motion":
    Vor der inneren Schleife ein

    Quellcode

    1. int* aktGegner = Gegner[x];
    2. int* aktGegnerEntscheidung = GegnerEntscheidung[x];

    und dann innerhalb der inneren Schleife z.B. auf aktGegner[y] statt Gegner[x][y] zugreifen.

    Finde ich besser lesbar und es wird im Zweifelsfall schneller, zumindest nicht langsamer. Eigentlich sollte der Compiler das automatisch machen, aber es ist mittlerweile ziemlich schwierig geworden, vorherzusagen, was polly da so tut. Aber für Optimierungen auf dieser Ebene ist intensives Profilen und die Analyse des Kompilats notwendig, alles andere ist ziemlich blindes Gestochere. Ich würde nach wie vor versuchen, den Code erstmal nach Lesbarkeits- und Strukturaspekten zu schreiben. Das erhöht die Wahrscheinlichkeit, dass man auf höherer Ebene wesentlich lohnendere Optimierungsmöglichkeiten findet.
    Multigrad - 360°-Produktfotografie für den Mac