Code Optimierung

Wichtiger Hinweis: Bitte ändert nicht manuell die Schriftfarbe auf schwarz sondern belasst es bei der Standardeinstellung. Somit tragt ihr dazu bei dass euer Text auch bei Verwendung unseren dunklen Forenstils noch lesbar ist!

Tipp: Ihr wollt längere Codeausschnitte oder Logfiles bereitstellen? Benutzt unseren eigenen PasteBin-Dienst Link
  • Ihr wollt euren Code Optimieren ?


    Ok fang ich mal an , es geht klar um Pawn/C++/C egal jetzt, jedenfalls erster Tipp das euer Code kürzer und schneller wird,
    was man oft im Script ,,The Godfather´´ sieht wäre:


    if(!strlen(tmp))
    {
    SendClientMessage(playerid,farbe,"nachricht");
    return 1;
    }


    Noch Kürzer ?? klar ! warum nicht ?


    if(!tmp[0])return SendClientMessage(playerid,farbe,"nachricht");


    Tja kurz genug ? :>




    .:::: Hilfreich ::::.


    Mich kotzt es persönlich an wenn Variablen einzeln definiert werden wobei man doch eig mehrere z.b. für einen Spieler braucht ,
    das sieht meist so aus:


    new Bank[MAX_PLAYERS];
    new Geld[MAX_PLAYERS];


    vllt auch so ?


    new Bank[MAX_PLAYERS],
    Geld[MAX_PLAYERS];


    naja beides der gleiche Müll wenn man es doch für jeden Spieler haben möchte , daran sitzt man ewig,
    hier so gehts am besten:


    enum spielerpaket
    {
    geld,
    bank,
    }
    new SpielerDaten[MAX_PLAYERS][spielerpaket];


    nun könnt ihr es so z.b. nutzen ( Für die, die Kein Plan haben )


    SpielerDaten[playerid][geld] = 10000;




    .:::: Text Optimieren :::::.


    Manchmal wenn ich durch ein Skript schaue sehe ich das manche variablen auf 256 stehen und als Textwert genutzt werden X(


    new String[256];


    Da denk ich mir doch Sa-mp unterstützt allgemein nur 128 Zeichen ,
    und zuviele Text Index Zellen machen das Skript noch unnötig Groß und den Code Langsamer


    new String[128];


    schon besser, aber was ihr auch noch beachten solltet definiert wirklich nur die Zeichenlänge die man in dem fall braucht.


    Das beste Beispiel , was ich übringens früher aus gewohnheit gemacht habe, eine Variable mit 128 Zellen zu definieren und darin einen Namen zu packen, aber ein Name kann maximal nur 16 Zeichen nutzen O_O"


    also:


    new Playername[16];
    GetPlayerName(playerid,Playername,sizeof Playername);




    .:::: Speichern von Daten im Script::::.


    So das ist mal gegen, die Leute die MySql nutzen für ihr Skript, es mag sein das MySql evntl. schneller ist und ihr Prima damit klar kommt, aber die normalen Datei Funktionen haben ebenso
    ein gute Leistung und benutzen mit dieser Methode auch so gut wie keine großen Resourcen:



    DMA's Zitat:
    Man muss ja nicht nach jedem kack speichern



    Da kann ich nur zustimmen, den Speichern würde ich erst wenn es die Situation erfordert,
    wie z.b.


    Der Spieler geht Offline Daten werden bei OnPlayerDisconnect gespeichert


    oder:


    Der Server geht offline , Daten werden meist per OnGameModeExit() oder OnGameModeExitFunc() gespeichert


    Das sind die besten wege zu einem Resourcen schonenden Skript!



    Geschrieben von mir BlackFoX und DMA hat sich die Zeit genommen und nochmal drüber geschaut bzw. nochwas ausgebessert ^^

    Mfg. BlackFoX_UD_ alias [BFX]Explosion


    Einmal editiert, zuletzt von Trooper[Y] ()

  • Erweiterung by Br1ght]NSG:


    Schleifen-Optimierung:


    for(new i = 0; i < MAX_PLAYERS; i++) { }


    Kennt ihr sicherlich alle. Eine ganz normale for-Schleife die von 0 - MAX_PLAYERS abläuft und dabei immer i um 1 erhöht. Aber in dieser Form ist die Schleife nicht gerade gut für euren Server.


    Optimisation


    Möglichkeit 1:
    Warum auf einem 32 Slot Server die Schleife bis 200 durchlaufen lassen?
    Das geht auch einfacher:
    for(new i = 0; i < 32 /* Ersetzen durch Slotanzahl oder max. User Anzahl */; i++) { }
    Na also, brauchen wir sie keine 200 Mal durchlaufen lassen.


    Möglichkeit 2 (in Verbindung mit 1):
    Warum die Schleife für nicht verbundene User durchlaufen lassen?
    for(new i = 0; i < 32; i++) { if(IsPlayerConnected(i)) { } }
    Damit überprüfen wir für alle 32 Slots ob "i", also der Spieler in diesem Fall, verbunden ist. Wenn nicht, dann wird die folgende Funktion nicht aufgerufen.


    Return Werte:
    Mal ein ganz einfacher, gängiger Code, der oft verwendet wird:
    new Float:hp;
    for(new i = 0; i < MAX_PLAYERS; i++)
    {
    if(IsPlayerConnected(i))
    {
    GetPlayerHealth(i, hp);
    SetPlayerHealth(i, hp+10);
    }
    }


    Mal abgesehen von der for-Schleife ist dieser Code doch eigentlich ganz schön gecodet. Findet ihr nicht?


    Denkste


    Denn:
    GetPlayerHealth überprüft schon automatisch ob "i", also in diesem Fall der Spieler, connectet ist. Es returnt 0 wenn er nicht connectet ist, und 1 wenn er online ist. Genauso die Abfrage: "IsPlayerConnected". Also überprüfen wir in diesem Code 2 Mal ob "i" verbunden ist, nicht gerade sehr vorteilhaft.
    Einfacher und einen Tick schneller ist dieser Code:
    new Float:hp;
    for(new i = 0; i < MAX_PLAYERS; i++)
    {
    if(GetPlayerHealth(playerid, hp))
    {
    SetPlayerHealth(playerid, hp+10);
    }
    }


    Habe den Code getestet, funktioniert genauso gut.


    Und wenn ihr den Code hernehmen wollt, die beste Version bei 32 Slot Servern:
    new Float:hp;
    for(new i = 0; i < 32; i++)
    {
    if(GetPlayerHealth(playerid, hp))
    {
    SetPlayerHealth(playerid, hp+10);
    }
    }


    [ende]So, das wars erstmal von mir, vielleicht gibts später noch mehr, mal schaun.[/ende]


    MfG,
    Br1ght]NSG | [NSG]Bright


    *PS: Alles was ich über ressourcenschonendes Coden und Scriptoptimierungen weiß, habe ich durch dieses Topic gelernt:
    Code Optimisations by Y_Less
    Lest es euch durch, wird nicht schaden.

    Einmal editiert, zuletzt von [NSG]Bright ()

  • #undef MAX_PLAYERS
    #define MAX_PLAYERS 32


    Inline If:
    new a = 0;
    printf("A ist %s", (!a?("Null"):("grösser als 0")));


    Bitte nehmt nicht die alte PAWNCC Version.
    Die neuste erhaltet ihr immer hier:
    http://www.compuphase.nl/pawn/pawn.htm


    langsam:
    for(new i = 0; i < MAX_PLAYERS; i++) {
    //bla
    }
    for(new i = 0; i < MAX_PLAYERS; i++) {
    //bla2
    }
    schnell:
    new i = 0;
    for(;i < MAX_PLAYERS; i++) {
    // bla
    }
    for(;i >= 0; i++) {
    // bla
    }


    Und lasst euch eins sagen:
    DCMD ist übersichtlicher, doch der Aufruf der neuen Funktion kostet Zeit. ;)

  • Zitat

    Und lasst euch eins sagen:
    DCMD ist übersichtlicher, doch der Aufruf der neuen Funktion kostet Zeit. ;)

    also ist dcmd jetz schlecht oder gut ? wenn schlecht welche ist die beste lösung ?

    „Auge um Auge - und die ganze Welt wird blind sein.“
    - Mahatma Gandhi

  • enum spielerpaket
    {
    geld,
    bank,
    }
    new SpielerDaten[MAX_PLAYERS][spielerpaket];
    Ich nenn die immer anderes,sonst kommt es vielleicht vor am ende das es Variablen mit den gleichen Namen gibt.
    enum e_Spielerpaket
    {
    spGeld
    spBank,
    }
    new SpielerDaten[MAX_PLAYERS][e_Spielerpaket];
    sp steht dabei für Spielerpaket


    Zitat

    new Playername[16];
    GetPlayerName(playerid,Playername,sizeof Playername);


    Defines sind da um sie zu nutzen.Deswegen sollte man MAX_PLAYER_NAME nehmen,man weiss ja nie.Falls es in einer neueren Version von SA:MP dazu kommt,dass die MAX_PLAYER_LEN erhöht wird auf 34 steht man dumm da mit einer größe von 16 ;) .



    //Edit:

    Zitat

    Da denk ich mir doch Sa-mp unterstützt allgemein nur 128 Zeichen ,
    und zuviele Text Index Zellen machen das Skript noch unnötig Groß und den Code Langsamer


    Das stimmt nicht.Der Chat unterstützt nur 128 Zeichen,deswegen ist es unnötig unter OnPlayerText oder OnPlayerCommandText einen string alá new string[256] zu erstellen.
    Beim auslesen einer Datei mit fread() kann es gut möglich sein das man mehr als 127 Zellen brauch,denn dort ist kein Limit (Irgendwann wir man wahrscheinlich an Grenzen kommen).Eben so bei SQL arbeiten.

    2 Mal editiert, zuletzt von Goldkiller ()

  • Wenn man sichs einfach machen möchte und nicht bei jedem neuen Script zuerst immer alles umdefinieren will, sollte man die a_samp Include editieren und die Werte MAX_PLAYERS sowie am besten noch MAX_PLAYER_NAME anpassen.

  • a_samp.inc
    #define SAMP_02X
    // alter code
    #undef MAX_PLAYER_NAME
    #define MAX_PLAYER_NAME 16


    So ist das bei mir. ;x


    Wenn also eine neue Version kommt, dann jo ist da kein Problem, da die a_samp.inc ja ersetzt werden muss.

  • Was mich interessieren würde, wäre bisher


    for(new i = 0;i<MAX_PLAYERS;i++)


    for(new i = 0;i<GetMaxPlayers();i++)


    So wie ich verstanden habe bzw. y_Less sagte wäre er selber nicht sicher
    was schneller ist , aber meiner meinung nach wäre die erste art dieser schleife schneller,
    da er in der Zweiten auf eine Funktion zugreifen muss namen GetMaxPlayers(); , würde
    doch normal wieder Zeit kosten, doch iwie haben beide eine Verzögerung, da MAX_PLAYERS bzw. die definierte 200
    alle Spieler/Slots durchlaufen müsste , und GetMaxPlayers(); schon wieder auf eine Externe Funktion zugreift.

    Mfg. BlackFoX_UD_ alias [BFX]Explosion


  • Kommt drauf an.Da hat Y_Less aber nicht ganz recht wie ich finde.

    Zitat


    for(new i = 0;i<MAX_PLAYERS;i++) 
    for(new i = 0;i<GetMaxPlayers();i++)


    Das ist genau genommen die langsamste Variante,wenn man davon aus geht das in der server.cfg 200 als Maximale Spieleranzahl eingetragen ist sowie MAX_PLAYERS auf 200 definiert ist.


    Wenn man jetzt davon aus geht,dass der Server sowieso nur maximal 100 Spieler zu lässt sieht es aber anders aus.Ohne MAX_PLAYERS auf 100 zu re-definieren.Dann wäre allerdings immernoch die erste variante mit MAX_PLAYERS schneller.
    Schneller wäre es schon so,wenn man von folgendem ausgeht:

    Zitat

    Server.cfg 100
    MAX_PLAYERS 200
    for(new i = 0;i<MAX_PLAYERS;i++) 
    for(new i = 0,j = GetMaxPlayers();i<j;i++)


    i<GetMaxPlayers() - Bei jedem neuen durchlauf wird GetMaxPlayers() (Gibt den Server.cfg Eintrag zurück) abgefragt,was viel langsamer ist als eine Konstante,200.Auch wenn es vielleicht nur 100 möglich Slots sind.Deswegen wurde das zuerst geändernt.


    Die beste Methode um GetMaxPlayers() zu nutzen wäre in OnGameModeInit() die Anzahl in einer Variable zu speichern und dann in der for()/while() Schleife zu benutzten.Ich benutze es so nur in meinem Admin Script,da ich nicht davon ausgehe das jemand das Script re-kompiliert und direkt MAX_PLAYERS re-definiert.


    Zitat

    Server.cfg 100
    MAX_PLAYERS 200


    for(new i = 0;i<MAX_PLAYERS;i++) 
    for(new i = 0;i<g_Max_Players;i++) //g_Max_Players = GetMaxPlayer();


    Jetzt wäre es mit g_Max_Players tatsächlich schneller,wer aber zu Faul ist um MAX_PLAYERS an seine Maximale Spieleranzahl an zu passen ist irgendwo selber Schuld.


    Um das mal an der Speed-Reihenfolge darzustellen:


    Kurz gesagt,
    for(new i = 0;i<MAX_PLAYERS;i++) 
    ist die schnellste Variante,wenn man MAX_PLAYERS re-definiert an seine Maximale Spieler Anzahl die auch in der server.cfg eingetragen ist.



    Wundert mich das hier einige Wert auf Speed-Optimierung legen.Freut mich jedenfalls :)

    Einmal editiert, zuletzt von Goldkiller ()

  • #undef MAX_PLAYERS
    #define MAX_PLAYERS 100
    Nach #include <a_samp> ist am einfachsten.

    /say, /kick, /ban etc. mit Grund (strtok benutzt):

    new cmd[32],tmp[32],idx;


    if(!strcmp(cmd,"/kick",true) && IsPlayerAdmin(playerid))
    {
    tmp=strtok(cmdtext,idx);
    new id=strval(tmp); // or ReturnUser(tmp);
    if(!IsPlayerConnected(id) || id==playerid || !cmdtext[idx])
    return SendClientMessage(playerid,0xFF0000FF,"Usage: /KICK <ID> <REASON>");


    new string[128];
    GetPlayerName(id,string,sizeof(string));
    format(string,sizeof(string),"*** %s has been kicked. (Reason: %s)",string,cmdtext[idx]);
    SendClientMessageToAll(0xFF0000FF,string);
    Kick(id);
    return 1;
    }


    new cmd[32],tmp[32],idx;


    if(!strcmp(cmd,"/say",true) && IsPlayerAdmin(playerid))
    {
    if(!cmdtext[idx])
    return SendClientMessage(playerid,0xFF0000FF,"Usage: /SAY <TEXT>");


    new string[128];
    format(string,sizeof(string),"* Admin: %s",cmdtext[idx]);
    SendClientMessageToAll(0x00FF00FF,string);
    Kick(id);
    return 1;
    }
    Anstatt lange Funktionen für Grund zu benutzen, einfach wissen, was der Index bei strtok bewirkt und anwenden. ;)



    Was man auch oft sieht: Überflüssig viele string-Variablen, Beispiel:

    if(!strcmp(cmd,"/me",true))
    {
    if(!cmdtext[idx])
    return SendLanguageMessage(playerid,0xFF0000FF,"Usage: /me <Text>");


    new name[MAX_PLAYER_NAME];
    GetPlayerName(playerid,name,sizeof(name));
    new string[128];
    format(string,sizeof(string),"%s %s",name,cmdtext[idx]);
    SendClientMessageToAll(0xFFFF00FF,string);
    return 1;
    }


    Schlecht.



    if(!strcmp(cmd,"/me",true))
    {
    if(!cmdtext[idx])
    return SendLanguageMessage(playerid,0xFF0000FF,"Usage: /me <Text>");


    new string[128];
    GetPlayerName(playerid,string,sizeof(string));
    format(string,sizeof(string),"%s %s",name,cmdtext[idx]);
    SendClientMessageToAll(0xFFFF00FF,string);
    return 1;
    }


    Viel besser.



    Wieso immer GetPlayerName!?
    Anstatt immer den Spielernamen neu zu laden:

    new PlayerName[MAX_PLAYERS][MAX_PLAYER_NAME];
    new PlayerIp[MAX_PLAYERS][16];


    public OnPlayerConnect(playerid)
    {
    GetPlayerName(playerid,PlayerName[playerid],MAX_PLAYER_NAME);
    GetPlayerIp(playerid,PlayerIp[playerid],16);
    //etc.
    return 1;
    }


    Man hat so mehr oder weniger konstante Daten gespeichert in einer Variable, anstatt es immer wieder neu zu laden.
    Falls man SetPlayerName benutzt, formatiert man PlayerName eben neu.

  • Aber die Function ist langsamer.
    Auch wenn 90% der Optimierungen eher technische Sache ist.


    Ob ich nun einen Jump Zero oder einen Jump Not Equal mach ist relativ wurst:


    if(!x[0]) // Jump Zero
    if(x[0] == '\0') // Jump Not Equal


    Langsamer sind natürlich Native:
    if(!strlen(x))
    // AMX (Pseudo):
    ncall strlen
    cmp
    jne

  • *Thema hochhol*


    Timer


    Dieses Tutorial gilt NUR für Timer die sich WIEDERHOLEN.


    Vorwort: Es herrscht die Behauptung dass viele Timer gleich viel Lagg ist. Diese Behauptung stimmt, jedoch nur teilweise, wie ich jetzt genauer erläutere:


    _______________________________________________________________________________________________________________


    Beispiel eines weit verbreiteten Systems (erfunden):
    SetTimer("ObjectStreamer", 1000, 1);
    SetTimer("AreaStreamer", 1000, 1);


    So, damit haben wir 2 Timer die genau parallel laufen, somit muss der Server jede Sekunde den Code von beiden Callbacks bewältigen.
    Nun, das kann, je nach Code der ausgeführt wird zu Laggs führen da der Server nunmal viel ausführen muss.


    _______________________________________________________________________________________________________________


    Jetzt kommen wir dazu wie wir das ganze anders, und vorallem ressourcenschonender lösen können:


    Beispiel*: Wir haben 1 Timer mit 1000 Intervall, 1 mit 1500, 1 mit 500, 1 mit 7500, 1 mit 15000


    Nun sieht die Belastung folgendermaßen aus (Zeit - Anzahl der Timer die gleichzeitig ausgeführt werden):
    500 - 1
    1000 - 2
    1500 - 2
    2000 - 2
    3000 - 3
    3500 - 1
    4000 - 2
    4500 - 3
    5000 - 2
    6000 - 3
    7000 - 2
    7500 - 4
    8000 - 2
    ...
    15000 - 5


    Das heißt, alle 15 Sekunden muss der Server alle 5 Timer auf einmal bewältigen, und das kann, je nach Anzahl des Codes der damit aufgeführt wird zu Laggs führen.


    Nun gibt es 2 Möglichkeiten um das zu verhindern:


    _______________________________________________________________________________________________________________


    Die 1. wäre die Timer so einstellen dass sie sich nicht überschneiden KÖNNEN, egal nach wievielen Wiederholungen. Diejenigen die in Mathe aufgepasst haben wissen dass es Zahlen gibt die genau diese Eigenschaft haben. Primzahlen.


    D.h. wir starten einen Timer statt mit z.B. einem 1500er Delay einfach mit 1497. 1497 ist eine Primzahl und kann also niemals mit einer anderen Zahl in Konflikt geraten. (Ich habs überprüft, es ist wirklich eine)


    SetTimer("ObjectStreamer", 987, 1);
    SetTimer("AreaStreamer", 1017, 1);


    Nun, diese Möglichkeit funktioniert aber nur wenn wir zwischen allen Timern min. 10 Millisekunden Unterschied haben, ansonsten zeigt sich kein klarer Unterschied.


    Unter Umständen kann es auch passieren dass sich die Timer doch noch überschneiden, bzw. die Auslastung nicht gleichmäßig ist, sondern wellenartig verläuft. Wenn man jedoch die Zahlen gut wählt, kann es sein dass sich das alles nurnoch jede Stunde trifft.


    _______________________________________________________________________________________________________________


    Die 2. Möglichkeit ist etwas komplexer, da man unter Umständen ein ganzes System dafür schreiben muss, damit es reibungslos funktioniert. Aber diese Möglichkeit ist auch die effizientere:


    Wir starten die Timer versetzt.


    Beispiel mit 4 1000 Millisekunden Timern:
    Den ersten starten wir ganz normal, den zweiten nach 250 Millisekunden, den dritten nach 500 und den vierten letzendlich nach 750. Damit hätten wir die Auslastung (welche natürlich vom Code der ausgeführt wird abhängig ist) gleichmäßig verteilt, und die Timer überschneiden sich nicht.


    SetTimer("Timer_1", 1000, 1);
    SetTimer("StartTimer_2", 250, 0);
    SetTimer("StartTimer_3", 500, 0);
    SetTimer("StartTimer_4", 750, 0);


    public StartTimer_2() { SetTimer("Timer_2", 1000, 1); return 1; }
    public StartTimer_3() { SetTimer("Timer_3", 1000, 1); return 1; }
    public StartTimer_4() { SetTimer("Timer_4", 1000, 1); return 1; }


    Das schwere ist halt dass man diese Zeitversetzung jedesmal neu berechnen muss wenn man einen Timer hinzufügt, bzw. den Delay ändert.


    Letzendlich muss man selbst entscheiden welche der beiden Versionen man bevorzugt. Euer Server wird es euch aufjedenfall danken.


    _______________________________________________________________________________________________________________


    Ein sehr nützliches Topic was dieses Thema betrifft, ist dieses hier aus dem englischen SA:MP Forum:


    Timer Discussion


    * Ein Teil der hier enthaltenen Informationen stammt aus diesem Topic:
    Bringen Timer den Server zum Laggen?
    _______________________________________________________________________________________________________________


    Das wars, viel Spaß beim Optimieren ;)


    MfG,
    Bright[NSG]

    Einmal editiert, zuletzt von [NSG]Bright ()

  • Nja das mit dem Timern kann man aber auch vereinfachen indem man bei deinem Beispiel


    SetTimer("Timer_1", 1000, 1);
    SetTimer("StartTimer_2", 250, 0);
    SetTimer("StartTimer_3", 500, 0);
    SetTimer("StartTimer_4", 750, 0);


    Einfach Parameter dazwischen quetscht, ein kleines Beispiel


    new x_var[1][MAX_PLAYERS];
    SetTimer("StartTimer_2", 250, 0);


    #define defaultvalue 0


    public StartTimer_2();
    {
    for(new x= 0;x<sizeof(PlayerOnServer);x++) //Abfrage wie viele Spieler sich auf dem Server befinden einrichten :sleeping:
    if(x_var[0][i] <= 0){
    x_var[0][x]++;
    else if(x_var[0][i] >= 1){
    x_var[0][x] = defaultvalue; //Wert wieder auf null setzen
    CallLocalFunction("KuhmachtMuh", "s", "~| Kuh |~");
    return 1;
    }
    }


    forward KuhmachtMuh(const string[]);
    public KuhmachtMuh(const string[])
    {
    printf("Muuuh> %s", string);
    //Und was ihr sonst braucht
    return 86;
    }



    Damit führt man einen Timer aus, aber zwei zeitversetzte Funktionen, also eine Funktion
    lässt sich direkt im StartTimer_2 ausführen lassen die andere im KuhmachtMuh public :>

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

  • Ja, ich geb zu das war nicht gerade die optimalste Methode. Hätte ich wirklich anders machen könnne.


    BTW: Bei dir hat sich ein Fehler eingeschlichen:
    public StartTimer_2();


    Shit Happens.