Jump to content

[HLADAM] Ako znížit CPU


Ratchet_xD

Recommended Posts

Urobil som si cez pawno na server warpy ale zatažuju mi server nekedy aj spadne kvoli nim :( nevie nekdo ako mám cez pawno spravit aby tie warpy neboli velkou zátažou pre moj server a nezaberali vela CPU ? Pls píšte. :) "help" "help"

Link to comment
Share on other sites

 

Optimalizace kodu

 

Testování

Pokud máte hlubší zájem o pawno a jde vám o co největší výkon je vhodné testovat rychlost svých scriptu. Na to je vhodné použít funkci GetTickCount, která vrací počet ms po které je server spuštěn (rozdílem GetTickCount po scriptu a před scriptem získáme počet ms potřebných na provedení scriptu). Avšak aby rozdíl byl měřitelný (rozdíly sou většinou v řádech %ms) je dobré dané kody na porovnání hodit do cyklu s předefinovaným počtem opakování aby byl ten rozdíl opravdu viditelný

Kód:

 

#define NUM_TestLoop 8000

new time1,time2;

time1 = GetTickCount();

for (new i=0;i

{

..Code1

}

time2 = GetTickCount();

printf("Code1 Time: %d",time2-time1);

time1 = GetTickCount();

for (new i=0;i

{

..Code2

}

time2 = GetTickCount();

printf("Code2 Time: %d",time2-time1);

 

 

Paměťová redukce

 

Booleany

Tam kde to jde, používejte booleany (může obsahovat 2 hodnoty - true/false). Boolean zabírá v paměti jeden Bit (nejmenší možná hodnota se kterou je procesor schopný pracovat)

Kód:

 

new bool:Promena

 

Když dáte oproti booleanu číslo (přestože vám stačí uchovavat pouze 2 hodnoty)

Kód:

 

new Promena

 

Vytvoříte proměnou zabírající 32bitů. Docela rozdíl ne?, zvláště pak pokud je proměná pro 500hráčů

 

new string[256+];

Nevím kdo s tím přišel, ale velmi často pawneři z nepochopitelných důvodů vytvářeji všude new string[256] nebo dokonce i vyšši, proč?. Maximální délka zprávy v chatu (SCM) je 128 znaků. New string[128] je tedy naprosto dostačující (můžete používat stringy menší, ale zase lepší víc než mín což by mělo za následek nezobrazení některých znaků).

 

Funguje to asi takhle

Kód:

 

new string[128];

for (new i=0;i

{

string = 0;

}

 

(Samozřejmě to probíhá mnohem rychleji, ale princip je stejný). Oproti stringu o 256 znacích to znamená 100% redukci nejen paměti ale také CPU protože každému poli je v pawn přiřazená defaultní hodnota 0.Větší string má smysl používat u databazi/logů/ či nějakých takových věcí kde je opravdu zapotřebí větší počet znaků.

Tak samo je zbytečné vytvářet string[128] znaků dlouhý pro jméno hráče když maximalní délka nicku je 21 znaků.Vždy zvažte kolik znaků opravdu potřebujete

 

Matematické operace

Kompiler je schopný optimalizovat konstanty.. na příkladu to bude asi nejlepší

cislo - 5 + 8 .. kompiler převede na cislo + 3

naopak pokud kod převede do nasledujícího tvaru (cislo-5) +8 nebude kompiler schopný optimalizovat a zcela zbytečně se následně provedou 2 matematické operace místo jedné

 

Docela oblíbené je také používaní nasledujícího kodu

Kód:

 

floatsqroot(floatpower(floatabs(floatsub(x,x1)),2)+floatpower(floatabs(floatsub(y,y1)),2)+floatpower(floatabs(floatsub(z,z1)),2));

 

Tak zaprvé floatabs je naprosto zbytečná funkce, když umocnění nadruhou je vždycky kladné. Další věc je floatsub (vratí rozdíl dvou Float.. docela zbytečné ne když máme znamínko -, navíc je to pomalejší? Docela dost mě ale překvapilo že floatpower(x,2) [funkce pro umocnění] je výrazně pomalejší než (x*x).

Výsledný optimalizovaný kod pak bude vypadat takto:

Kód:

 

floatsqroot((x-x1)*(x-x1)+(y-y1)*(y-y1)+(z-z1)*(z-z1));

 

 

Další věc new ran = random(2);

switch(ran).. zbytečná proměná navíc ->

switch(random(2))

 

Prázdný řetězec

Velmi často se pro zjištení prázdného řetězce používá tohle to:

Kód:

 

if (strlen(params) == 0)

 

Tato funkce zjištuje počet znaků v řetězci zhruba tímto způsobem, což při větších řetězcích je zbytečně zátěžové zvláště pak pokud chceme pouze ověřit jestli je prázdný

Kód:

 

for (new i=0;i

 

Nejepší řešením je (pokud string neexistuje, vrací false)

Kód:

 

if (!params[0])

 

 

Používejte PVary

Pokud máte server o 500slotech je určitě namístě v rámci paměťové redukce začít používat PVary (PlayerVariables). Mažou se automaticky po odpojení hráče, takže nezabirají "prazdná místa" jako klasické new. Jejich výhodou je také že s nima může pracovat i FS takže nemusíte řešit "konektivitu s modem" pomocí pomalého CallRemoteFunction. Dou také dynamicky mazat -> nemusíte udržovat v běhu "potencionálně potřebné" proměné a vytvoříte je /zničíte dle potřeby.

 

Avšak u frekventovaných proměných které často cyklíte doporučuju zůstat u klasických new protože PVary budou zřejmě pomalejší než klasické proměné (zase je otázka jaký chcete udržovat poměr mezi CPU/Paměť)

 

Naučte se pracovat s navratovými hodnotami

 

Exemplarní případ toho jak by to nemělo vypadat

Kód:

 

if(IsPlayerInAnyVehicle(playerid))

{

new veh = GetPlayerVehicleID(playerid);

GetVehicleHealth(veh,..);

}

 

Script první ověří jestli je hráč ve vozidle, pokud ano funkce GetPlayerVehicleID opět ověří jestli je hráč ve vozidle a vrací ID jeho vozidla a nebo 0 v případe že hráč nesedí ve vozidle.Následující kod mi příjde mnohem sympatičtější

Kód:

 

new veh = GetPlayerVehicleID(playerid);

if (veh)

{

GetVehicleHealth(veh..)

}

 

Navratové hodnoty jsou popsany na wiki (která bohužel nyní nejde a toto je jediné co si pamatuju Cheesy)

 

Nové funkce

Pokud to jde, používejte implentované funkce

IsPlayerInRangeOfPoint() je mnohem rychlejší než pawno ekvivalent v podobe IsPlayerInSphere()

 

Uchovavejte navratové hodnoty

 

Pokud nějakou funkci voláte více než jednou, uchovavejte si její hodnotu ušetříte tím spustu CPU

Kód:

 

stock GetPlayers()

{

for (new i=0;i

{

if (IsPlayerConnected(i) players++;

}

return players;

}

if (GetPlayers() > 5)

else if (GetPlayers() < 2)..

 

čímž dojde naprosto zbytečně k několikatinásobnému cyklení všech hráčů ->

Kód:

 

new players = GetPlayers();

if (players > 5)

else if (players < 2)..

 

Cyklení hráčů

Kód:

 

Obvykle probíhá takto:

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

{

if (IsPlayerConnected(i))

{

...

}

}

 

Pokud má váš server 500slotů značně efektivnější řešení může být ukládat ID připojených hráčů do proměné (aktualizované v OnPlayerDisconnect a OnPlayerConnect).

Kód:

 

new Players[500] = {-1,...};

new players_count;

#define player Players

 

public OnPlayerConnect(playerid)

if (IsPlayerNPC(playerid)) return 1;

for (new i=0;i

{

if (player == -1)

{

player = playerid;

break;

}

}

players_count++;

 

public OnPlayerDisconnect(playerid,reason)

{

if (IsPlayerNPC(playerid)) return 1;

for (new i=0;i

{

if (player == playerid)

{

player = -1;

for (new j=i+1;j

{

if (Player[j] == -1)

{

if (Player[j-1] == -1) break;

player = Player[j-1];

Player[j-1] = -1;

break;

}

}

}

}

players_count--;

}

 

následné cyklení hráčů pak bude vypadat následovně (vzhledem k tomu že do proměné Players jsou ukladana ID pouze připojených hráčů není třeba podmínka IsPlayerConnected() && !IsPlayerNPC:

Kód:

 

new p;

for (new i=0;i

{

p = player;

SetPlayerPos(p..

GetPlayerPos(p..

GetPlayerFacingAngle(p..

 

}

 

Na serveru o 500slotech s tím dosáhnete kurevského zrychlení na serverech o menším počtem slotu to už nebude tak vyrazné, nicméně výkon tímhle tím můžete jenom zvyšit.

 

Podmínky

 

Velmi často, naprosto nelogicky je v kodu vidět něco ve smyslu

Kód:

 

new a = 5;

if (a == 0)..

if (a == 2)..

if (a == 5)..

 

Je jasné, že a se bude rovnat jenom jedné hodnotě proč tedy zbytečně testovat pro každé číslo (i když předtím už byla podmínka splněna)

Používejte buďto returny (které ukončí celý script) nebo else if (který je ověřován pouze v případe že předchozí podmínka/ky nebyly splněny.

Kód:

 

if (a == 0)..

else if (a == 2)..

else if (a == 5)..

 

 

Timery

Pro timery obecně platí pravidlo čím míň tím líp, nejlépe používat jenom ty které sou opravdu nezbytně nutné. Pomocí počítaní s proměnou můžete ušetřit takových třeba 20timerů a sloučit je do jednoho publicu. Jako největší zlo vidím SetTimerEx pro každého hráče (a nejlépe když má každý hráč ještě 3 takové timery- 100hráčů > 300 SetTimerEx); Přitom se to dá vyřešit jednoduchým počítáním s proměnou. Do publicu s timerem na sekundu můžete dát nepříklad něco takového (narychlo vytažené z PFR, PlayerSpecial[playerid][timer] určite počet sekund,PlayerSpecial[playerid][timer_type] určuje druh timeru -> v tomto případě nemužou jet paralerně). Zaleží jen na vaší vynalezavosti

Kód:

 

if (PlayerSpecial[playerid][timer] != -1)

{

if (PlayerSpecial[playerid][timer] > 0) PlayerSpecial[playerid][timer]--;

else{

PlayerSpecial[playerid][timer] = -1;

switch (PlayerSpecial[playerid][timer_type])

{

case TIMER_TYPE_SLEEP:{

StopSleeping(playerid);

}

}

}

 

Filterscripty

To samé jako Timery, čím mín tím líp

//Dál už mě teď nic nenapadá, časem doplním. Jde hlavně o to vždy zvážit jestli by daný script nešel vyřešit lépe a opravdu nepřetěžovat funkce a volat je opravdu jenom tolikrát kolikrát je to nezbytně nutné (strašně se mi líbí, dini -> Když ukládáte 20 proměných 20x otevřete a zavřete soubor,20x vytvoříte nějaké proměné a někteří experti místo toho aby jednou zjistili jméno hráče a pak to vždy ukladali do jeho souboru ho zjistí 20x přes PlayerName()

 

 

by : Darknes

 

Link to comment
Share on other sites

Já nemůžu kvůli warpům ti padá server cos tam do toho dal warp do vesmiru nebo co :d

 

xDD jeden warp je tam dost vysoko :d

 

-- ned 15. kvě 2011 21:05:39 --

 

Optimalizace kodu

 

Testování

Pokud máte hlubší zájem o pawno a jde vám o co největší výkon je vhodné testovat rychlost svých scriptu. Na to je vhodné použít funkci GetTickCount, která vrací počet ms po které je server spuštěn (rozdílem GetTickCount po scriptu a před scriptem získáme počet ms potřebných na provedení scriptu). Avšak aby rozdíl byl měřitelný (rozdíly sou většinou v řádech %ms) je dobré dané kody na porovnání hodit do cyklu s předefinovaným počtem opakování aby byl ten rozdíl opravdu viditelný

Kód:

 

#define NUM_TestLoop 8000

new time1,time2;

time1 = GetTickCount();

for (new i=0;i

{

..Code1

}

time2 = GetTickCount();

printf("Code1 Time: %d",time2-time1);

time1 = GetTickCount();

for (new i=0;i

{

..Code2

}

time2 = GetTickCount();

printf("Code2 Time: %d",time2-time1);

 

 

Paměťová redukce

 

Booleany

Tam kde to jde, používejte booleany (může obsahovat 2 hodnoty - true/false). Boolean zabírá v paměti jeden Bit (nejmenší možná hodnota se kterou je procesor schopný pracovat)

Kód:

 

new bool:Promena

 

Když dáte oproti booleanu číslo (přestože vám stačí uchovavat pouze 2 hodnoty)

Kód:

 

new Promena

 

Vytvoříte proměnou zabírající 32bitů. Docela rozdíl ne?, zvláště pak pokud je proměná pro 500hráčů

 

new string[256+];

Nevím kdo s tím přišel, ale velmi často pawneři z nepochopitelných důvodů vytvářeji všude new string[256] nebo dokonce i vyšši, proč?. Maximální délka zprávy v chatu (SCM) je 128 znaků. New string[128] je tedy naprosto dostačující (můžete používat stringy menší, ale zase lepší víc než mín což by mělo za následek nezobrazení některých znaků).

 

Funguje to asi takhle

Kód:

 

new string[128];

for (new i=0;i

{

string = 0;

}

 

(Samozřejmě to probíhá mnohem rychleji, ale princip je stejný). Oproti stringu o 256 znacích to znamená 100% redukci nejen paměti ale také CPU protože každému poli je v pawn přiřazená defaultní hodnota 0.Větší string má smysl používat u databazi/logů/ či nějakých takových věcí kde je opravdu zapotřebí větší počet znaků.

Tak samo je zbytečné vytvářet string[128] znaků dlouhý pro jméno hráče když maximalní délka nicku je 21 znaků.Vždy zvažte kolik znaků opravdu potřebujete

 

Matematické operace

Kompiler je schopný optimalizovat konstanty.. na příkladu to bude asi nejlepší

cislo - 5 + 8 .. kompiler převede na cislo + 3

naopak pokud kod převede do nasledujícího tvaru (cislo-5) +8 nebude kompiler schopný optimalizovat a zcela zbytečně se následně provedou 2 matematické operace místo jedné

 

Docela oblíbené je také používaní nasledujícího kodu

Kód:

 

floatsqroot(floatpower(floatabs(floatsub(x,x1)),2)+floatpower(floatabs(floatsub(y,y1)),2)+floatpower(floatabs(floatsub(z,z1)),2));

 

Tak zaprvé floatabs je naprosto zbytečná funkce, když umocnění nadruhou je vždycky kladné. Další věc je floatsub (vratí rozdíl dvou Float.. docela zbytečné ne když máme znamínko -, navíc je to pomalejší? Docela dost mě ale překvapilo že floatpower(x,2) [funkce pro umocnění] je výrazně pomalejší než (x*x).

Výsledný optimalizovaný kod pak bude vypadat takto:

Kód:

 

floatsqroot((x-x1)*(x-x1)+(y-y1)*(y-y1)+(z-z1)*(z-z1));

 

 

Další věc new ran = random(2);

switch(ran).. zbytečná proměná navíc ->

switch(random(2))

 

Prázdný řetězec

Velmi často se pro zjištení prázdného řetězce používá tohle to:

Kód:

 

if (strlen(params) == 0)

 

Tato funkce zjištuje počet znaků v řetězci zhruba tímto způsobem, což při větších řetězcích je zbytečně zátěžové zvláště pak pokud chceme pouze ověřit jestli je prázdný

Kód:

 

for (new i=0;i

 

Nejepší řešením je (pokud string neexistuje, vrací false)

Kód:

 

if (!params[0])

 

 

Používejte PVary

Pokud máte server o 500slotech je určitě namístě v rámci paměťové redukce začít používat PVary (PlayerVariables). Mažou se automaticky po odpojení hráče, takže nezabirají "prazdná místa" jako klasické new. Jejich výhodou je také že s nima může pracovat i FS takže nemusíte řešit "konektivitu s modem" pomocí pomalého CallRemoteFunction. Dou také dynamicky mazat -> nemusíte udržovat v běhu "potencionálně potřebné" proměné a vytvoříte je /zničíte dle potřeby.

 

Avšak u frekventovaných proměných které často cyklíte doporučuju zůstat u klasických new protože PVary budou zřejmě pomalejší než klasické proměné (zase je otázka jaký chcete udržovat poměr mezi CPU/Paměť)

 

Naučte se pracovat s navratovými hodnotami

 

Exemplarní případ toho jak by to nemělo vypadat

Kód:

 

if(IsPlayerInAnyVehicle(playerid))

{

new veh = GetPlayerVehicleID(playerid);

GetVehicleHealth(veh,..);

}

 

Script první ověří jestli je hráč ve vozidle, pokud ano funkce GetPlayerVehicleID opět ověří jestli je hráč ve vozidle a vrací ID jeho vozidla a nebo 0 v případe že hráč nesedí ve vozidle.Následující kod mi příjde mnohem sympatičtější

Kód:

 

new veh = GetPlayerVehicleID(playerid);

if (veh)

{

GetVehicleHealth(veh..)

}

 

Navratové hodnoty jsou popsany na wiki (která bohužel nyní nejde a toto je jediné co si pamatuju Cheesy)

 

Nové funkce

Pokud to jde, používejte implentované funkce

IsPlayerInRangeOfPoint() je mnohem rychlejší než pawno ekvivalent v podobe IsPlayerInSphere()

 

Uchovavejte navratové hodnoty

 

Pokud nějakou funkci voláte více než jednou, uchovavejte si její hodnotu ušetříte tím spustu CPU

Kód:

 

stock GetPlayers()

{

for (new i=0;i

{

if (IsPlayerConnected(i) players++;

}

return players;

}

if (GetPlayers() > 5)

else if (GetPlayers() < 2)..

 

čímž dojde naprosto zbytečně k několikatinásobnému cyklení všech hráčů ->

Kód:

 

new players = GetPlayers();

if (players > 5)

else if (players < 2)..

 

Cyklení hráčů

Kód:

 

Obvykle probíhá takto:

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

{

if (IsPlayerConnected(i))

{

...

}

}

 

Pokud má váš server 500slotů značně efektivnější řešení může být ukládat ID připojených hráčů do proměné (aktualizované v OnPlayerDisconnect a OnPlayerConnect).

Kód:

 

new Players[500] = {-1,...};

new players_count;

#define player Players

 

public OnPlayerConnect(playerid)

if (IsPlayerNPC(playerid)) return 1;

for (new i=0;i

{

if (player == -1)

{

player = playerid;

break;

}

}

players_count++;

 

public OnPlayerDisconnect(playerid,reason)

{

if (IsPlayerNPC(playerid)) return 1;

for (new i=0;i

{

if (player == playerid)

{

player = -1;

for (new j=i+1;j

{

if (Player[j] == -1)

{

if (Player[j-1] == -1) break;

player = Player[j-1];

Player[j-1] = -1;

break;

}

}

}

}

players_count--;

}

 

následné cyklení hráčů pak bude vypadat následovně (vzhledem k tomu že do proměné Players jsou ukladana ID pouze připojených hráčů není třeba podmínka IsPlayerConnected() && !IsPlayerNPC:

Kód:

 

new p;

for (new i=0;i

{

p = player;

SetPlayerPos(p..

GetPlayerPos(p..

GetPlayerFacingAngle(p..

 

}

 

Na serveru o 500slotech s tím dosáhnete kurevského zrychlení na serverech o menším počtem slotu to už nebude tak vyrazné, nicméně výkon tímhle tím můžete jenom zvyšit.

 

Podmínky

 

Velmi často, naprosto nelogicky je v kodu vidět něco ve smyslu

Kód:

 

new a = 5;

if (a == 0)..

if (a == 2)..

if (a == 5)..

 

Je jasné, že a se bude rovnat jenom jedné hodnotě proč tedy zbytečně testovat pro každé číslo (i když předtím už byla podmínka splněna)

Používejte buďto returny (které ukončí celý script) nebo else if (který je ověřován pouze v případe že předchozí podmínka/ky nebyly splněny.

Kód:

 

if (a == 0)..

else if (a == 2)..

else if (a == 5)..

 

 

Timery

Pro timery obecně platí pravidlo čím míň tím líp, nejlépe používat jenom ty které sou opravdu nezbytně nutné. Pomocí počítaní s proměnou můžete ušetřit takových třeba 20timerů a sloučit je do jednoho publicu. Jako největší zlo vidím SetTimerEx pro každého hráče (a nejlépe když má každý hráč ještě 3 takové timery- 100hráčů > 300 SetTimerEx); Přitom se to dá vyřešit jednoduchým počítáním s proměnou. Do publicu s timerem na sekundu můžete dát nepříklad něco takového (narychlo vytažené z PFR, PlayerSpecial[playerid][timer] určite počet sekund,PlayerSpecial[playerid][timer_type] určuje druh timeru -> v tomto případě nemužou jet paralerně). Zaleží jen na vaší vynalezavosti

Kód:

 

if (PlayerSpecial[playerid][timer] != -1)

{

if (PlayerSpecial[playerid][timer] > 0) PlayerSpecial[playerid][timer]--;

else{

PlayerSpecial[playerid][timer] = -1;

switch (PlayerSpecial[playerid][timer_type])

{

case TIMER_TYPE_SLEEP:{

StopSleeping(playerid);

}

}

}

 

Filterscripty

To samé jako Timery, čím mín tím líp

//Dál už mě teď nic nenapadá, časem doplním. Jde hlavně o to vždy zvážit jestli by daný script nešel vyřešit lépe a opravdu nepřetěžovat funkce a volat je opravdu jenom tolikrát kolikrát je to nezbytně nutné (strašně se mi líbí, dini -> Když ukládáte 20 proměných 20x otevřete a zavřete soubor,20x vytvoříte nějaké proměné a někteří experti místo toho aby jednou zjistili jméno hráče a pak to vždy ukladali do jeho souboru ho zjistí 20x přes PlayerName()

 

 

by : Darknes

Díki skusím niečo vimislet :d Moc dobrý pawner niesom :d

Link to comment
Share on other sites

Guest
This topic is now closed to further replies.
×
×
  • Create New...