Code Optimierung

  • Nja da sollte ja jeder draufkommen sobald


    public function lacks forward declaration (symbol "StartTimer_2")


    kommt :>
    Für PlayerOnServer müsste man eben nochmal ein extra public anlegen, damit er Beispielsweise unter
    OnPlayerConnect/OnPlayerDisconnect Spieler hinzufügt/entfernt.
    Aber was soll ich sagen, ist halt die Trägheit im Alter :sleeping:

    [align=center]
    Mta Script Editor - Work in Progress

  • Gott, warum liest sich das keiner durch?


    Naja egal, ich grab das ganze mal wieder aus.


    Logische Optimierungen


    Tja, da haben wir sie, die Logik. Viele hinterfragen überhaupt nicht was genau da eigentlich in ihrem Script passiert - aber wundern sich dann wenn es zu Server-Abstürzen, Laggs uvm. kommt. Am Ende geben sie natürlich nicht sich selbst die Schuld, sondern, wem sonst auch, der Sprache Pawn.


    Nun, fangen wir einfach an:


    for(new i = 0; i < MAX_PLAYERS; i++)
    {
    if(IsPlayerConnected(i)) {
    new string[128], name[MAX_PLAYER_NAME];
    GetPlayerName(i, name, sizeof(name));
    format(string, sizeof(string), "Name: %s ID: %d", name, i);
    SendClientMessageToAll(0xFF0000FF, string);
    }
    }


    Ganz einfacher Code, nicht?
    Die Schleife geht alle player IDs von 0 bis 199 durch (playerid 200 gibt es btw nicht), überprüft ob die player ID vorhanden ist, erstellt anschließend einen zwei Strings, einen mit 128 Charakteren, einen mit der Größe von MAX_PLAYER_NAME (theoretisch 24, praktisch 16). Dann wird der Name von "i" herausgefunden und der String "name" damit überschrieben. Zu guter letzt wird das ganze per printf formatiert und in der Console angezeigt.


    Dumm gecodet.


    Zum einen natürlich der viel zu große String, zum anderen aber auch der Punkt, dass wir 200 Mal zwei Variablen erstellen. Wenn ihr sowas im Script habt, tut mir euer Server jetzt schon leid.


    Die richtige Variante wäre in diesem Fall wohl eher in dieser Form:
    new string[32]/* don't need more */, name[MAX_PLAYER_NAME];
    for(new i = 0; i < MAX_PLAYERS; i++)
    {
    if(GetPlayerName(i, name, sizeof(name))) {
    /* Solltet ihr das nicht ganz kapieren, schaut euch die Posts auf der ersten Seite nochmal an,
    dann wisst ihr was ich damit bewirke */
    printf("Name: %s ID: %d", name, i);
    }
    }


    Andere Beispiele von vollkommen abstrakter, unverständlicher Logik sind beispielsweise solche Sachen:
    GetPlayerID(playerid)
    {
    return playerid;
    }


    Ok, ok, dieses Beispiel war vielleicht doch etwas zu fiktiv xD


    Aber so etwas kommt oft vor:
    stock ReturnName(playerid)
    {
    new name[MAX_PLAYER_NAME];
    GetPlayerName(playerid, name, sizeof(name));
    return name;
    }


    Ok, ich weiß: Viele nutzen so eine Funktion, ist ja immerhin ganz einfach damit den Spielernamen zu bekommen usw... Aber: Sie ist langsam.
    Besser wäre einfach so etwas:
    /* Globale Variable */
    new gPlayerName[MAX_PLAYERS][MAX_PLAYER_NAME]; // Neuer Array, 2-dimensional.


    /* Laden des Namens der playerid in den Array: */
    public OnPlayerConnect(playerid)
    {
    GetPlayerName(playerid, gPlayerName[playerid], MAX_PLAYER_NAME);
    return 1;
    }


    /* Anwendungsbeispiel */
    public OnPlayerCommandText(playerid, cmdtext[])
    {
    if(!strcmp(cmdtext,"/name")) {
    printf("%s", gPlayerName[playerid]);
    return 1;
    }
    return 0;
    }


    [ende]Ich hoffe ihr könnt das was ich euch gerade gezeigt habe in dieser und/oder anderer Form hernehmen. Solltet ihr keine Ahnung von dem Code hier drin haben: Scripting Basics


    Und kommt mir jetzt nicht damit dass ihr schon alles wisst. Tut ihr nicht. Ich genauso wenig. Keiner weiß alles.[/ende]


    Solltet ihr noch Ideen haben was genau ich noch hinzufügen sollte schreibt mich per PM an.


    MfG,
    Br1ght]NSG a.k.a. Bright[NSG]

  • Was den String betrifft: Wer den ersten Post von BlackFoX gelesen hat, sollte mittlerweile alles dazu wissen.


    Übrigens würde ich raten euch mit dem Lesen Zeit zu lassen. Lieber 1 Woche brauchen und dann alles wissen, anstatt 3 Stunden knallhart anschaun und am nächsten Tag alles vergessen zu haben.

  • Frage,
    wenn man alle new für tore etc auch auf die art macht bringt das was ?


    enum Newdefine
    {
    pdtor,
    callcost,
    }
    new Newdefine;

    Lust auf russisch roulette ?
    Okay...
    ... Ich hol mal meine Automatik ;D

  • Muss ich jetzt grad überlegen.
    Du könntest beispielsweise so etwas machen:
    enum e_Tor_Names {
    LSPD_Tor,
    SFPD_Tor,
    LVPD_Tor
    }


    new Tore[e_Tor_Names];


    /* Object erstellen, am besten unter OnGameModeInit() : */
    Tore[LSPD_Tor] = CreateObject(...);
    /* Command zum Bewegen : */
    if(!strcmp(cmdtext,"/lspd")) {
    MoveObject(Tore[LSPD_Tor], ...);
    return 1;
    }


    THEORETISCH funktioniert es so, weiß nicht ob es praktisch auch funktioniert, dürfte aber in dieser Form stimmen.


    // Was ganz wichtig ist:
    Eine enum-Struktur darf NICHT so heißen wie eine Variable, führt zu Errors.
    Der "Header" der Enumeration (enum blabla) sollte IMMER einen anderen Namen haben als die dazu gehörige Variable/Array.


    Falsch:
    enum Everything
    CallCost
    }


    new Everything[Everything];


    Richtig:
    enum _Everything
    CallCost
    }


    new Everything[_Everything];


  • Nicht so fiktiv wie man vllt. denkt, es hat wirklich mal jemand im SA-MP Forum diese Funktion im "Useful Functions" Fred gepostet
    Ihr könnt euch sicherlich vorstellen, wie Y_Less danach explodiert ist... :D


    Achja übrigends zur ersten Seite, dass dcmd langsamer sei, als strcmp etc. stimmt natürlich, weils eine neue Funktion aufruft ABER
    in Verbindung mit strcmp wird dann meist strtok verwendet und das ist wiederum der CPU Killer schlecht hin und wenn man beachtet, wieviel übersichtlich dcmd das ganze Skript macht, dann kann man diesen wirklich sehr kleinen Leistungsunterschied ruhig ignorieren...


    So und damit mein Post auch noch etwas Konstruktives an Funktionen hat:


    Sparen von Zeit in Positionsvergleichen


    Eine ziemlich nützliche Funktion, die sehr oft auch von Streamern benutzt wird ist:


    Code
    1. bool: isInReach(Float: x1, Float: y1, Float: z1, Float: x2, Float: y2, Float: z2, Float: reach)
    2. {
    3. return (getDistanceBetweenPoints(x1, y1, z1, x2, y2, z3) <= reach);
    4. }
    5. Float: getDistanceBetweenPoints(Float: x1, Float: y1, Float: z1, Float: x2, Float: y2, Float: z2)
    6. {
    7. return floatsqroot(floatpower(floatabs(floatsub(x1, x2)),2) + floatpower(floatabs(floatsub(y1, y2)),2) + floatpower(floatabs(floatsub(z1, z2)),2));
    8. }


    Sieht ziemlich professionell und optimiert aus, ist es aber definitiv NICHT.


    Was macht getDistanceBetweenPoints?
    Es rechnet den drei dimensionalen Pythagoras aus, also es zieht die Positionen von einander ab und berechnet den absoluten Wert um die Länge der Strecken zu erhalten, dann werden die Seiten alle mit 2 potenziert und anschließend die Wurzel draus gezogen. Klingt logisch. Ist es auch. Aber nicht effektiv.
    Zuerst einmal könnte man

    Code
    1. floatpower(floatabs(floatsub(x1, x2)),2)


    durch

    Code
    1. floatsub(x1, x2)*floatsub(x1, x2)


    ersetzen womit man einen Funktionsaufruf sparen würde. Der wahre Leistungsausbremser ist aber floatsqroot welche in dieser Form sehr ineffektiv ist.
    Natürlich ist diese Funktion von den Entwicklern von Pawn sehr effektiv programmiert worden, jedoch rechnet sie nunmal die Wurzel auf glaub ich 20 Stellen genau aus und das ist halt selbst mit der besten Engine leistungsfordernd.
    Zumal wir ja nichtmal die wirkliche Distanz brauchen, wir brauchen ja lediglich wissen ob die Koordinaten in Reichweite der anderen liegen.
    Durch simple mathematische Termumformungen kann man dann folgendes machen:

    Code
    1. (getDistanceBetweenPoints(x1, y1, z1, x2, y2, z3)*getDistanceBetweenPoints(x1, y1, z1, x2, y2, z3) <= reach*reach)


    ist exakt das gleiche, beide Seiten wurden mit 2 potenziert, jedoch wird dadurch die Wurzel welche in getDistanceBetweenPoints steckt aufgehoben und wenn man die Funktion nun intern schreibt und weitere Kürzungen von floatpower und floatabs vornimmt die auch überflüssig werden hat man also:

    Code
    1. bool: isInReach(Float: x1, Float: y1, Float: z1, Float: x2, Float: y2, Float: z2, Float: reach)
    2. {
    3. return (floatsub(x1, x2)*floatsub(x1, x2) + floatsub(y1, y2)*floatsub(y1, y2) + floatsub(z1, z2)*floatsub(z1, z2) <= reach*reach)
    4. }


    Das ist nun schon um einiges optimierter als am Anfang.
    Aber es geht noch um einiges besser, wir erinnern uns, dass Funktionsaufrufe Zeit kosten also werden auch die floatsubs alle durch ein "-" ersetzt somit haben wir:

    Code
    1. bool: isInReach(Float: x1, Float: y1, Float: z1, Float: x2, Float: y2, Float: z2, Float: reach)
    2. {
    3. return ((x1- x2)*(x1-x2) + (y1-y2)*(y1- y2) + (z1-z2)*(z1- z2) <= reach*reach)
    4. }


    Nah dran am Perfekten ABER es geht natürlich immernoch besser und zwar können wir noch einen Funktionsaufruf sparen. Welchen? Richtig "isInReach"
    Also weiter optimiert:

    Code
    1. #define isInReach(%0,%1,%2,%3,%4,%5,%6) \
    2. (((%0 - %3) * (%0 - %3)) + ((%1 - %4) * (%1 - %4)) + ((%2 - %5) * (%2 - %5)) <= %6 * %6)


    So und weiter gehts nicht, dieser Code ist jetzt so weit optimiert, dass er 40 mal schneller ist als der ursprüngliche Code und somit vor allem in Streamern, wo diese Funktion sehr oft aufgerufen wird, einiges an wichtiger Rechenzeit spart
    Ist übrigends nichts was ich rausgefunden habe ._. Sondern Y_Less wie auch fast alle anderen Sachen die mit SA-MP Scripting zu tun haben :D
    Naja ich hoffe mal ich konnt auch mal ein bisschen helfen
    /me legt die wunden Finger in ein Erholungsbad :D

  • ich weiß zwar jetzt nicht genau auf welchen Beitrag du dich beziehst, jedoch sind viele Optimierungen auch zur Leserlichkeit und tragen meistens dazu bei, dass das Skript "länger" wird, jedoch nur die .pwn in der .amx ist es dann wieder so als wenn nichts wär (bestes Beispiel Kommentare oder defines)

  • Jo sry wollte dich nicht angreifen. Du magst besser sein wie ich im scripten aber schau weisst du wielange du dann für ein script schreiben würdest? Aber wenn sich das wirklich lohnt hab ich nix gesagt :thumbup:


  • Um ehrlich zu sein:


    Wenn du den Code optimierst brauchst du auch nicht länger.


    Am besten du machst es so:
    Du schreibst ein Script auf deine Art.


    Dann schreibst du ein Script auf optimierte Art und Weise, also so, dass es möglichst ressourcenschonend gecodet ist.


    Danach kannst vergleichen was 1. schneller war und 2. einfacher war.


    // Ich persönlich bin mit ressourcenschonenderen Methoden schneller.

  • Achso wenn du "länger" auf die Zeit beziehst, die es zur Entwicklung braucht dann muss ich dir zustimmen, wenn man an jeder Ecke nur rumoptimieren würde, würden sich Entwicklungszeiten mit der Zeilenanzahl expotential erhöhen. Aber so ein paar simple Sachen gehen ja schon, vor allem wenn sie schon so wie hier bereitgestellt werden und nicht selber entwickelt werden müssen. Und länger macht ja mein Beitrag dein Skript nicht, es sspart ja sogar nen paar Zeilen weils aus 2 Funktionen nen theoretischen Einzeiler macht, wobei die Anwendung die gleiche ist...


  • jo wenn man das so macht Bringt das auch was ? :D ^^

    Lust auf russisch roulette ?
    Okay...
    ... Ich hol mal meine Automatik ;D

  • Das bringt soviel, das die Übersicht vorhanden ist wie Tjong schon erwähnte und du dir Linien Sparst was wieder zum anderen Punkt führt,
    ausserdem tragen enum klassen dazu bei das weniger Resourcen verbraucht werden, das kann man auch daran merken wenn
    1000 Variablen erstellst die alle Indexiert sind und eine enum erstellst die das enthält und dies einmal verknüpfst, die Datei größe steigern
    sich bei dieser ausrichtung der 1000 Index Variablen

    Mfg. BlackFoX_UD_ alias [BFX]Explosion


  • Technisch gesehen wenig bis gar nix, jedoch ist es meiner Meinung nach viel übersichtlicher wenn man es so macht.


    Ich empfehle es dir aufjedenfall:


    //


    So, ich hab jetzt selbst eine kleine Frage.
    Und zwar habe ich mir eine Alternative zur ganzen Sache mit IsPlayerConnected usw. ausgedacht.


    Ich zeigs am besten einfach:
    new bool:SlotActive[200] = false;


    #define IsConnected(%1) SlotActive[%1] == true


    #define foreach(%1,%2) new %1 = 0; \
    for(; %1 < %2; %1++) if(IsConnected(%1))


    public OnPlayerConnect(playerid) {
    SlotActive[playerid] = true;
    foreach(players,MAX_PLAYERS) {
    SendClientMessage(players,COLOR_RED,"Test");
    printf("%d", players);
    return 1;
    }


    Nun frage ich mich aber, ob das ganze auch schneller ist.


    Theoretisch dürfte es ja schneller sein, immerhin sind defines und variablen laut Speed Liste schneller als native Funktionen wie z.B. IsPlayerConnected.


    Leider kann ich es aber nicht mit genug Leuten testen um zu einem schlüssigen Ergebnis zu kommen. Daher frage ich euch ob ihr wisst ob die Standart Variante:
    for(new i = 0; i < MAX_PLAYERS; i++) if(IsPlayerConnected(i))
    oder meine:
    foreach(p2, MAX_PLAYERS)
    schneller ist.

  • Für alle die ihren AMX Code per DisAMX (nicht zu verwechseln mit DeAMX) optimieren wollen:


    Addition
    Beispiel: a = b + 2
    load.pri %[b] // primary register
    const.alt 2 // alternate
    add // primary = primary + alternate
    stor.pri %[a]


    Läßt sich optimieren durch:
    load.pri %[b]
    add.c 2
    stor.pri %[a]


    Vergleich: 8 Calls gegen 6 Calls.
    Die zweite Register-Variante ist um einiges schneller. (Nachzulesen Pawn Impl.)


    Auch Dec (In jeglicher Form) ist schneller als Sub (In jeglicher Form)
    Wobei dieses Defiziet zumindest auf Intel Prozessoren weniger gewichtig ist.


    Außerdem, wenn genug Dynamic Memory noch frei ist, ruhig diesen niedriger setzen.
    #pragma dynamic 4096
    Genügt für viele Filterscripte vollkommen.


  • Hm naja zB. in nem Checkpoint Streamer:



    kA ob der funktioniert hab ich jetzt grad mal so hier im Forum hingeskriptet, aber für so welche Sachen halt, wenn halt teilweise mehrere 1000 Aufrufe pro Sekunde stattfinden, bringt das wirklich viel.
    Im Vergleich zB. zu Commands die werden wesentlich seltener aufgerufen

    Einmal editiert, zuletzt von Tjong ()