Popular Post IllidanS4 65 Odesláno: 7. Březen, 2018 Popular Post Share Odesláno: 7. Březen, 2018 (upraveno) PawnPlus 0.8 Stažení | Dokumentace Představuji vám svůj nový plugin, který z Pawnu udělá moderní programovací jazyk, v němž bude radost programovat pro SA-MP. Přidáním nových konstruktů a "typů" rozšiřuje sílu jazyka, ale zachovává a dokonce i navyšuje jednoduchost programování. Shrnutí hlavních funkcí Řetězce Všichni to známe – řetězec je pole znaků s pevnou maximální délkou, kterou vždy musíme znát, pokud chceme s řetězci cokoliv provést. Odteď už ne! PawnPlus přináší dynamickou alokaci řetězců, které lze spojovat, rozdělovat, porovnávat či jinak upravovat. Možná si říkáte, k čemu je to dobré, když funkce SA-MP (např. SendClientMessage) vyžadují předání řetězce jako pole, ale počkejte – jde to! Tento plugin umožní přetvořit jakoukoliv funkci SA-MP (i jiných pluginů) na variantu, která místo polí bere nové řetězce a funguje naprosto stejně. Asynchronní programování Nebojte, žádná vlákna či race-condition, ale lehké asynchronní programování pomocí úloh, jehož vzorem je jazyk C#. Pomocí nového příkazu jazyka lze pozastavit průběh stávající funkce a odložit jeho vykonávání na jakoukoliv jinou dobu a mezitím spustit třeba něco jiného. Již žádné složité používání SetTimerEx, s tímto pluginem lze prostě ve funkci čekat, než nastane žádaná situace, a pak pokračovat. "Reflexe" Pawn sice není natolik pokročilý, aby umožňoval jakoukoliv reflexi, ale v tomto pluginu naleznete i pár funkcí, které umožní např. zavolat nativní funkci pomocí jejího jména jako řetězce, či navázání libovolné funkce na konkrétní událost serveru. Příklady Vytvoření, spojení a předání řetězců #include "PawnPlus.inc" //Původní SendClientMessageToAll(color, const message[]) upravíme tak, že všechny "const x[]" nahradíme "AmxString:x". native SendClientMessageToAllStr(color, AmxString:message) = SendClientMessageToAll; //Pomocná funkce vytvářející řetězec (jehož maximální velikost známe). stock String:GetPlayerNameStr(playerid) { new name[MAX_PLAYER_NAME]; GetPlayerName(playerid, name, sizeof(name)); return str_new(name); } public OnPlayerConnect(playerid) { //Řetězce jsou reprezentované proměnnými se značkou "String". Lze je spojovat pomocí operátoru +. Funkce str_val umí převést libovolnou hodnotu na řetězec. new String:name = GetPlayerNameStr(playerid); new String:msg = str_new("Hráč ")+name+str_new(" (")+str_val(playerid)+str_new(") se připojil na server."); //Provede se zavolání funkce SendClientMessageToAll, jen místo druhého parametru se vezme dynamický řetězec namísto pole znaků. SendClientMessageToAllStr(-1, msg); } Rozdělení a porovnání řetězce //Obdobným způsobem upravena funkce SendClientMessage. native SendClientMessageStr(playerid, color, AmxString:message) = SendClientMessage; public OnPlayerCommandText(playerid, cmdtext[]) { new String:cmd = str_new(cmdtext); new String:name = cmd; new String:args = STRING_NULL; //Cyklus nalezne v řetězci mezeru a rozdělí jej na jméno a argumenty. new len = str_len(cmd); for(new i = 0; i < len; i++) { if(str_getc(cmd, i) == ' ') { name = str_sub(cmd, 0, i); args = str_sub(cmd, i+1); break; } } //Porovnání jména příkazu. if(name == str_new("/test")) { SendClientMessageStr(playerid, -1, args); //Vypsání argumentů. return true; } return false; } str_find a str_split bude v pozdější verzi. Pozastavení vykonávání funkce stock Odpocet() { SendClientMessageToAll(-1, "3"); wait_ms(1000); //Neblokující čekání (žádný cyklus). SendClientMessageToAll(-1, "2"); wait_ms(1000); SendClientMessageToAll(-1, "1"); wait_ms(1000); SendClientMessageToAll(-1, "0"); } //Poznámka: Čekání zablokuje vykonávání kódu až do nejbližší (externě vyvolané) funkce public. Pokud chcete, aby vnější kód pokračoval rovnou, použije CallLocalFunction. Čekání na libovolnou událost //Předělání funkce MoveObject tak, aby vracela úlohu, na niž lze "čekat" (tedy dokončení hýbání s objektem). stock task:MoveObjectTask(objectid, Float:X, Float:Y, Float:Z, Float:Speed, Float:RotX = -1000.0, Float:RotY = -1000.0, Float:RotZ = -1000.0) { //Úloha představuje abstraktní proces, který lze dokončit a jehož výsledkem může být nějaká hodnota. //task_new vytvoří novou prázdnou (nedokončenou) úlohu. new task:t = task_new(); //Zaregistruje novou "obsluhu" pro callback OnObjectMoved. Veřejná funkce SingleFireObjectTask se zavolá pokaždé, //kdy by se měla zavolat funkce OnObjectMoved a navíc se jí předá několik argumentů navíc. //specifikátor "e" značí předání ID nově vytvořeného objektu (to funkce pawn_register_callback i vrací). pawn_register_callback(#OnObjectMoved, #SingleFireObjectTask, "edd", t, objectid); MoveObject(objectid, X, Y, Z, Speed, RotX, RotY, RotZ); return t; } stock ObjectTest() { new obj = CreateObject(19300, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0); print("Object created!"); //Proběhne čekání na úlohu; zbytek kódu se vykoná, až skončí. await MoveObjectTask(obj, 0.0, 0.0, 10.0, 5.0); print("Object moved!"); } //Nová obsluha události OnObjectMoved má navíc tři parametry (na začátku), jejichž hodnotu lze určit //v pawn_register_callback. Zbytek parametrů je z OnObjectMoved. forward SingleFireObjectTask(callback:id, task:task, obj, objectid); public SingleFireObjectTask(callback:id, task:task, obj, objectid) { if(obj == objectid) { //Pokud se pohnul objekt, na nějž je zaregistrovaná tato obsluha (uloženo v parametru obj), obsluha se odregistruje pawn_unregister_callback(id); //a úloha, která k ní náleží, je nastavena na hotovou (s nějakým výsledkem). task_set_result(task, objectid); } } Technické informace Jak to celé funguje? Technologie, na které celý tento plugin staví, se nazývá hookování. Hookování principiálně umožňuje přepsat kód nějaké funkce vlastním kódem, který se spustí místo něj. Díky patří Zeexovi za jeho knihovnu subhook. Tak například každá nativní funkce, která bere řetězce, používá funkci amx_GetAddr z API AMX. Tato funkce očekává adresu nějaké proměnné uvnitř stroje AMX a vrací ukazatel, který na ni ukazuje (to lze zapsat jako DAT+addr, kde DAT je ukazatel na počátek datové sekce stroje AMX, jenž lze získat z takto pojmenovaného registru). Tato funkce se musí zavolat vždy, když v Pawnu předáváme nějakou adresu (výstupní proměnné či pole) a na hookování je tedy ideální. Pokud AMX zjistí, že addr neukazuje dovnitř datové sekce, vrátí chybu, a pak přichází na scénu nový kód pluginu, který místo vrácení chyby interpretuje adresu jako (nějaký) identifikátor dynamicky alokovaného řetězce a ukazatel na jeho znaková data úspěšně vrátí. Tak se navenek každé funkci jeví předaný argument jako správný, ale když dojde na získání ukazatele, namísto dovnitř AMX ukazuje vrácené číslo na data pluginu. Zbývá maličkost, tedy zařídit, že addr bude pro dynamické řetězce ukazovat vždy do paměti, která je mimo AMX (sice to téměř jisté, ale nikoliv zaručené). Zde se uplatňuje vlastní tag AmxString, za nímž stojí funkce str_addr. Ta z ukazatele na řetězec spočítá relativní ofset vůči datové sekci stroje AMX, takže pro ukazatele mimo datovou sekci bude vytvořené číslo vždy chybné pro normální funkci amx_GetAddr. Funkce amx_Exec se zase volá vždy, když chce server spustit kód v AMX. AMX samotné podporuje předčasné ukončení vykonávání funkce pomocí vyvolání chyby a jedna taková chyba je i AMX_ERR_SLEEP (lze ji vykonat příkazem sleep v Pawnu), která značí, že se kód může obnovit. Jelikož hookuji každé volání amx_Exec, mohu to okamžitě zjistit a průběh vykonávání obnovit, aniž by o tom byl informován server. Zbývá vyřešit jediný problém – uložený obnovovací stav AMX je jen jeden a při jiném volání se zruší. Proto si plugin ukládá veškerou dynamickou paměť AMX (halda a zásobník) a registry k sobě, jakmile je výkon funkce pozastaven, aby nedošlo ke ztrátě. Při obnově je potom paměť AMX dočasně vrácena do původního stavu a spouštění původní funkce se obnoví. Funkce pawn_register_callback funguje pomocí hookování amx_Exec a amx_FindPublic, jež se zavolá vždy, když je třeba zjistit index veřejné funkce v AMX. Pokud je na název callbacku registrována nějaká obsluha, místo skutečného indexu se vrátí záporný index, který je následně identifikován v amx_Exec, která napřed vykoná příslušné obsluhy zjištěné z tohoto indexu. Řetězce a garbage collection Jak známo, každá dynamická alokace musí mít svoji dealokaci, jinak by začala unikat paměť, ale tady jsem si vesele používal str_new a žádnou dealokaci jsem neřešil, ačkoliv použitá paměť byla nová. Jak je to možné? Řetězce mají svůj vlastní garbage collector, který se stará o jejich mazání. Ve skutečnosti existují dva druhy řetězců – lokální (dočasné) a globální. Lokální řetězce jsou jejich nejčastější typ (String) a jejich existence je omezena po dobu výkonu nejvyššího callbacku. Je-li např. do skriptu vstoupeno callbackem OnPlayerConnected, všechny nové lokální řetězce existují po celý běh této veřejné funkce a jakmile ta skončí, smažou se. Pozor! – jakmile funkce skončí, použití jejích lokálních řetězců způsobí pád serveru. Řetězce se mažou jen po skončení nejvyšší veřejné funkce, takže pokud se ve skriptu skáče pomocí CallLocalFunction/CallRemoteFunction, vnitřní řetězce existují i po skončení vnitřních funkcí public. To ovšem neplatí pro SetTimer, jelikož vnitřní funkce se zavolá až po skončení vnější. Životnost lokálních řetězců není prodloužena ani pomocí asynchronních funkcí, neboť samotný callback je ukončen prvním čekáním. Proto existují globální řetězce, které nejsou automaticky mazány. Jejich použití je ale nebezpečné, neboť se musíte postarat o jejich smazání; musíte myslet na každé místo, kde by byl globální řetězec. Příklad: native print_s(AmxString:string) = print; new GlobalString:str; public OnFilterScriptInit() { str = str_new("Goodbye world!"); } public OnFilterScriptExit() { print_s(str); str_free(str); str = STRING_NULL; } Globální řetězce jsou označeny tagem GlobalString a převod na ně je automatický, pokud do takto značené proměnné přiřazujete. Lze toho docílit i voláním funkce str_to_global. Všechny funkce fungují stejně pro globální i lokální řetězce. Místo str_free můžete použít i funkci str_to_local, jež naopak vrátí řetězec do lokálního prostoru. To doporučuji použít v kombinaci s asynchronními funkcemi: public OnFilterScriptInit() { new String:str = str_new("Hello world!"); str_to_global(str); wait_ms(1000); str_to_local(str); print_s(str); } Mutabilita řetězců Řetězce jsou mutabilní, tedy měnitelné (až na jednu výjimku – STRING_NULL). To znamená, že některé funkce (například str_del) pouze upravují existující řetězec, takže pokud s ním pracují jiné funkce, dostanou jiná data. To lze vyřešit vytvořením nového řetězce (např. přes str_clone) nebo použitím funkcí, které samy o sobě tvoří nový řetězec (např. str_sub). STRING_NULL má speciální chování. Lze jej použít na místě normálního řetězce a všechna volání proběhnou úspěšně, ale jeho délka je vždy nulová. Funkce jako str_resize nebo str_set na něj tedy nemají žádný vliv. str_clone a jiné vytvářející funkce ale vždy udělá řetězec nový (a normální). Návratová hodnota asynchronní funkce Funkce obsahující čekání skončí vlastně předčasně a v takovém případě je nutno vrátit kontrolu serveru. Jelikož ale server očekává od funkce nějakou hodnotu hned, na případný příkaz return nemusí vůbec dojít. Od toho je tu funkce yield, jež navrátí hodnotu volajícímu kódu okamžitě: public OnFilterScriptInit() { new ret = CallLocalFunction(#Func, ""); printf("%d", ret); } forward Func(); public Func() { yield 12; wait_ms(1000); return 13; } Program vypíše 12 hned po spuštění. Návratovou hodnotu zbytku funkce (či z dalších volání yield) zatím nelze nijak odchytit, ale očekávám přidání něčeho na způsob coroutines v Lua (task_create z veřejné funkce a await, který lze zavolat vícekrát na takovou úlohu). Něco divného na mém kódu? await a yield jsou funkce, ale používám je jako příkazy. Pawn umožňuje vypustit závorky z takového volání, což vypadá rozhodně lépe. Také používám výraz typu #Func, což známe z maker, a je to totéž jako prosté "Func". Pokud řetězec je zároveň jméno funkce, líbí se mi toto použití pro odlišení. Edited 8. Srpen, 2018 by IllidanS4 7 4 Link to comment Share on other sites More sharing options...
Tanga 132 Odesláno: 8. Březen, 2018 Share Odesláno: 8. Březen, 2018 (upraveno) před 10 hodinami, IllidanS4 said: Pokud AMX zjistí, že addr neukazuje dovnitř datové sekce, vrátí chybu, a pak přichází na scénu nový kód pluginu, který místo vrácení chyby interpretuje adresu jako (nějaký) identifikátor dynamicky alokovaného řetězce a ukazatel na jeho znaková data úspěšně vrátí. Milé! Vcelku prepracované aj ostatné veci, také rôzne detajly a tak. Páči sa mi to! Edit: Tá sekcia "Reflexe", čo iné by si tam chcel napríklad nakódiť? Podla mňa Pawn je vcelku schopný v tomto. Našiel si niečo čo sa nedá? Edited 8. Březen, 2018 by Tanga Reflexe Link to comment Share on other sites More sharing options...
IllidanS4 65 Odesláno: 8. Březen, 2018 Author Share Odesláno: 8. Březen, 2018 (upraveno) Tu sekci tam mám v podstatě jen na ty dvě funkce, pawn_call_native a pawn_register_callback (a ta "reflexe" v tom je prakticky jen to, že jméno funkce je řetězec). Mohlo by být zajímavé třeba analyzovat různé debugovací informace ve skriptu apod. pro nějaké užitečné věci, ale nenapadá mě moc příkladů využití. Teď mě mimoděk napadla funkce pawn_create_arglist, která by si uložila seznam různorodých argumentů a mohla by ho předat do (nativní) funkce, která očekává proměnný počet argumentů. stock MyPrint(const format[], ...) { new ArgList:args; pawn_create_arglist(args, 1); //začátek argumentů současné funkce printf(format, args); pawn_destroy_arglist(args); } V případě variadických funkcí jsou všechny argumenty předané referencí, takže pokud by si pawn_create_arglist zapamatovala adresu proměnné args, sloužila by ta jako unikátní identifikátor. Jen je nutno takovou adresu zase odregistrovat. Edited 8. Březen, 2018 by IllidanS4 Link to comment Share on other sites More sharing options...
xhunterx 55 Odesláno: 8. Březen, 2018 Share Odesláno: 8. Březen, 2018 (upraveno) No znie to zaujimavo. Nicmenej by si mohol pridat k tomu pluginu aj nejaku licenciu, aby ho bolo mozne pouzivat PS: PawnPlus uz je pouzivane meno projektu. Mozno by si mohol vybrat nieco vhodnejsie, ako StringPlus. Edited 8. Březen, 2018 by xhunterx Link to comment Share on other sites More sharing options...
IllidanS4 65 Odesláno: 8. Březen, 2018 Author Share Odesláno: 8. Březen, 2018 (upraveno) před 4 minutami, xhunterx said: No znie to zaujimavo. Nicmenej by si mohol pridat k tomu pluginu aj nejaku licenciu, aby ho bolo mozne pouzivat Na GitHubu máš přeci MIT. ;-) před 4 minutami, xhunterx said: PawnPlus uz je dost pouzivane meno projektu [1, 2]. Mozno by si mohol vybrat nieco vhodnejsie, ako StringPlus. Hledal jsem projekty se stejným jménem, ale naštěstí ani jeden z toho není plugin. Edited 8. Březen, 2018 by IllidanS4 1 Link to comment Share on other sites More sharing options...
xhunterx 55 Odesláno: 8. Březen, 2018 Share Odesláno: 8. Březen, 2018 před 1 minutou, IllidanS4 said: Na GitHubu máš přeci MIT. ;-) Sorry, som slepy. Neviem preco som to tam nevidel, ked som ten subor hladal. Link to comment Share on other sites More sharing options...
ATomas 296 Odesláno: 8. Březen, 2018 Share Odesláno: 8. Březen, 2018 (upraveno) Ja teda s pluginy moc nekamaradim. Ale zajima me to asynchronni programovani. To asi nefunguje na principu vice vlaken (procesoru) ze? Zkosel jsem si vytvorit moznost vice vlaken pomoci C++, ovsem vzdy mi to druhe vlakno proste cekalo az se dokonci akce na vlakne prvnim. Kdyz jsem odstranil to cekani, tak svr crashnul. Jinak nejake rychlostni testy? Celkem by me zajimalo jestli je to rychlejsi nez postaru. Protoze jestli ne, tak bych rozhodne neobjetoval pomalost scriptu vymenou za pohodlnost programatora Edited 8. Březen, 2018 by ATomas Link to comment Share on other sites More sharing options...
Hlavní moderátor vEnd 279 Odesláno: 8. Březen, 2018 Hlavní moderátor Share Odesláno: 8. Březen, 2018 před 1 hodinou, ATomas said: Jinak nejake rychlostni testy? Celkem by me zajimalo jestli je to rychlejsi nez postaru. Protoze jestli ne, tak bych rozhodne neobjetoval pomalost scriptu vymenou za pohodlnost programatora Není ta starost o výkon už trochu přehnaná? Jsem zastánce toho, aby lidé optimalizovali svoje kódy jak jen to jde, ovšem když se nabídne možnost, jak si ušetřit čas nebo usnadnit práci na úkor několika milisekund, ne-li méně, proč to nevyužít. Majitelé velkých serverů (jako ty) tyto pluginy stejně používat nebudou, ale když bude někdo vlastnit server pravidelně navštěvovaný 20-30 hráči (což už by byl sám o sobě úspěch na CZ/SK scéně), nevidím důvod, proč takové možnosti nepoužívat. Kdyby se každý ohlížel jen na výkon, do dnes programujeme v asembleru. @IllidanS4 vypadá to dobře, kdy to plánuješ vydat? Z tohoto: před 13 hodinami, IllidanS4 said: Plugin je zatím ve vývoji ksem pochopil, že se zatím nejedná o finální verzi. Link to comment Share on other sites More sharing options...
IllidanS4 65 Odesláno: 8. Březen, 2018 Author Share Odesláno: 8. Březen, 2018 před 3 hodinami, ATomas said: To asi nefunguje na principu vice vlaken (procesoru) ze? Zkosel jsem si vytvorit moznost vice vlaken pomoci C++, ovsem vzdy mi to druhe vlakno proste cekalo az se dokonci akce na vlakne prvnim. Kdyz jsem odstranil to cekani, tak svr crashnul. Nová vlákna to nevytváří; radši bych skutečné paralelní programování v AMX nedělal, protože to na to moc není stavěné. Teoreticky by asi šlo nechat výpočty v AMX běžet na jiném vlákně, ale všechna volání nativních funkcí by musela být synchronizována s hlavním. Otázkou je, jestli to za tu námahu stojí. Všechna obnovení z čekání (po wait_ticks, wait_ms) jsou vyvolána v rámci serverových tiků v hlavním vlákně. wait_ms je mimochodem přesnější než SetTimer. před 3 hodinami, ATomas said: Jinak nejake rychlostni testy? Celkem by me zajimalo jestli je to rychlejsi nez postaru. Protoze jestli ne, tak bych rozhodne neobjetoval pomalost scriptu vymenou za pohodlnost programatora Práce s řetězci je rychlá stejně jako s std::string v C++ (+ volání nativních funkcí). Testy jsem zatím nedělal, ale až na vytváření a mazání operace zrychlit nepůjdou. před 2 hodinami, vEnd said: kdy to plánuješ vydat? Dodělám dokumentaci, vše zkontrolují a vydám první verzi. Zbývá provést trochu lepší testování, ale zatím nevím o žádných chybách. Link to comment Share on other sites More sharing options...
xhunterx 55 Odesláno: 8. Březen, 2018 Share Odesláno: 8. Březen, 2018 (upraveno) před 11 hodinami, ATomas said: Ale zajima me to asynchronni programovani. To asi nefunguje na principu vice vlaken (procesoru) ze? Myslim, ze existuje plugin na viacvlaknove pawno, ale ako Illidan povedal, musis synchronizovat v podstate vsetky native a aj pristup ku globalnym premennym, takze sa to az na par extremnych pripadov neoplati. Viacvlaknove spracovanie je naozaj len uzitocne, ked nieco pocitas, ako hashe hesiel alebo zlozite query v DB. před 11 hodinami, ATomas said: Jinak nejake rychlostni testy? Celkem by me zajimalo jestli je to rychlejsi nez postaru. Protoze jestli ne, tak bych rozhodne neobjetoval pomalost scriptu vymenou za pohodlnost programatora Ako tiez nemam rad zbytocne pomaly kod, ale ako sa hovori, netreba optimalizovat predcasne. Ono vacsinou aj tak vacsinu vykonu ti zoberie par pomalych funkcii, ktore sa volaju casto. Tak v nich pripadne mozes pouzit aj stare polia ked to odprofilujes ale ak ti nieco taketo usetry cas programatora, myslim ze je to cennejsie nez tych par cyklov CPU. Nakonec tieto operacie su tak rychle, ze na tom vacsinou nezalezi aj keby boli 10x pomalsie... Jedine v nejakych velkych cykloch... To povedane, ja osobne som nikdy prilis nemal rad tuto syntax, takze ja asi ostanem pri starych stringoch zatial. Ale vnukol si mi par inych napadov, na ktore sa asi pozriem s mojim pluginom Edited 8. Březen, 2018 by xhunterx Link to comment Share on other sites More sharing options...
IllidanS4 65 Odesláno: 9. Březen, 2018 Author Share Odesláno: 9. Březen, 2018 Verze 0.1 vydána! Do příspěvku nahoru jsem doplnil odkaz na stažení a dokumentaci. Hlaste mi prosím chyby, ideálně na GitHub. Link to comment Share on other sites More sharing options...
ATomas 296 Odesláno: 11. Březen, 2018 Share Odesláno: 11. Březen, 2018 On 8. 3. 2018 at 14:13, vEnd said: Není ta starost o výkon už trochu přehnaná? Jsem zastánce toho, aby lidé optimalizovali svoje kódy jak jen to jde, ovšem když se nabídne možnost, jak si ušetřit čas nebo usnadnit práci na úkor několika milisekund, ne-li méně, proč to nevyužít. Majitelé velkých serverů (jako ty) tyto pluginy stejně používat nebudou, ale když bude někdo vlastnit server pravidelně navštěvovaný 20-30 hráči (což už by byl sám o sobě úspěch na CZ/SK scéně), nevidím důvod, proč takové možnosti nepoužívat. Kdyby se každý ohlížel jen na výkon, do dnes programujeme v asembleru. Tak pokud ten sa-mp furt jede na jednom vlakne. Tak me vykon celkem zajima Hlavne se strimgem pracujes kazdou chvili. Ale jako jo mas pravdu na serveru s 20ti lidma moc vykon asi resit nebudes Link to comment Share on other sites More sharing options...
IllidanS4 65 Odesláno: 11. Březen, 2018 Author Share Odesláno: 11. Březen, 2018 před 14 minutami, ATomas said: Tak pokud ten sa-mp furt jede na jednom vlakne. Tak me vykon celkem zajima Hlavne se strimgem pracujes kazdou chvili. Ale jako jo mas pravdu na serveru s 20ti lidma moc vykon asi resit nebudes Beze všeho, zde máš jedno měření ekvivalentních operací pomocí polí a pomocí PawnPlus: public OnFilterScriptInit() { new start, top = 10000; start = GetTickCount(); new buf[128]; new val[16]; for(new i = 0; i < top; i++) { buf[0] = 0; for(new j = 0; j < 10; j++) { valstr(val, j); strcat(buf, val); } } printf("%d", GetTickCount()-start); start = GetTickCount(); new String:str = str_new(""); for(new i = 0; i < top; i++) { str_clear(str); for(new j = 0; j < 10; j++) { str_append(str, str_int(j)); } } printf("%d", GetTickCount()-start); } 429 350 424 372 334 348 385 330 364 426 401 378.4545 50 49 50 48 48 50 49 48 48 49 49 48.90909 Link to comment Share on other sites More sharing options...
xhunterx 55 Odesláno: 11. Březen, 2018 Share Odesláno: 11. Březen, 2018 (upraveno) před 5 hodinami, IllidanS4 said: 429 350 424 372 334 348 385 330 364 426 401 378.4545 50 49 50 48 48 50 49 48 48 49 49 48.90909 Nechapem moc tym vysledkom, fakt by to chcelo popisat viac Prve je normal, druhe je tvoje? Taktiez je to vela spusteni FS alebo o co ide? A taktiez treba povedat, ze hento neni zrovna bezne pouzitie toho tvojho kodu... Je dost videt, ze hento ti cisla nahrava podla mna. PS: Ja osobne by som to postavil proti formatu s 10 %i. Edited 11. Březen, 2018 by xhunterx Link to comment Share on other sites More sharing options...
IllidanS4 65 Odesláno: 11. Březen, 2018 Author Share Odesláno: 11. Březen, 2018 (upraveno) První řádek horní polovina kódu, druhý řádek spodní polovina. Poslední sloupec je průměr. Stačí říct a udělám měření pro jiný typ operace. Edited 11. Březen, 2018 by IllidanS4 Link to comment Share on other sites More sharing options...
xhunterx 55 Odesláno: 12. Březen, 2018 Share Odesláno: 12. Březen, 2018 (upraveno) public OnFilterScriptInit() { new start, top = 10000; start = GetTickCount(); new buf[128]; new val[16]; for(new i = 0; i < top; i++) { buf[0] = 0; format(buf, sizeof(buf), "%s %s bol vyhodeny %s %s na %ih, %im a tiez %is.", "Hrac", "ATomas", "hracom", "xhunterx", 6477, 34, 12); } printf("%d", GetTickCount()-start); start = GetTickCount(); new String:str = str_new(""); for(new i = 0; i < top; i++) { str_clear(str); str = str_new("Hrac") + str_new(" ") + str_new("xhunterx") + str_new(" bol vyhodeny ") + str_new("hracom") + str_new(" ") + str_new("xhunterx") + str_new(" na ") + str_int(6477) + str_new("h, ") + str_int(34) + str_new("m a tiez ") + str_int(12) + str_new("s,"); } printf("%d", GetTickCount()-start); } Skus toto, myslim, ze takto nejak sa to ma pouzivat? Je to sice asi jeden z najhorsich pripadov, ale zaujima ma, jak to bude vyzerat v takejto situacii. Ak to zvladne toto tak to je asi fajn Taktiez jak na to pozeram je to strasne neprehladne mozno by si mal spravit nejake makro alebo neco PS: Ak nechapes preco su tie mena a to Hrac zvlast v stringu, snazim sa simulovat situaciiu, ako pouzivam u seba, ze to je vratene z nejakej funkcie, napr Nick(playerid), Hodnost(playerid). Edited 12. Březen, 2018 by xhunterx Link to comment Share on other sites More sharing options...
IllidanS4 65 Odesláno: 12. Březen, 2018 Author Share Odesláno: 12. Březen, 2018 Tenhle kód momentálně nelze takto porovnat, protože format zatím nemá přímou alternativu. Tu ale plánuji přidat. Testoval jsem to s str_append (to je lepší, nevytváří tolik řetězců) a tam je sice PawnPlus mnohem pomalejší, ale je to taky kvůli mnohem častějším voláním nativních funkcí, a navíc ty kódy nejsou ekvivalentní, i kdyby stačilo jednou zavolat (hypotetické) str_format, neboť v prvním případě se řetězec "ATomas" nemusí vytvářet vůbec. Link to comment Share on other sites More sharing options...
xhunterx 55 Odesláno: 13. Březen, 2018 Share Odesláno: 13. Březen, 2018 (upraveno) před 16 hodinami, IllidanS4 said: Tenhle kód momentálně nelze takto porovnat, protože format zatím nemá přímou alternativu. Tu ale plánuji přidat. Testoval jsem to s str_append (to je lepší, nevytváří tolik řetězců) a tam je sice PawnPlus mnohem pomalejší, ale je to taky kvůli mnohem častějším voláním nativních funkcí, a navíc ty kódy nejsou ekvivalentní, i kdyby stačilo jednou zavolat (hypotetické) str_format, neboť v prvním případě se řetězec "ATomas" nemusí vytvářet vůbec. On 8. 3. 2018 at 0:25, IllidanS4 said: new String:msg = str_new("Hráč ")+name+str_new(" (")+str_val(playerid)+str_new(") se připojil na server."); Tak hned tvoj prvy priklad toto pouziva ako ekvivalent formatu. A ano, bolo mi jasne, ze toto bude pomalsie, nicmenej preto som to napisal. Lebo som vedel, ze toto bude velmi zle na zlozitost a zaujimalo ma ako moc zle. A aj ked priznavam, ze moj kod je asi najhorsi realny pripad, tvoj kod co testujes je pravdepodobne najlepsi pripad pre teba. Ako kde v praxy by si pouzival nieco hentake? Zretazit 10 cisel? Uprimne jedina vec, co ma zatial naozaj zaujala na tomto tvojom plugine (string casti) je to, ze by public funkcie mohli returnovat stringy... Edited 13. Březen, 2018 by xhunterx Link to comment Share on other sites More sharing options...
IllidanS4 65 Odesláno: 13. Březen, 2018 Author Share Odesláno: 13. Březen, 2018 Má to jisté výhody, má to jisté nevýhody. Mojí počáteční prioritou byla jednoduchost používání; věci jako alokace a časté používání nativních funkcí mohou kód snadno zpomalit. Tvůj příklad dával dobu trvání v poměru 1 : 50 pro klasický kód, ale to bylo z důvodu dvou věcí, které jsem už zmínil. Jinak v nové verzi bude podpora pro buffery, což znamená, že ani funkce jako GetPlayerName nemusí ukládat výsledek do mezipole, nýbrž rovnou do dynamického řetězce. Při snížení počtu volání a alokací se kód využívající plugin přiblíží rychlostí ke klasickému, a pokud vytvořím lepší str_format, bude i rychlejší. Link to comment Share on other sites More sharing options...
IllidanS4 65 Odesláno: 14. Březen, 2018 Author Share Odesláno: 14. Březen, 2018 On 13. 3. 2018 at 10:49, xhunterx said: Tak hned tvoj prvy priklad toto pouziva ako ekvivalent formatu. A ano, bolo mi jasne, ze toto bude pomalsie, nicmenej preto som to napisal. Lebo som vedel, ze toto bude velmi zle na zlozitost a zaujimalo ma ako moc zle. A aj ked priznavam, ze moj kod je asi najhorsi realny pripad, tvoj kod co testujes je pravdepodobne najlepsi pripad pre teba. Ako kde v praxy by si pouzival nieco hentake? Zretazit 10 cisel? Uprimne jedina vec, co ma zatial naozaj zaujala na tomto tvojom plugine (string casti) je to, ze by public funkcie mohli returnovat stringy... S využitím nové funkce str_format a podpory bufferů: native GetConsoleVarAsStringStr(const varname[], AmxStringBuffer:buffer, len) = GetConsoleVarAsString; public OnFilterScriptInit() { new start, top = 100000; start = GetTickCount(); new buf[128]; new val[32]; for(new i = 0; i < top; i++) { GetConsoleVarAsString("rcon_password", val, 32); format(buf, sizeof buf, "rcon_password=%s", val); } printf("%d", GetTickCount()-start); start = GetTickCount(); new String:str = str_new(""); new String:strbuf = str_new(""); for(new i = 0; i < top; i++) { str_resize(strbuf, 32); str_resize(strbuf, GetConsoleVarAsStringStr("rcon_password", strbuf, 32)); str_set_format(str, "rcon_password=%S", strbuf); } printf("%d", GetTickCount()-start); } Dolní kód běží 1,5× pomaleji, což zase není taková velká ztráta. Navíc pokud by chtěl někdo použít tuto funkci pro formátování řetězců do MySQL/SQLite (%q už jde), řekl bych, že 5 nanosekund na formátování nikomu vadit nebude, zvlášť když odpadá nutnost odhadovat velikost výstupního bufferu. To považuji za mnohem větší výhodu. Když jsme u toho MySQL, už jsem ověřil, že pomocí asynchronního programování se dají dělat asynchronní dotazy na MySQL bez nutnosti všelijakých veřejných funkcí a předávání dočasných parametrů: stock task:mysql_pquery_task(MySQL:handle, const query[]) { new task:t = task_new(); mysql_pquery(handle, query, #OnQueryTaskCompleted, "i", t); return t; } forward OnQueryTaskCompleted(task:task); public OnQueryTaskCompleted(task:task) { task_set_result(task, 0); } public OnMySQLConnected() { await mysql_pquery_task(db, "SELECT COUNT(*) AS `count` FROM `characters`"); new count; cache_get_value_name_int(0, "count", count); printf("%i", count); return 1; } Link to comment Share on other sites More sharing options...
xhunterx 55 Odesláno: 15. Březen, 2018 Share Odesláno: 15. Březen, 2018 (upraveno) před 20 hodinami, IllidanS4 said: S využitím nové funkce str_format a podpory bufferů: Takze sme upustili od tej syntaxe s + a vlastne sa len zbavujeme velkosti hej? Aspom by si mohol spravit returnujuci format: String:str_rformat(fmt[], ...); Ako suhlasim s tym, ze 1.5x pomalsi format nicomu moc neublizi, len fakt moc nevidim tu utilitu, zvlast ked syntax mi pride zlozitejsia nez original s tym str_new a podobne... Edited 15. Březen, 2018 by xhunterx Link to comment Share on other sites More sharing options...
IllidanS4 65 Odesláno: 15. Březen, 2018 Author Share Odesláno: 15. Březen, 2018 (upraveno) před 47 minutami, xhunterx said: Takze sme upustili od tej syntaxe s + a vlastne sa len zbavujeme velkosti hej? Aspom by si mohol spravit returnujuci format: String:str_rformat(fmt[], ...); Ako suhlasim s tym, ze 1.5x pomalsi format nicomu moc neublizi, len fakt moc nevidim tu utilitu, zvlast ked syntax mi pride zlozitejsia nez original. Taková funkce už existuje - str_format, jež je plánována na 0.2. Spojování řetězců (pomocí + a od 0.1.1 i pomocí %) je stále přítomno a ideální v případě, máme-li po ruce více dynamických řetězců. Výhodna je značná - možnost transportu (SetTimerEx) a práce s řetězci napříč skripty, odstranění nutnosti počítat při každé operaci s velikostí cílového bufferu, nezávislost na konkrétním skriptu a odstranění obecných nároků na alokaci v paměti skriptu. Už jen pole řetězců typu [MAX_PLAYERS][128] ti, pokud nepoužíváš PVars, přidá 50 KB velikosti samotného kompilátu. V kombinaci s jinými pluginy, které taky umožňují ukládat buňky (např. seznamy, EXTRA_DATA v streameru) je dynamická externí alokace jediná možnost, jak v takových strukturách ukládat liboovolné řetězce. Jediná alternativa je udělat si pole, kam budeš házet očíslované řetězce, ale pak už není moc velký rozdíl v tom, když použiješ tohle. Navíc dostaneš garbage collector a obecně rychlejší funkce na manipulaci. Absolutní rychlost je jedna věc, ale vezmi si, že všechny funkce standardní knihovny pro řetězce Pawnu musejí počítat velikost vstupu. Pak ti strlen běží lineárně, protože pokaždé hledá nulový znak, zatímco str_len má složitost konstantní. Syntaxe je taková, s jakou by ses setkal v každé knihovně pro lepší práci s řetězci. Pro str_new existuje alias @ - (str_new("x") == @("x")) a pro str_val se dá zapnout alias @@. Pawn bohužel objektový přístup nenabízí, takže místo (efektnějšího) zápisu str.delete(0, 2) musí být str_delete(str, 0, 2). Nevím, jak moc "jednodušší" by sis ji představoval. Koneckonců, namísto !strcmp(a, b ) můžeš používat a == b. To je vcelku velké zjednodušení, nemyslíš? Nevím, s kolika dalšími jazyky máš zkušenost, ale jen si vezmi, jak se to dělá v C++: std::string str1 = "Hello world"; size_t len = str1.size(); std::string half1 = str1.substr(0, len/2); std::string half2 = str1.substr(len/2); assert(half1+half2 == str1); V Pawnu s tímto pluginem (a v moderních jazycích jako je C#, Java a v podstatě i Lua) to je naprostá analogie: new String:str1 = @("Hello world"); new len = str_len(str1); new String:half1 = str_sub(str1, 0, len/2); new String:half2 = str_sub(str1, len/2); assert(half1+half2 == str1); A teď to srovnej s klasickým Pawnem: new str1[] = "Hello world"; new len = strlen(str1); //chytřeji sizeof(str1)-1 new half1[sizeof(str1)/2+1], half2[sizeof(str1)/2+1]; strmid(half1, str1, 0, len/2); strmid(half2, str1, len/2, len); new str2[sizeof(half1)+sizeof(half2)]; strcat(str2, half1); strcat(str2, half2); assert(!strcmp(str1, str2)); Takhle se to dělalo naposledy v C. A narozdíl od C je Pawn "bezpečnější", takže každá alokace v new ti zároveň tu paměť vynuluje. To je naprosto zbytečné, když tam hned následně zapisuji. Edited 15. Březen, 2018 by IllidanS4 Link to comment Share on other sites More sharing options...
xhunterx 55 Odesláno: 15. Březen, 2018 Share Odesláno: 15. Březen, 2018 (upraveno) před 3 hodinami, IllidanS4 said: Taková funkce už existuje - str_format, jež je plánována na 0.2. Super před 3 hodinami, IllidanS4 said: Spojování řetězců (pomocí + a od 0.1.1 i pomocí %) je stále přítomno a ideální v případě, máme-li po ruce více dynamických řetězců. Tomuto nechapem, ved moj benchmark bol o spojovani dynamickych retazcov. Pointa bola, ze veci ako meno mali byt vratene funkciou napr String:Name(playerid). před 3 hodinami, IllidanS4 said: odstranění obecných nároků na alokaci v paměti skriptu. Už jen pole řetězců typu [MAX_PLAYERS][128] ti, pokud nepoužíváš PVars, přidá 50 KB velikosti samotného kompilátu. To je velmi pekne v teorii. Lenze tvoj pristup pravdepodobne sposoby vazne problemy s fragmentaciou pamate. Takze v konecnom dosledku mozes zabrat ovela viac RAM nez keby si pouzil staticke polia, zvlast ked ide o lokalne premenne. před 3 hodinami, IllidanS4 said: Navíc dostaneš garbage collector a obecně rychlejší funkce na manipulaci. Tymto som fakt neni presvedceny z tvojich benchmarkov. Napriklad co veci ako str[i] . Podobne veci budes muset robit cez nejaky native nehovoriac o tom, ked rozdelujes polia. V tvojom priklade pouzivas strmid ale tu funkciu som ja nepouzil uz roky. Ovela jednoduchsie je pouzit str[len/2] a pripadne tam docasne dat null terminator. Takze by som argumentoval, ze vo vela pripadoch tvoje veci budu pomalsie. před 3 hodinami, IllidanS4 said: Absolutní rychlost je jedna věc, ale vezmi si, že všechny funkce standardní knihovny pro řetězce Pawnu musejí počítat velikost vstupu. Pak ti strlen běží lineárně, protože pokaždé hledá nulový znak, zatímco str_len má složitost konstantní. Ukladanie velkosti verzus pocitanie je stara debata, ktoru asi nevyriesime tu. Ale urcite to nieje vseobecna vyhoda, pretoze tu dlzku musis pocitat pri zmenach, coz moze byt pomalsie nez ju spocitat az ked ju potrebujes. Taktiez vacsina funkcii ako strcat by nemali priamo volat strlen ale zistit dlzku pri praci, napr while(str[i++] != '\0') před 3 hodinami, IllidanS4 said: Výhodna je značná - možnost transportu (SetTimerEx) a práce s řetězci napříč skripty Uznavam, toto je velka vyhoda, ale pride mi, ze je jedina. Edited 15. Březen, 2018 by xhunterx Link to comment Share on other sites More sharing options...
IllidanS4 65 Odesláno: 15. Březen, 2018 Author Share Odesláno: 15. Březen, 2018 před 5 hodinami, xhunterx said: Tomuto nechapem, ved moj benchmark bol o spojovani dynamickych retazcov. Pointa bola, ze veci ako meno mali byt vratene funkciou napr String:Name(playerid). Špatně jsem se vyjádřil, ale je rozdíl v tom, zdali chceš naformátovat řetězec podle nějakého vzoru, či zdali chceš jen spojit dvě věci dohromady. Většina jazyků má podporu pro oboje. před 7 hodinami, xhunterx said: To je velmi pekne v teorii. Lenze tvoj pristup pravdepodobne sposoby vazne problemy s fragmentaciou pamate. Takze v konecnom dosledku mozes zabrat ovela viac RAM nez keby si pouzil staticke polia, zvlast ked ide o lokalne premenne. Uznávám, že momentálně nemám řetězce uložené nejlepším způsobem (protože jsem ještě nedodělal stable_vector), ale to argumentuješ proti všem jazykům, které mají dynamicky alokované řetězce. Předpokládám, že C++ alokuje nějak rozumně. new start, top = 1000000; new buf[] = "aaaaaaaaaaabbbbbbbbbbbbccccccccccccccc"; new String:str1 = str_new(buf); start = GetTickCount(); for(new i = 0; i < top; i++) { buf[i % 38] = buf[37 - i % 38]; } printf("%d", GetTickCount()-start); start = GetTickCount(); for(new i = 0; i < top; i++) { str_setc(str1, i % 38, str_getc(str1, 37 - i % 38)); } printf("%d", GetTickCount()-start); Horní 348,78 ms, dolní 520,11 ms, tedy zhruba 1,5× pomalejší, což na volání nativní funkce není špatné. AMX ti přímou adresaci paměti nedovolí, takže bez nativní funkce se to neobejde. před 7 hodinami, xhunterx said: Ukladanie velkosti verzus pocitanie je stara debata, ktoru asi nevyriesime tu. Ale urcite to nieje vseobecna vyhoda, pretoze tu dlzku musis pocitat pri zmenach, coz moze byt pomalsie nez ju spocitat az ked ju potrebujes. Taktiez vacsina funkcii ako strcat by nemali priamo volat strlen ale zistit dlzku pri praci Souhlasím, ale chtěj po většině pawnerů, aby přestali používat for(new i = 0; i < strlen(s); i++). Taky jsem párkrát využil chytré dělení řetězce tímto způsobem (při zalamování textu a rozdělování podle konců řádků) a je fakt, že když je dobrý algoritmus, nepotřebuje to žádnou alokaci navíc. Ale takovýto kód je nízkoúrovňový, zdlouhavý na vytvoření a netriviální pro ne příliš pokročilé kodéry. Abych měl taky příklad "z reálného světa", kde by se mi toto osvědčilo, použil jsem část svého módu, konkrétně tu, kde se vytvářejí vlastní příkazy (což mohou admini). Registruje se alias příkazu a reálný příkaz, který se místo něj vykoná. #define ALIAS_CMD_LENGTH 129 enum CMD_ALIAS_ARRAY { CMD_ALIAS_ARRAY_NEW[ALIAS_CMD_LENGTH], CMD_ALIAS_ARRAY_REAL[ALIAS_CMD_LENGTH] }; static commands_arr[60][CMD_ALIAS_ARRAY]; stock TryCustomCommandArray(cmdtext[]) { for(new i = 0; i < sizeof commands_arr; i++) { if(commands_arr[i][CMD_ALIAS_ARRAY_NEW][0]) { if(!strcmp(cmdtext, commands_arr[i][CMD_ALIAS_ARRAY_NEW])) { //print(commands_arr[i][CMD_ALIAS_ARRAY_REAL]); return true; } } } return false; } stock CreateCustomCommandArray(newcmd[], realcmd[]) { for(new i = 0; i < sizeof commands_arr; i++) { if(commands_arr[i][CMD_ALIAS_ARRAY_NEW][0] && !strcmp(commands_arr[i][CMD_ALIAS_ARRAY_NEW], newcmd)) { commands_arr[i][CMD_ALIAS_ARRAY_NEW][0] = '\0'; } if(!commands_arr[i][CMD_ALIAS_ARRAY_NEW][0]) { commands_arr[i][CMD_ALIAS_ARRAY_REAL] = '\0'; strcat(commands_arr[i][CMD_ALIAS_ARRAY_NEW], newcmd, ALIAS_CMD_LENGTH); strcat(commands_arr[i][CMD_ALIAS_ARRAY_REAL], realcmd, ALIAS_CMD_LENGTH); return true; } } return false; } stock RemoveCustomCommandArray(command[]) { for(new i = 0; i < sizeof commands_arr; i++) { if(commands_arr[i][CMD_ALIAS_ARRAY_NEW][0] && !strcmp(commands_arr[i][CMD_ALIAS_ARRAY_NEW], command)) { commands_arr[i][CMD_ALIAS_ARRAY_NEW][0] = '\0'; return true; } } return false; } enum CMD_ALIAS_STR { GlobalString:CMD_ALIAS_STR_NEW, GlobalString:CMD_ALIAS_STR_REAL }; static commands_str[60][CMD_ALIAS_STR]; stock TryCustomCommandStr(String:cmdtext) { for(new i = 0; i < sizeof commands_str; i++) { if(commands_str[i][CMD_ALIAS_STR_NEW] && commands_str[i][CMD_ALIAS_STR_NEW] == cmdtext) { //print_s(commands_str[i][CMD_ALIAS_STR_REAL]); return true; } } return false; } stock CreateCustomCommandStr(String:newcmd, String:realcmd) { for(new i = 0; i < sizeof commands_str; i++) { if(commands_str[i][CMD_ALIAS_STR_NEW] && commands_str[i][CMD_ALIAS_STR_NEW] == newcmd) { commands_str[i][CMD_ALIAS_STR_REAL] = realcmd; return true; } if(!commands_str[i][CMD_ALIAS_STR_NEW]) { commands_str[i][CMD_ALIAS_STR_NEW] = newcmd; commands_str[i][CMD_ALIAS_STR_REAL] = realcmd; return true; } } return false; } stock RemoveCustomCommandStr(String:command) { for(new i = 0; i < sizeof commands_str; i++) { if(commands_str[i][CMD_ALIAS_STR_NEW] && commands_str[i][CMD_ALIAS_STR_NEW] == command) { //str_free(commands_str[i][CMD_ALIAS_STR_NEW]); commands_str[i][CMD_ALIAS_STR_NEW] = STRING_NULL; return true; } } return false; } public OnFilterScriptInit() { new start, top = 10000; start = GetTickCount(); for(new i = 0; i < top; i++) { CreateCustomCommandArray("/acmd", "/real_command1"); CreateCustomCommandArray("/bcmd", "/real_command2"); CreateCustomCommandArray("/ccmd", "/real_command3"); CreateCustomCommandArray("/dcmd", "/real_command4"); TryCustomCommandArray("/acmd"); TryCustomCommandArray("/bcmd"); TryCustomCommandArray("/ccmd"); TryCustomCommandArray("/dcmd"); RemoveCustomCommandArray("/acmd"); RemoveCustomCommandArray("/bcmd"); RemoveCustomCommandArray("/ccmd"); RemoveCustomCommandArray("/dcmd"); } printf("%d", GetTickCount()-start); start = GetTickCount(); new String:acmd = @("/acmd"); new String:acmd2 = @("/acmd"); new String:bcmd = @("/bcmd"); new String:bcmd2 = @("/bcmd"); new String:ccmd = @("/ccmd"); new String:ccmd2 = @("/ccmd"); new String:dcmd = @("/dcmd"); new String:dcmd2 = @("/dcmd"); new String:real_command1 = @("/real_command1"); new String:real_command2 = @("/real_command2"); new String:real_command3 = @("/real_command3"); new String:real_command4 = @("/real_command4"); for(new i = 0; i < top; i++) { CreateCustomCommandStr(acmd, real_command1); CreateCustomCommandStr(bcmd, real_command2); CreateCustomCommandStr(ccmd, real_command3); CreateCustomCommandStr(dcmd, real_command4); TryCustomCommandStr(acmd2); TryCustomCommandStr(bcmd2); TryCustomCommandStr(ccmd2); TryCustomCommandStr(dcmd2); RemoveCustomCommandStr(acmd2); RemoveCustomCommandStr(bcmd2); RemoveCustomCommandStr(ccmd2); RemoveCustomCommandStr(dcmd2); } printf("%d", GetTickCount()-start); } Horní 812 ms, dolní 153 ms, tedy zhruba 5× rychlejší. Řetězce jsem si alokoval napřed, protože normální pole jsou taky alokované jenom jednou, a dvakrát jsou tam proto, aby to náhodou neporovnávalo podle reference. Možná bys mohl namítnout, že hašovací tabulka by to urychlila, ale i při jednom záznamu v poli je porovnávání v pluginu 10× rychlejší. Link to comment Share on other sites More sharing options...
xhunterx 55 Odesláno: 15. Březen, 2018 Share Odesláno: 15. Březen, 2018 (upraveno) před 10 minutami, IllidanS4 said: Horní 348,78 ms, dolní 520,11 ms, tedy zhruba 1,5× pomalejší, což na volání nativní funkce není špatné. AMX ti přímou adresaci paměti nedovolí, takže bez nativní funkce se to neobejde. To je celkom rozsireny mytus. V sampe nieje problem adresovat akukolvek pamat celeho procesu, ak si ochotny pouzivat #emit alebo v novej verzii __emit() před 10 minutami, IllidanS4 said: Horní 812 ms, dolní 153 ms, tedy zhruba 5× rychlejší. Řetězce jsem si alokoval napřed, protože normální pole jsou taky alokované jenom jednou, a dvakrát jsou tam proto, aby to náhodou neporovnávalo podle reference. Možná bys mohl namítnout, že hašovací tabulka by to urychlila, ale i při jednom záznamu v poli je porovnávání v pluginu 10× rychlejší. Zaujimave. Skoda ze nieje videt, ktora cast tak dlho trva, ci to vytvaranie alebo vyhladavanie. Lebo v praxi ta zaujima iba TryCustomCommand, nie pridavanie, co sa stane len pri nacitani FS Ale teda ono je to dost zaujimave tieto rychlosti, ze asi naozaj pojde o use-case. Len teda to sa blbo obhajuje, ono vacsinou ludi ako ATomasa a uprimne aj mna zaujima skorej najhorsi pripad. Pretoze ak su sytuacie, v ktorych je tvoj plugin podstatne pomalsi, tak to podstatne spomalenie moze sposobovat lagy, koly jednojadrovosti sampu. PS: Cital som viac o taskoch a tento plugin je urcite nekompatibilny s mojim, kedze ja miesto toho aby som pokazde volal amx_FindPublic si to zavolam len raz vzdy. Edited 15. Březen, 2018 by xhunterx Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now