Dobar kompajler bi takve sitnice trebal optimizirati sam.
Random brojevi u C# nisu nimalo random
- poruka: 99
- |
- čitano: 19.977
- |
- moderatori:
Lazarus Long, XXX-Man, vincimus
Dobro, pustite sad C++.
No ako alokacije zauzimaju previše resursa, onda imam još jedno pitanje: dali je ok kad imam neku vrijednost do koje se teže dođe, dakle puno djeljenja, množenja, pozivanja metoda itd, razbiti tu vrijednost u manje variable, tako da ta kobasica od formule za vrijednost bude ljepo čitljiva na jeziku razumljivom običnom čovjeku? Mislim, ovo je samo primjer, no općenito, male apstrakcije kao umjesto da pišeš x / 2 imaš variablu polaX koja ima vrjednost x / 2. Onda netreba više razmišljati onih pola stotinke što je x / 2, nego samo čitaš. Ja to uvjek radim, no moglo bi biti resource heavy. Što vi kažete?
Alokacije ne zauzimaju previse resursa, nego su u usporedbi sa svime ostalim najcesci izvor problema. Alokacija je 50% programiranja - ostalih 50% je funkcija (tj. algoritam).
Dakle, nije stvar u tome da izbjegavas alokacije, nego je stvar u tome da ih radis pametno.
Ovaj tvoj primjer definitivno nije los, iako je podosta neuobicajen. Ja sam neko vrijeme koristio inline funkcije za tu namjenu, primjerice kad bi mi 'if' bio boolean-heavy, extractao bi ga u metodu sa jasnim nazivom.
No s vremenom dodjes u stanje u kojem to ne moras raditi, jer postanes dovoljno dobar u arhitekturi da ti je kod sam po sebi jednostavan i citljiv.
Inace, savjetujem ti da probas umjesto alokacija varijabli koristiti komentare. Domagoj je dao odlican odgovor, no devil is in the details. Iako bi compiler _trebao_ to moci optimizirati sam, zacudio bi se na kojim se sve glupostima compileri predaju i naprave glupost.
Jedan od meni dragih primjera je sveopce rasiren MS compiler koji odustane od unrollanja for loopa nakon 4 iteracije.
Jedan od meni dragih primjera je sveopce rasiren MS compiler koji odustane od unrollanja for loopa nakon 4 iteracije.
Bahaha. Zbilja? Odustane od unwindanja nakon 4 iteracije? A mozda je i to optimizacija? xD Posto je loop unrolling sam po sebi memory-time tradeoff. :D
Bahaha. Zbilja? Odustane od unwindanja nakon 4 iteracije? A mozda je i to optimizacija? xD Posto je loop unrolling sam po sebi memory-time tradeoff. :D
Ovisno o tome kakav mu je loop i kakav ti je for-uvjet, nekad krelac nece unrollati ni najjednostavnije stvari kao sto je punjenje arraya :-)
GCC/MinGW imaju svojih musica ali MSC mi je kretenast do kraja.
Loop se obicno compilira u jne koji nije jeftin jer minimalno unistava prefetch. Zbog toga prakticno ni 'if'-ovi nisu dobri ako bas hoces biti analan i iskoristiti maksimum, a ovo je bitno kod operacija nad linearnim podacima koje si prethodno pospremio u cache, naravno ako si ih pametno alocirao.
Primjerice, imas klasu za vektor i klasu za matricu. Napisat ces alokator koji ce sve alocirane matrice pospremiti u jedan dio memorije a sve vektore u drugi. Recimo da imas array pozicija vertexa koje zelis pomnoziti sa modelview matricom. Kad krene mnozenje, onda prvo matrica uleti u cache i tamo ostaje jer sve mnozis s njom, a vektori se prefetchaju ovisno o operacijama koje slijede, tako da CPU ne ceka na podatke nego koristi svaki cycle koji ima za mnozenje jer je L1 cache pun podataka koji su potrebni.
Ako u tu pricu uvalis jmp/je/jne - dovidjenja. Jer procesor ne moze skuziti sto da prefetcha dok ne evaluira jump.
Naravno, ovo su sve low-end optimizacije koje na modernim PC-ima imaju smisla samo u funkcijama koje se izvrsavaju po nekoliko stotina tisuca puta u cycleu. Onda mozes ustedjedi po pola milisekunde do max cijele, ali 1ms za rendering engine je cijela vjecnost, i dobiti to sa jednostavnim unrollanjem loopa je blazenstvo - ako skuzis da te compiler zahebao, naravno! Prodjecni time budget za moderni 3D engine je oko 6, max 8 msec po cycleu.
Takodjer, ove stvari mogu biti bitne ako pises kod za mikrokontroller, no opet ovisi koji jer vecina mikrokontrolera mogu ocitavati stanje pinova svaki cycle, pa je prefetch nepostojeci jer je nepotreban.
Btw - pardon na offtopicu :-)
Dobro, pustite sad C++.
No ako alokacije zauzimaju previše resursa, onda imam još jedno pitanje: dali je ok kad imam neku vrijednost do koje se teže dođe, dakle puno djeljenja, množenja, pozivanja metoda itd, razbiti tu vrijednost u manje variable, tako da ta kobasica od formule za vrijednost bude ljepo čitljiva na jeziku razumljivom običnom čovjeku? Mislim, ovo je samo primjer, no općenito, male apstrakcije kao umjesto da pišeš x / 2 imaš variablu polaX koja ima vrjednost x / 2. Onda netreba više razmišljati onih pola stotinke što je x / 2, nego samo čitaš. Ja to uvjek radim, no moglo bi biti resource heavy. Što vi kažete?
Prema ovome boldanom mi se čini da ti pojam alokacije nije baš najjasniji.
Uglavnom alokacija predstavlja rezerviranje memorije u koju spremaš neki objekt, podatak ili što već, bez obzira kako je ta memorija rezervirana. Tako znači da deklariranje neke varijable(npr. int i;) isto predstavlja alokaciju, samo što se u tom slučaju sam kompajler brine o alociranju i oslobađanju te alocirane memorije, npr. kada izađeš iz bloka alocirani prostor se automatski oslobađa. Ta memorija se rezervira na dijelu memorije koji se naziva stack.
Drugi način je da ti sam alociraš memoriju operatorom new i u tom slučaju kompajler ne oslobađa memoriju prilikom izlaska iz bloka u kojemu je alociran, već za oslobađanje memorije si zadužen ti ili GC. Taj dio memorije se naziva heap.
Iz tvog početnog primjera:
Random randomizer = new Random();
Ovom linijom si ti napravio dvije alokacije a da toga nisi bio možda niti svjestan.
Prvo si deklarirao referencu randomizer i za nju je kompajler automatski alocirao memoriju na stacku. Zatim si opratorom new na heapu alocirano onoliko memorije koliko je potrebno za smještanje objekta tipa Random i dodijelio tu mem. adresu referenci randomizer.
I sada staviš tu liniju u petlju da se ponavlja, recimo, 20 puta i dešava se to da kompajler rezervira 20 mjesta za varijablu randomizer, a operator new rezervira 20 mjesta za objekt tipa Random. Kada se petlja završi i izađe iz bloka kompajler oslobodi 20 mjesta kojih je rezervirao na stacku za reference, ali onih 20 mjesta rezerviranh na heapu ostaje sve dok ih ne oslobodi GC.
Što se tiće tvog primjera, tu nema nekog univerzalonog pravila već sve zavisi od slučaja do slučaja, bitno je da znaš što si točno napravio i koliko će to imati utjecaja na program.
Konkretno u primjeru ako se to izvodi par puta tokom programa nema ništa loše u tome, izgubiti ćeš par baytova memorije i možda koji ciklus procesora ali to je u 99,999% slučajeva zanemarivo, a dobio si na čitljivosti koda. Ali da tu operaciju izvodiš nekoliko tisuća/miliona puta tada to možda ne bi bilo najpametnije.
Uglavnom, alokaciju nemožeš izbječi ona je sastavni dio programiranja, već je moraš dobro razumjeti i pametno koristiti.
Slike sve objasnjavaju.
http://www.hackforums.net/member.php?action=profile&uid=897694
EDIT: Necu se svadati sa djecom koja su dosla sa hackforums foruma
Ako ćemo biti iskreni, neda mi se brinuti o alokaciji. Bar ne trenutno. Milsim da još nikad nisam napisao petlju koja se vrti više nego desetak puta. Što se tiče mojeg primjera, mislim da ću nastaviti tako raditi, jer je bolje i za debugging i za čitanje i za razumjevanje.
Koliko malo znam o problemu govori i to da uopće neznam kako se oslobađa memorija zauzeta new operatorom. I inače mislio sam da pointer zauzima memoriju ali referenca ne? Opet, čini mi se da je u C++-u bilo tako. No u C++-u je trebalo referencu inicijalizirat odmah, kao const variablu. Nekužim zašto se to onda u C# jednastavno ne zove pointer a ne referenca. Ili ja opet nešto propuštam?
Slike sve objasnjavaju.
http://www.hackforums.net/member.php?action=profile&uid=897694
EDIT: Necu se svadati sa djecom koja su dosla sa hackforums foruma
U čemu je razlika u kodu između slika? Nemogu je pronaći.
Inače, ima li tvoj potpis ikakve veze samnom :)
Slike sve objasnjavaju.
http://www.hackforums.net/member.php?action=profile&uid=897694
EDIT: Necu se svadati sa djecom koja su dosla sa hackforums foruma
U čemu je razlika u kodu između slika? Nemogu je pronaći.
Inače, ima li tvoj potpis ikakve veze samnom :)
Pogledaj taskmanager i uocitit ces razliku.
Ne, potpis nema veze sa tobom
Nekužim zašto se to onda u C# jednastavno ne zove pointer a ne referenca. Ili ja opet nešto propuštam?
Referenca je neka memorijska adresa koja se prebacuje uokolo kao vrijednost
dakle:
&nesto;
cim ti tu adresu (referencu) strpas u neku varijablu dobivas pointer na tu varijablu:
int a, *b;
b=&a;
referenca
pointer
Mozes to ovako gledati, pointer je varijabla koja u sebi drzi referencu (adresu) na neki komad memorije.
Slike sve objasnjavaju.
http://www.hackforums.net/member.php?action=profile&uid=897694
EDIT: Necu se svadati sa djecom koja su dosla sa hackforums foruma
U čemu je razlika u kodu između slika? Nemogu je pronaći.
Inače, ima li tvoj potpis ikakve veze samnom :)
Pogledaj taskmanager i uocitit ces razliku.
Ne, potpis nema veze sa tobom
Ma shvatio sam tu razliku! Nego ne kužim kako do nje dolazi! U kodu nema razlike. Ili to ima veze s temom iz hackforumsa koju si postao za koju moram biti registriran da je otvorim?
Nekužim zašto se to onda u C# jednastavno ne zove pointer a ne referenca. Ili ja opet nešto propuštam?
Referenca je neka memorijska adresa koja se prebacuje uokolo kao vrijednost
dakle:
&nesto;
cim ti tu adresu (referencu) strpas u neku varijablu dobivas pointer na tu varijablu:
int a, *b;
b=&a;
referenca
pointer
Mozes to ovako gledati, pointer je varijabla koja u sebi drzi referencu (adresu) na neki komad memorije.
Dakle:
Random randomizer;
Je u biti pointer?
Ako ćemo biti iskreni, neda mi se brinuti o alokaciji. Bar ne trenutno. Milsim da još nikad nisam napisao petlju koja se vrti više nego desetak puta. Što se tiče mojeg primjera, mislim da ću nastaviti tako raditi, jer je bolje i za debugging i za čitanje i za razumjevanje.
Koliko malo znam o problemu govori i to da uopće neznam kako se oslobađa memorija zauzeta new operatorom. I inače mislio sam da pointer zauzima memoriju ali referenca ne? Opet, čini mi se da je u C++-u bilo tako. No u C++-u je trebalo referencu inicijalizirat odmah, kao const variablu. Nekužim zašto se to onda u C# jednastavno ne zove pointer a ne referenca. Ili ja opet nešto propuštam?
Trebao bi brinuti o alokacijama, memory management je osnova programiranja, ali pri tome mislim da se ne moraš opterečivati s tim da nađeš rješenje koje će biti memorijski ili brzinski najbolje, već rješenje koje će biti najoptimalnije za tvoj problem.
Odnosno najbitnije je razumjeti, znači kada napišeš dio koda moraš točno razumjeti što si napravio, koje su loše strane tog rješenja i da li će te loše strane imati utjecaja na sam produkt odnosno program.
Ovo gore boldano neka te uopće ne zabrinjava, znam dosta ljudi koji primaju plaču kao programeri a te stvari im nisu baš najjasnije, tako da je za svaku pohvalu što želiš naučiti i priznaješ ako negdje pogriješiš, a pogotovo s obzirom na tvoje godine.
U C#, kada alocirš memoriju sa new ne oslobađaš je ručno već ostavljaš GC-u da to odradi u pozadini. GC se sam pokreće i ti nemaš utjecaja na njega(u biti možeš, ali bolje ne), za razliku od C++ koji nema garbage collector i za svako oslobađanje memorije si ti zadužen.
Svaka varijabla kada je deklariraš ona zauzima memoriju(mora biti negdje zapisana) bio to pokazivač, referenca, znakovna varijabla ili bilo što drugo.
Pointer i referenca su ti principu jedno te isto, ima malih razlika ali u biti to je to, očita razlika je u izgledu, referenca skriva svoje pravo lice i koristiš je kao običnu varijablu. Znači kod pointera moraš paziti da staviš operator dereferenciranja(*), ako ga zaboraviš dobiješ adresu na koju on pokazuje, što je uzrok mnogim greškama i frustacijama u C/C++.
C# je dizajniran da bi bio što jednostavniji i ograničio mogućnost pogreške i zbog toga u njemu nema pokazivača već samo reference.
Osnovini principi koji vrijede u C++, vrijede i u C# samo što je skriveno od korisnika, pa to ljudima izgleda skroz drugačije.
Random randomizer;
Je u biti pointer?
Da. Vidi ovo:
http://stackoverflow.com/questions/3590519/object-pointer-in-c
Ali nije mi jasno zasto ti je to vazno.
Nekužim zašto se to onda u C# jednastavno ne zove pointer a ne referenca. Ili ja opet nešto propuštam?
Referenca je neka memorijska adresa koja se prebacuje uokolo kao vrijednost
dakle:
&nesto;
cim ti tu adresu (referencu) strpas u neku varijablu dobivas pointer na tu varijablu:
int a, *b;
b=&a;
referenca
pointer
Mozes to ovako gledati, pointer je varijabla koja u sebi drzi referencu (adresu) na neki komad memorije.
Ovo i nije baš točno.
int a, *b; - pokazivač
int &c=a; - referenca.
b=&a;
Referencu koristiš kao c=5;, pokazivač *b=5; Ovim izrazima si varijable prvo preko reference promijenio vrijednost u 5, a zatim toj istoj varijabli promijenio vrijednost u 7, jer i pokazivač i referenca gledaju na istu mem. adresu(od varijable a).
Ovo što si označio žuto je točno, zeleno nije referenca već samo izraz kojim pokazivaču b dodjeljuješ adresu varijable a.
Do zabune dolazi jer znak & u C++ ima dvojako značenje. Prilikom deklaracje varijable npr. int &c označava da je ta varijabla referenca, a u izrazima sa pointerima se koristi kao adresni operator.
Dakle:
Random randomizer;
Je u biti pointer?
Nije Random je referenca, u C# nema pointera.
Ovo što si označio žuto je točno, zeleno nije referenca već samo izraz kojim pokazivaču b dodjeljuješ adresu varijable a.
nvm. ja sam ostao na C poimanju referenci. Potpuno sam zanemario da C++ ma drukcije definiran pojam reference.
http://publib.boulder.ibm.com/infocenter/lnxpcomp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8l.doc%2Flanguage%2Fref%2Fcplr233.htm
Mislio sam da je razlika u tome da pointer zauzima memoriju a ref ne. Ako je razlika (više-manje) samo operator '*' onda definitivno nije nimalo bitno.
Mislio sam da je razlika u tome da pointer zauzima memoriju a ref ne.
Uh bas si se uhvatio tog zauzeca memorije. Znas li koliko pointer na 32 bitnom procesoru zauzima memorije? 32 bita (4 bajta). Na 64 bitnom to se penje na cijelih 8 bajtova. Taj pointer ce jednako memorije zauzimati pokazivao on na blok memorije od 10, KB ili na blok memorije od 300 MB.
Referenca je memorijska adresa. Operatorom "&" dobivas adresu necega. Bilo bi malo nezgodno kada bi morao tipkati nesto poput 0xAF345D88.
nwm. ja sam ostao na C poimanju referenci. Potpuno sam zanemario da C++ ma drukcije definiran pojam reference.
http://publib.boulder.ibm.com/infocenter/lnxpcomp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8l.doc%2Flanguage%2Fref%2Fcplr233.ht
Ako varijabla u sebi drzi memorijsku adresu na komad memorije to je pointer, ne zanima me sto se to u C# zove referenca.
U principu referenca nije memorijska adresa, već vrsta varijable, kao što je to i pointer.
U C-u ne postoje reference, već samo pokazivači, i tamo & znak predstavlja samo unarni "address operator".
C++ poznaje i pokazivače i reference, koji se deklariraju i koriste različito i u C++ znak & predstavlja oznaku reference, ali se i koristi kao adresni operator u radu sa pokazivačima.
C# poznaje samo reference i tamo se znak & ne koristi u tom kontekstu.
Kao što sam već naveo pokazivači i reference(uC++) jako slični, ali se deklariraju različito, koriste različito i ne vrijede ista pravila za oba.
Ovo nije da te napadam, vjerujem da razumiješ kak' to sve funkcionira, samo dosta je bitno da se stvari zovu svojim imenom da ne bi bilo previše nesporazuma u komunikaciji.
Za one koje zanima:
Pokazivač(C i C++) je varijabla koja sadrži adresu memorijske lokacije.
Prilikom deklaracije, oznaka pokazivača je *, i ne mora biti inicijaliziran(npr. int *a;).
Pokazivaču se može dodijeliti i promijeniti vrijednost tokom izvođena programa adresnim operatorm &, npr. a = &b; a=&c;
Vrijednosti na koju pokazuje pokazivač se pristupa(i mjenja) operatorm dereferenciranja *, npr *a=5;
Referenca(C++) je varijabla koja sadrži adresu memorijske loakcije.
Prilikom deklaracije, oznaka reference je & i MORA biti inicijalizirana(npr. int &a=b;);
Prilikom izvođenja referenci se ne može promijeniti vrijednost, odnosno ne može se promijeniti adresa na koju ref. pokazuje.
Vrijednosti na koju pokazuje referenca se pristupa bez ikakvog operatora, npr a=5;
Ne postoje kao tip podatka, da, ali se i dalje proslijedivanje adresa varijabli putem adresnog operatora u C-u kolokvijalno naziva "pass by reference", iako to nije ista stvar kao u npr C++ koji ima bas definirane reference.
Sve pet. C# je daleko izvan moje zone djelovanja, mislio sam da je dovoljno slican da cu se moci provuci sa svojim objasnjenjima, ali ocito ne. Bolje da me ti ispravis sada, nego netko drugi kasnije :D
Se spustiš malo na zemlju i probaš naučiti nešto od ljudi koji x puta zanaju bolje od tebe, ako misliš da si neki bog, pokaži nam šta znaš - Vaši programerski radovi.
Se spustiš malo na zemlju i probaš naučiti nešto od ljudi koji x puta zanaju bolje od tebe, ako misliš da si neki bog, pokaži nam šta znaš - Vaši programerski radovi.
A sta bi ti dijete drago da vidis?
A sta bi ti dijete drago da vidis?
Jučer zamijenio dudu sa tipkovnicom, a danas sve oslovljava sa dijete........
A sta bi ti dijete drago da vidis?
Jučer zamijenio dudu sa tipkovnicom, a danas sve oslovljava sa dijete........
Vrati se na HackForums i odi zabavljaj se sa djecom.
Prijavljeno zbog trollanja
Vrati se na HackForums i odi zabavljaj se sa djecom.
Prijavljeno zbog trollanja
Vidim da ti taj forum spominješ skoro u svakom postu, jel' to možda neka indirektna reklama?
Slike sve objasnjavaju.
http://www.hackforums.net/member.php?action=profile&uid=897694
EDIT: Necu se svadati sa djecom koja su dosla sa hackforums foruma
Samo malo dijete bi i popušilo ove tvoje slike! Preko minute ti je proc na 100% i dalje stoji na 100% iako program samo čeka input za izaći. Na prvoj slici je program također završen i u istom stanju (i isti kod), a na memoriji nema peakova, procesor ima peakova, ali se ne može raspoznati od normalnih systemskih peakova. Zašto nisi stavio tab s resusima po procesima, aha da, jer bismo onda vidjeli na drugoj slici Prime 95 ili nešto slično zbog čega je procesor na 100%
Slike sve objasnjavaju.
http://www.hackforums.net/member.php?action=profile&uid=897694
EDIT: Necu se svadati sa djecom koja su dosla sa hackforums foruma
Samo malo dijete bi i popušilo ove tvoje slike! Preko minute ti je proc na 100% i dalje stoji na 100% iako program samo čeka input za izaći. Na prvoj slici je program također završen i u istom stanju (i isti kod), a na memoriji nema peakova, procesor ima peakova, ali se ne može raspoznati od normalnih systemskih peakova. Zašto nisi stavio tab s resusima po procesima, aha da, jer bismo onda vidjeli na drugoj slici Prime 95 ili nešto slično zbog čega je procesor na 100%
Prvo sam pokrenuo puno procesa da procesor bude 100% koristen.
Onda sam pokrenuo program i uslikao sliku.
Nakon toga sam zavrsio te programe i pokrenuo opet program.
Tesko za shvatiti?
Nemas blage veze o ćemu govoriš.
Dakle, program prokrečeš iz VB Expressa i to u Debug modu, uz to je na proces zakačen debugger i onda monitoring procesa radiš u task manageru.
Kao pravi profesionalac. BDW, ona trik na drugoj slici s 1,1,1,1,1.. je baš fora. Šteta što ga je nemoguće ponoviti.
Bahaha. Zbilja? Odustane od unwindanja nakon 4 iteracije? A mozda je i to optimizacija? xD Posto je loop unrolling sam po sebi memory-time tradeoff. :D
Ovisno o tome kakav mu je loop i kakav ti je for-uvjet, nekad krelac nece unrollati ni najjednostavnije stvari kao sto je punjenje arraya :-)
GCC/MinGW imaju svojih musica ali MSC mi je kretenast do kraja.
Loop se obicno compilira u jne koji nije jeftin jer minimalno unistava prefetch. Zbog toga prakticno ni 'if'-ovi nisu dobri ako bas hoces biti analan i iskoristiti maksimum, a ovo je bitno kod operacija nad linearnim podacima koje si prethodno pospremio u cache, naravno ako si ih pametno alocirao.
Primjerice, imas klasu za vektor i klasu za matricu. Napisat ces alokator koji ce sve alocirane matrice pospremiti u jedan dio memorije a sve vektore u drugi. Recimo da imas array pozicija vertexa koje zelis pomnoziti sa modelview matricom. Kad krene mnozenje, onda prvo matrica uleti u cache i tamo ostaje jer sve mnozis s njom, a vektori se prefetchaju ovisno o operacijama koje slijede, tako da CPU ne ceka na podatke nego koristi svaki cycle koji ima za mnozenje jer je L1 cache pun podataka koji su potrebni.
Ako u tu pricu uvalis jmp/je/jne - dovidjenja. Jer procesor ne moze skuziti sto da prefetcha dok ne evaluira jump.
Naravno, ovo su sve low-end optimizacije koje na modernim PC-ima imaju smisla samo u funkcijama koje se izvrsavaju po nekoliko stotina tisuca puta u cycleu. Onda mozes ustedjedi po pola milisekunde do max cijele, ali 1ms za rendering engine je cijela vjecnost, i dobiti to sa jednostavnim unrollanjem loopa je blazenstvo - ako skuzis da te compiler zahebao, naravno! Prodjecni time budget za moderni 3D engine je oko 6, max 8 msec po cycleu.
Takodjer, ove stvari mogu biti bitne ako pises kod za mikrokontroller, no opet ovisi koji jer vecina mikrokontrolera mogu ocitavati stanje pinova svaki cycle, pa je prefetch nepostojeci jer je nepotreban.
Btw - pardon na offtopicu :-)
Zanimljiv post al mi neke stvari nisu baš najjasnije 1) kako možeš znati dali je neka instrukcija prefetchana 2) ne poništavali se problem s jmp/je/jne s pipeliningom? Sorry, ako je moje pitanje glupo al taj low level ja zaj... za skužit.