Fórum Root.cz
Hlavní témata => Vývoj => Téma založeno: RAII 22. 02. 2014, 19:21:08
-
Zkoušel někdo z vás jazyk D? Jaké jsou dojmy? Já sem se na něj trošku koukal a teda nic moc. Straší tam GC. De sice vypnout, ale knihovna a některé language features pak moc dobře nefungujou. Plus, GC při zběru paměti freezne všechny vlákna (nevím jak sou na tom jiné GC, ale mám pocit že to de i bez freezu). Teda fakt netuším proč by člověk měl jít do D a ne třeba do javy ...
-
A proč do něho jdes?
-
GC při zběru paměti freezne všechny vlákna (nevím jak sou na tom jiné GC, ale mám pocit že to de i bez freezu).
Až se váš pocit změní v jistotu tím že najdete implementaci GC která nebude potřebovat "stop the world" neboli zastavení všech vláken tak nám prosím dejte vědět.
-
GC při zběru paměti freezne všechny vlákna (nevím jak sou na tom jiné GC, ale mám pocit že to de i bez freezu).
Až se váš pocit změní v jistotu tím že najdete implementaci GC která nebude potřebovat "stop the world" neboli zastavení všech vláken tak nám prosím dejte vědět.
Concurrent Garbage Collector
-
Zkoušel někdo z vás jazyk D? Jaké jsou dojmy? Já sem se na něj trošku koukal a teda nic moc. Straší tam GC. De sice vypnout, ale knihovna a některé language features pak moc dobře nefungujou. Plus, GC při zběru paměti freezne všechny vlákna (nevím jak sou na tom jiné GC, ale mám pocit že to de i bez freezu). Teda fakt netuším proč by člověk měl jít do D a ne třeba do javy ...
Nejlepší způsob GC je nevytvářet odpad (garbage), tj. alokovat vždy, když to jde, na zásobníku. D také umožňuje alokovat na haldě manuálně, takže lze použít chytré pointry apod. Běžná aplikace (nějaké GUI kolem databáze + trochu business logiky) by s GC neměla mít problem. High performance kód by měl používat zásobník a vlastní alokátor.
-
Běžná aplikace (nějaké GUI kolem databáze + trochu business logiky) by s GC neměla mít problem. High performance kód by měl používat zásobník a vlastní alokátor.
Nemáte nějaký link na to, jak tohle dělat v Javě (jde-li to vůbec)? Zrovna včera jsem se snažil něco najít, ale neúspěšně.
-
Běžná aplikace (nějaké GUI kolem databáze + trochu business logiky) by s GC neměla mít problem. High performance kód by měl používat zásobník a vlastní alokátor.
Nemáte nějaký link na to, jak tohle dělat v Javě (jde-li to vůbec)? Zrovna včera jsem se snažil něco najít, ale neúspěšně.
Nejde. Jedině snad alogovat pole/buffer a v něm si dělat vlastní správu paměti (to se někdy dělá, ale je to hodně nepraktické).
-
v jave nemate moznost vlastneho alokatora. neviete ani alokovat objekty na stacku. mozete skusit pouzit inu implementaciu gc (otazka je, preco to chcete)
http://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html
high performance kod v jave sa riesi inak
http://java.cz/article/cz-podcast-90-psani-low-latency-java-aplikaci
-
Běžná aplikace (nějaké GUI kolem databáze + trochu business logiky) by s GC neměla mít problem. High performance kód by měl používat zásobník a vlastní alokátor.
Nemáte nějaký link na to, jak tohle dělat v Javě (jde-li to vůbec)? Zrovna včera jsem se snažil něco najít, ale neúspěšně.
Java to takto neumí, ale zato má lepší GC a některé objekty dává na zásobník automaticky. Vzhledem k délce vývoje je správa paměti v Javě vysoce optimalizovaná. Kdyby to přesto nestačilo, tak snad jen nativní kód.
-
Pánové, já vás nechápu...proč se koukám na jazyk D? Sem zvědavý. Ano, vým, jde vytvořit gc bez freeznuti (reference counting). Ma však několik vad, třeba cyklicke datove struktury (jež se neuvolní). D má nějaký derivat mark and sweep (netuším jak maj přesně resenou fragmentaci). Takže, prosím, nenapadat me znalosti, pouze sem chtěl začít debatu. Pozoruji totiž že nabírá D na síle.
-
Díky všem za odkazy/komentáře. Píšu démona, které musí být schopen obsloužit požadavky do sekundy. A trochu se bojím toho, co se stane, až se spustí GC.
-
Co se stane?
To ti řeknu naprosto přesně, nestane se vůbec nic, protože bez OS, který zaručí, že tvůj program to řízení opravdu dostane, to nejsi schopný zajistit.
ALE pokud v programu uděláš kritickou sekci (critical section) tak by v jejím průběhu nemělo k podobnému excesu dojít, tedy pokud to vymýšlel někdo se špetkou rozumu.
Další metodou je zavolat GC sám, to vyprázdní čítač GC a nějakou dobu dá pokoj.
No a třeba v C# je už tahle možnost:
http://msdn.microsoft.com/en-us/library/system.runtime.gclatencymode.aspx
-
Díky všem za odkazy/komentáře. Píšu démona, které musí být schopen obsloužit požadavky do sekundy. A trochu se bojím toho, co se stane, až se spustí GC.
V D lze na chvíli zakázat GC, takže se zaručeně nespustí. A podle dokumentace, pokud se používají vlastní alokátory, tak se nespustí i bez zákazu (logicky). To by mělo jít použít univerzálně.
-
Pánové, já vás nechápu...proč se koukám na jazyk D? Sem zvědavý. Ano, vým, jde vytvořit gc bez freeznuti (reference counting). Ma však několik vad, třeba cyklicke datove struktury (jež se neuvolní). D má nějaký derivat mark and sweep (netuším jak maj přesně resenou fragmentaci). Takže, prosím, nenapadat me znalosti, pouze sem chtěl začít debatu. Pozoruji totiž že nabírá D na síle.
To je tak jediná vada. Ale proto se používají slabé reference (__weak v ObjC, weak_ptr v STL, WinRT také něco má). Plně automatický GC má smysl jen když přesouvá přeživší objekty, čímž se zaručí deterministická alokace na haldě, jinak totiž přináší jen nevýhody.
-
Pánové, já vás nechápu...proč se koukám na jazyk D? Sem zvědavý. Ano, vým, jde vytvořit gc bez freeznuti (reference counting). Ma však několik vad, třeba cyklicke datove struktury (jež se neuvolní). D má nějaký derivat mark and sweep (netuším jak maj přesně resenou fragmentaci). Takže, prosím, nenapadat me znalosti, pouze sem chtěl začít debatu. Pozoruji totiž že nabírá D na síle.
I pri pocitani referenci bude muset GC zastavit aktivni vlakna, rozdil je jen v tom, ze nemusi udelat STW pri oznacovani zivych/nezivych objektu. STW chovani lze omezit, napriklad tak, ze se vlakna zastavuji postupne, coz ale pro RT aplikaci je stejne spatne jako SWT :(
-
Běžná aplikace (nějaké GUI kolem databáze + trochu business logiky) by s GC neměla mít problem. High performance kód by měl používat zásobník a vlastní alokátor.
Nemáte nějaký link na to, jak tohle dělat v Javě (jde-li to vůbec)? Zrovna včera jsem se snažil něco najít, ale neúspěšně.
Alokace na zasobniku - v Jave nejde, alespon ne na urovni bajtkodu (JIT si to ale tak muze prelozit a nekdy to dela, coz vsak moc neovlivnite z programu). Vlastni alokator pres ByteBuffer, ale budete si muset delat vsechno okolo prace s pameti sam a hlavne rozumet tomu, co se stane s objekty vytvarenymi mimo tento buffer, ale ktere na nej obsahuji reference. Muj soukromy nazor - vetsinou jde o zbytecnou preliminary optimization :)
-
Zkoušel někdo z vás jazyk D? Jaké jsou dojmy? Já sem se na něj trošku koukal a teda nic moc. Straší tam GC. De sice vypnout, ale knihovna a některé language features pak moc dobře nefungujou. Plus, GC při zběru paměti freezne všechny vlákna (nevím jak sou na tom jiné GC, ale mám pocit že to de i bez freezu). Teda fakt netuším proč by člověk měl jít do D a ne třeba do javy ...
Ja vnem uz nejakou dobu pracuji a nemuzu si jej vynachvalit. Co se tyce GC, ano jsou urciute typy aplikaci kde to muze byt problem (tam idealne jak uz bylo psano je mozne si spravu pameti resit sam). Pro vetsinu aplikaci co jsem kdy psal je GC spis vyhoda. Jinak ohledne spravy pameti v D se ted hodne veci zlepsuje, teda minimalne je to jedna z hlavnich priorit. Jinak proc D misto javy? Tak hlavne proto ze jazyk D je systemovy jazyk, to znamena umoznuje prave takove veci jako resit si vlastni alokaci pameti. A celkove umoznuje jazyk D opravdu skvele veci. Pokazde kdyz mam po nejake dobe psat neco v Jave tak uplne silim z toho co vsechno tam nejde tak snadno jako v D. Ale to je samozrejme o zvyku stejny problem mam i s C++.
Celkove je D velmi zajimavy jazyk a to zejmena diky svym vlastnostem. Pro me je hodne dulezita snadna podpora C knihoven.
-
I pri pocitani referenci bude muset GC zastavit aktivni vlakna, rozdil je jen v tom, ze nemusi udelat STW pri oznacovani zivych/nezivych objektu. STW chovani lze omezit, napriklad tak, ze se vlakna zastavuji postupne, coz ale pro RT aplikaci je stejne spatne jako SWT :(
Pro RT aplikaci přeci stačí, když program poběží dostatečně často po dostatečně dlouhé doby.
To je tak jediná vada. Ale proto se používají slabé reference (__weak v ObjC, weak_ptr v STL, WinRT také něco má). Plně automatický GC má smysl jen když přesouvá přeživší objekty, čímž se zaručí deterministická alokace na haldě, jinak totiž přináší jen nevýhody.
Přesné počítání referencí má řadu dalších vad: Při alokaci mnoha krátce žijících objektů je výkon horší než u moderních vícegeneračních GC, těžko se efektivně implementuje pro konkurentní programy, nedeterministické uvolňování v konkurentních programech (nevíte, jaké vlákno spustí destruktor), lavinovité uvolňování. IMO počítání referencí má smysl pouze pro starší generace málo používaných objektů - viz třeba ulterior reference counting (a ani tam nepoužívají přesné počítání, protože to je příliš pomalé).
Jinak hlavní výhoda GC je modularita programů.
-
I pri pocitani referenci bude muset GC zastavit aktivni vlakna, rozdil je jen v tom, ze nemusi udelat STW pri oznacovani zivych/nezivych objektu. STW chovani lze omezit, napriklad tak, ze se vlakna zastavuji postupne, coz ale pro RT aplikaci je stejne spatne jako SWT :(
Pro RT aplikaci přeci stačí, když program poběží dostatečně často po dostatečně dlouhé doby.
Za predpokladu, ze budu mit jako programator moznost zarucit presnou dobu behu vlakna aplikace (+ maximalni povolenou dobu preruseni behu vlakna), tak rekneme ano, ale to v pripade GC (prakticky jakehokoli) nemam. Pod RT aplikaci si predstavme napriklad program, kteremu kontinualne prichazi po sbernici data a on je musi zarucene a za vsech okolnosti precist, aniz by pretekl buffer a data se ztratila. Obecne v beznem Linux/Windows prostredi to nejde zaridit a v pripade managovanych jazyku s GC taktez ne. "Resi" se to pouzitim mnohem silnejsiho zeleza nez by bylo zapotrebi a urcite davky rekneme viry, ze to proste bude fungovat :)
-
Pánové, já vás nechápu...proč se koukám na jazyk D? Sem zvědavý. Ano, vým, jde vytvořit gc bez freeznuti (reference counting). Ma však několik vad, třeba cyklicke datove struktury (jež se neuvolní). D má nějaký derivat mark and sweep (netuším jak maj přesně resenou fragmentaci). Takže, prosím, nenapadat me znalosti, pouze sem chtěl začít debatu. Pozoruji totiž že nabírá D na síle.
I pri pocitani referenci bude muset GC zastavit aktivni vlakna, rozdil je jen v tom, ze nemusi udelat STW pri oznacovani zivych/nezivych objektu. STW chovani lze omezit, napriklad tak, ze se vlakna zastavuji postupne, coz ale pro RT aplikaci je stejne spatne jako SWT :(
Při počítání referencí se jen atomicky sníží čítač referencí. Ostatních vláken se to nedotkne, pokud nesahají na stejný čítač.
-
Pánové, já vás nechápu...proč se koukám na jazyk D? Sem zvědavý. Ano, vým, jde vytvořit gc bez freeznuti (reference counting). Ma však několik vad, třeba cyklicke datove struktury (jež se neuvolní). D má nějaký derivat mark and sweep (netuším jak maj přesně resenou fragmentaci). Takže, prosím, nenapadat me znalosti, pouze sem chtěl začít debatu. Pozoruji totiž že nabírá D na síle.
I pri pocitani referenci bude muset GC zastavit aktivni vlakna, rozdil je jen v tom, ze nemusi udelat STW pri oznacovani zivych/nezivych objektu. STW chovani lze omezit, napriklad tak, ze se vlakna zastavuji postupne, coz ale pro RT aplikaci je stejne spatne jako SWT :(
Při počítání referencí se jen atomicky sníží čítač referencí. Ostatních vláken se to nedotkne, pokud nesahají na stejný čítač.
Muze byt u jednoducheho objektu, ale uz u takoveho seznamu (kde si program udrzoval pouze referenci na HEAD/CAR) je to slozitejsi operace, a samotna atomicita muze znamenat nutnost locku nebo alespon CAS operaci. Navic se pojmem "reference counting" muze oznacovat nekolik ruznych technologii, muze to byt klidne spojeno s generacnimi GC, v nichz dochazi k presunum objektu na heapu (ale to jsme asi oba dva nemeli na mysli, navic to je spis problematika managovanych jazyku, v nichz neexistuje manipulace s primymi ukazateli).
-
v pripade managovanych jazyku s GC taktez ne.
Proč to nejde zařídit v případě jazyků s GC? Čekal bych, že z parametrů programu (jako je rychlost alokace a rychlost generování odpadků) a parametrů GC (jako je rychlost zpracování) vypočtu, kolik času zbyde pro běh programu. Viz třeba článek "A Real-time Garbage Collector with Low Overhead and Consistent Utilization" od autorů Bacon, Cheng, Rajan, který v kapitole 4 obsahuje takovou analýzu pro kolektor Metronome.
-
Pánové, já vás nechápu...proč se koukám na jazyk D? Sem zvědavý. Ano, vým, jde vytvořit gc bez freeznuti (reference counting). Ma však několik vad, třeba cyklicke datove struktury (jež se neuvolní). D má nějaký derivat mark and sweep (netuším jak maj přesně resenou fragmentaci). Takže, prosím, nenapadat me znalosti, pouze sem chtěl začít debatu. Pozoruji totiž že nabírá D na síle.
I pri pocitani referenci bude muset GC zastavit aktivni vlakna, rozdil je jen v tom, ze nemusi udelat STW pri oznacovani zivych/nezivych objektu. STW chovani lze omezit, napriklad tak, ze se vlakna zastavuji postupne, coz ale pro RT aplikaci je stejne spatne jako SWT :(
Při počítání referencí se jen atomicky sníží čítač referencí. Ostatních vláken se to nedotkne, pokud nesahají na stejný čítač.
Muze byt u jednoducheho objektu, ale uz u takoveho seznamu (kde si program udrzoval pouze referenci na HEAD/CAR) je to slozitejsi operace, a samotna atomicita muze znamenat nutnost locku nebo alespon CAS operaci. Navic se pojmem "reference counting" muze oznacovat nekolik ruznych technologii, muze to byt klidne spojeno s generacnimi GC, v nichz dochazi k presunum objektu na heapu (ale to jsme asi oba dva nemeli na mysli, navic to je spis problematika managovanych jazyku, v nichz neexistuje manipulace s primymi ukazateli).
Jistě, u složitějších datových struktur může být zamykání komplikovanější, nicméně často je "konkurenční" chování efektivnější než kdyby se zastavila všechna vlákna. Navíc konkrétně v Cocoa se typicky provádí RC jen na hlavním vlákně (main loop), protože i callbacky na něm běží, a zbytek používá manuální správu paměti. Ale to už jsem odbočil.
-
Teď sem narazil na něco divného. Když sem se koukal na přetěžování operátorů v jazyce D, zjistil jsem že přetěžování operátorů new a delete je deprecated. Proboha proč, to si jako nepude napsat vlastní alokátor? Vysvětlení proč u toho nebylo. Taktéž sem někde viděl že scope proměnné budou též zrušeny. Hej, ty se s tím neserou, pro systémové programování je tendle jazyk tím pádem na nic (jo, OS s GC...). Sem trošku zklamaný, začal se mi líbit (x[] += y[] -> jak elegantní). Asi chtěj zabít RAII...
-
v pripade managovanych jazyku s GC taktez ne.
Proč to nejde zařídit v případě jazyků s GC? Čekal bych, že z parametrů programu (jako je rychlost alokace a rychlost generování odpadků) a parametrů GC (jako je rychlost zpracování) vypočtu, kolik času zbyde pro běh programu. Viz třeba článek "A Real-time Garbage Collector with Low Overhead and Consistent Utilization" od autorů Bacon, Cheng, Rajan, který v kapitole 4 obsahuje takovou analýzu pro kolektor Metronome.
Ano maji to tam analyzovane, ale ne pro vsechny pripady (predpokladam, ze nejaky dalsi clanek to objasni), napriklad velka pole a dalsi velke objekty. Dale mi tam schazi - ale mozna jen spatne ctu - analyza, co se stane pri velke fragmentaci heapu a naslednem pokusu o alokaci objektu. Btw uz samotny dukaz, ze treba nedojde pamet, je silene slozity /* mam volne tema na doktorskou praci! */, ten problem byl v clanku schvalne zjednodusen, ale to asi pro RT aplikace dostacuje, uznavam.
OT: Ono se vubec ukazuje, ze predpoklady, na kterych jsou postaveny generacni GC, nejsou vzdy splneny, hlavne to neplati u optimalizovanych aplikaci - tam generacni GC mnohdy nadelaji vic skody nez uzitku.
-
Ano maji to tam analyzovane, ale ne pro vsechny pripady (predpokladam, ze nejaky dalsi clanek to objasni), napriklad velka pole a dalsi velke objekty. Dale mi tam schazi - ale mozna jen spatne ctu - analyza, co se stane pri velke fragmentaci heapu a naslednem pokusu o alokaci objektu.
Velká pole reprezentují jako menší dvouúrovňová pole (viz 3.5). Velké fragmentaci předchází alokační strategií (viz 3.2) a defragmentací (viz 3.3).
-
Niekde som videl prednasku o presnom GC. Robi to dost rozdiel pri programoch s gigovymi heapmi. Uz to tam maju? Inak ako jazyk sice fajn, ale zhanat/pisat bindingy na vsetko ma trosku odradza. To uz radsej to c++, zase az take hnusne to nie je.
-
Ano maji to tam analyzovane, ale ne pro vsechny pripady (predpokladam, ze nejaky dalsi clanek to objasni), napriklad velka pole a dalsi velke objekty. Dale mi tam schazi - ale mozna jen spatne ctu - analyza, co se stane pri velke fragmentaci heapu a naslednem pokusu o alokaci objektu.
Velká pole reprezentují jako menší dvouúrovňová pole (viz 3.5). Velké fragmentaci předchází alokační strategií (viz 3.2) a defragmentací (viz 3.3).
S temi poli to budou mit jeste v praxi slozite. Vim to, protoze s temito velkymi objekty (humongous object) maji problemy vlastne vsechny soucasne GC, jak ty generacni, tak i ty zalozene na heapu rozdelenem na mnoho stejne velkych oblasti (Shenandoah GC). Z toho vyplyva i pouceni - pozor v Jave/C#/foobar na dlouhe vektory a matice, kdyz uz je skutecne potrbujete, tak nekdy pomuze ByteBuffer ;-)
-
Běžná aplikace (nějaké GUI kolem databáze + trochu business logiky) by s GC neměla mít problem. High performance kód by měl používat zásobník a vlastní alokátor.
Nemáte nějaký link na to, jak tohle dělat v Javě (jde-li to vůbec)? Zrovna včera jsem se snažil něco najít, ale neúspěšně.
Real Time Java Specification
http://www.rtsj.org/docs/docs.html
Memory management. There are two new types of memory areas that help prevent unpredictable delays commonly caused by traditional garbage collectors in real-time applications: immortal memory and scoped memory. Immortal memory holds objects without destroying them, except when the program ends. This means that objects created in immortal memory must be carefully allocated and managed, as with C programs. Scoped memory is used only while a process works within a particular section, or scope, of the program (such as in a method). Objects are automatically destroyed when the process leaves the scope. Neither immortal nor scoped memories are garbage collected, so using them avoids problems of GC interference. The Java RTS also provides limited support for providing memory allocation budgets for threads using memory areas. Maximum memory area consumption and maximum allocation rates for individual real-time threads may be specified when the thread is created.
-
GC při zběru paměti freezne všechny vlákna (nevím jak sou na tom jiné GC, ale mám pocit že to de i bez freezu).
Až se váš pocit změní v jistotu tím že najdete implementaci GC která nebude potřebovat "stop the world" neboli zastavení všech vláken tak nám prosím dejte vědět.
Concurrent Garbage Collector
To je sice pěkný název, ale já jsem se ptal na konkrétní implementaci. Ve všech implementacích které měly v názvu slovo concurrent jsem bohužel zatím vždycky našel i fázi která "stop the world" potřebovala, i když obvykle na kratší dobu než implementace které v názvu slovo concurrent neměly.
-
Teď sem narazil na něco divného. Když sem se koukal na přetěžování operátorů v jazyce D, zjistil jsem že přetěžování operátorů new a delete je deprecated. Proboha proč, to si jako nepude napsat vlastní alokátor? Vysvětlení proč u toho nebylo. Taktéž sem někde viděl že scope proměnné budou též zrušeny. Hej, ty se s tím neserou, pro systémové programování je tendle jazyk tím pádem na nic (jo, OS s GC...). Sem trošku zklamaný, začal se mi líbit (x[] += y[] -> jak elegantní). Asi chtěj zabít RAII...
Samozrejme mozes pouzit vlastne allocatory, dokonca budu allocatory priamo v phobose: https://github.com/andralex/phobos/blob/allocator/std/allocator.d
Miesto pretaznia new pouzi emplace: http://dlang.org/phobos/std_conv.html#.emplace
scoped je depracted lebo totozna funkcionalita je v phobose: http://dlang.org/phobos/std_typecons.html#.scoped
-
GC při zběru paměti freezne všechny vlákna (nevím jak sou na tom jiné GC, ale mám pocit že to de i bez freezu).
Až se váš pocit změní v jistotu tím že najdete implementaci GC která nebude potřebovat "stop the world" neboli zastavení všech vláken tak nám prosím dejte vědět.
Concurrent Garbage Collector
To je sice pěkný název, ale já jsem se ptal na konkrétní implementaci. Ve všech implementacích které měly v názvu slovo concurrent jsem bohužel zatím vždycky našel i fázi která "stop the world" potřebovala, i když obvykle na kratší dobu než implementace které v názvu slovo concurrent neměly.
Tak třeba CGC v .NETu. Pokud v době, kdy ten garbage collector markuje, nepotřebuje vlákno alokovat na haldě, tak se vůbec nezastaví.
-
GC při zběru paměti freezne všechny vlákna (nevím jak sou na tom jiné GC, ale mám pocit že to de i bez freezu).
Až se váš pocit změní v jistotu tím že najdete implementaci GC která nebude potřebovat "stop the world" neboli zastavení všech vláken tak nám prosím dejte vědět.
Concurrent Garbage Collector
To je sice pěkný název, ale já jsem se ptal na konkrétní implementaci. Ve všech implementacích které měly v názvu slovo concurrent jsem bohužel zatím vždycky našel i fázi která "stop the world" potřebovala, i když obvykle na kratší dobu než implementace které v názvu slovo concurrent neměly.
SerialGC, ParallelOldGC, G1 i ted nove Shenandoah - vsechny maji STW chovani, i kdyz o ruzne dobe trvani (ty "lepsi" GC zastavuji jen ve fazi (paralelniho) markingu). Experimentuje se s rozdelenim STW na pozastavovani jednotlivych vlaken aplikace (mutatoru), ale jeste to neni ve stabilni fazi.
-
Tak třeba CGC v .NETu. Pokud v době, kdy ten garbage collector markuje, nepotřebuje vlákno alokovat na haldě, tak se vůbec nezastaví.
To je pěkný trik o kterém jsem nevěděl. Ale obávám se že to pomůže pouze speciálním aplikacím - pokud po celou dobu markování GC vlákna nepotřebují alokovat na haldě, je vysoká pravděpodobnost že alokací na haldě bude tak málo že GC vlastně nemá moc co dělat. Naopak typické aplikace na haldě alokují, takže je ten CGC v .NETu stejně bude zastavovat. :-(
-
GC GC GC, to je snad ňáká mantra? v OOP není třeba GC (čas se s ním ale zabít dá no). Jo jinak mám další dojmy z Dčka. Slyšel jsem, že D bylo vytvořeno protože C++ je strašné a má strašnou syntaxi. Když se teď hrabu hloubš v D, C++ je proti tomu ráj. Pro příklad volaní konstruktoru a destruktoru:
C++: pointer->~jménoTřídy(); /* ... */
D: destroy(object) /* Destroy je prosím knihovní funkce ... vnitřně asi používá obj.__dtor() ale fakt nevím, nikde o tom není ani zmínka */
C++: new (memoryChunk) jménoTypu(args);
D: emplace!jménoTypu(memoryChunk, args) /* Opět knihovní funkce která vnitřně používá ňákej obj.__ctor(args) jež je nezdokumentovanej */
Smutné na tom je, že co by měla pokrývat syntaxe jazyka, pokrývá knihovna. A co by měla pokrývat knihovna pokrývá syntaxe (třeba sort u polí). Teda když bych chtěl udělat bootloader s C++, nebude to problém, s D si asi budu muset portovat celou knihovnu (která často dělá to co by měly dělat konstrukty jazyka) a odhalit všechny možné nezdokumentované nechutnosti.
-
GC GC GC, to je snad ňáká mantra? v OOP není třeba GC (čas se s ním ale zabít dá no). Jo jinak mám další dojmy z Dčka. Slyšel jsem, že D bylo vytvořeno protože C++ je strašné a má strašnou syntaxi. Když se teď hrabu hloubš v D, C++ je proti tomu ráj. Pro příklad volaní konstruktoru a destruktoru:
C++: pointer->~jménoTřídy(); /* ... */
D: destroy(object) /* Destroy je prosím knihovní funkce ... vnitřně asi používá obj.__dtor() ale fakt nevím, nikde o tom není ani zmínka */
C++: new (memoryChunk) jménoTypu(args);
D: emplace!jménoTypu(memoryChunk, args) /* Opět knihovní funkce která vnitřně používá ňákej obj.__ctor(args) jež je nezdokumentovanej */
Smutné na tom je, že co by měla pokrývat syntaxe jazyka, pokrývá knihovna. A co by měla pokrývat knihovna pokrývá syntaxe (třeba sort u polí). Teda když bych chtěl udělat bootloader s C++, nebude to problém, s D si asi budu muset portovat celou knihovnu (která často dělá to co by měly dělat konstrukty jazyka) a odhalit všechny možné nezdokumentované nechutnosti.
Ved emplace/destroy je tu kvoly tomu aby si nemusel manualne volat konstruktory/destruktory cez __ctor, __dtor...
To ze miesto placement new sa pouzie emplace a miest ~Trieda() sa pouzie destroy je len detail.
Co sa tyka sort-u, ten bude tiez deprecated :D.
Co sa tyka phobosu a alokacii, tak je to znamy problem. Momentalne sa caka na alokatory. Ked budu v phobose tak je v plane pouzit interne mallocator alebo nieco podobne. Dokonca su navrhy na atribut @nogc / @noheap ktore zakazu pre funkcie gc alokacie.
GC je v D hlavne kvoly tomu aby sa dalo v @safe kode garantovat memory safty a implementacia Slice-ov by bola bez GC problematicka.
-
Smutné na tom je, že co by měla pokrývat syntaxe jazyka, pokrývá knihovna. A co by měla pokrývat knihovna pokrývá syntaxe (třeba sort u polí). Teda když bych chtěl udělat bootloader s C++, nebude to problém, s D si asi budu muset portovat celou knihovnu (která často dělá to co by měly dělat konstrukty jazyka) a odhalit všechny možné nezdokumentované nechutnosti.
To, že konstruktory a destruktory jsou jen funkce se speciálním jménem není žádný problém. V C++ je to to samé, akorát je navrch trocha syntaktického cukru. Akorát jestli to není zdokumentované, tak je to na facku. Stejně tak v principu není třeba mít new a delete jako operátory, ale stačí knihovní funkce, které zavolají alokaci a pak konstruktor. Tyhle knihovní funkce ani není třeba při portování přepisovat, takže jsou podle mě OK prakticky vždycky. Místo přetěžování operátorů se jenom přetíží funkce, nebo ještě líp předá jiný alokátor. Přetěžování new a delete mi přijde jako jedna z nejzatemněnějších částí C++.
Dostalo mě, že D nemá vůbec specifikovaný paměťový model (aspoň podle tutoriálu). Ale prý můžeme předpokládat, že by mohl být podobný modelu C++. Hlavně že mají ve stadardní knihovně kopec vláknových věcí.
-
No, například v dokumentace sem nikdy nenarazil na __ctor(), __dtor() přičemž ty slouží k vyvolání konstruktoru pod referencí na objekt. Zjistil sem to až na fóru, bůh ví kde ty informace berou. Právě proto mne pečou funkce jako emplace, protože když budu dělat něco low level a budu potřebovat obejít GC, nemůžu použít takřka (jo něco jo...) nic z knihovny ale budu si muset udělat takovýhle kraviny sám. Což v případě neexistující dokumentace je na změnu jazyka. Teď se peru s vlastním kontejnerem bez GC paměti (malloc free), odměnou je SIGSEGV, a debuguj šmoulo...
-
Tak třeba CGC v .NETu. Pokud v době, kdy ten garbage collector markuje, nepotřebuje vlákno alokovat na haldě, tak se vůbec nezastaví.
To je pěkný trik o kterém jsem nevěděl. Ale obávám se že to pomůže pouze speciálním aplikacím - pokud po celou dobu markování GC vlákna nepotřebují alokovat na haldě, je vysoká pravděpodobnost že alokací na haldě bude tak málo že GC vlastně nemá moc co dělat. Naopak typické aplikace na haldě alokují, takže je ten CGC v .NETu stejně bude zastavovat. :-(
Oni tam mají ještě jeden trik. Pro markování je halda rozdělena na tři generace (dočasná, krátkodobá, dlouhodobá). GC pak blokuje alokace, pouze pokud se markuje nultá a první generace, což pro dlouho běžící programy nepředstavuje moc velký problém, protože ty většinou bývají malé s malým množstvím odkazů, takže se markují velmi rychle.
-
Dotaz trochu off topic: někdo tady tvrdil, že "jazyk D nabírá na síle"; je tomu skutečně tak, že se D podařilo výrazně zvednout popularitu? Já mám pocit, že jazyků, které by mu mohly konkurovat (byť nejsou úplně stejné) je čím dál více, počínaje Go a konče Rustem a o D se moc nikde nepíše...
-
Dotaz trochu off topic: někdo tady tvrdil, že "jazyk D nabírá na síle"; je tomu skutečně tak, že se D podařilo výrazně zvednout popularitu? Já mám pocit, že jazyků, které by mu mohly konkurovat (byť nejsou úplně stejné) je čím dál více, počínaje Go a konče Rustem a o D se moc nikde nepíše...
Podle analyzu serveru Ohloh je ten jazyk pro OS projekty zatim vyuzivan minimalne, samozrejme relativne vuci silne trojce C+Java+Python:
https://www.ohloh.net/languages/compare?measure=commits&percent=true&l0=c&l1=java&l2=python&l3=dmd&l5=dmd&commit=Update
I kdyz ma D nektere zajimave vlastnosti (prace s poli je dost luxusni v porovnani s Javou napriklad), tak za tech ~13 let uz ten jazyk mel dost casu se stat popularnejsim - tudiz se (AFAIK) nahrada C jakozto "vysokourovnoveho assembleru" nekona a pro praci s komplexnejsimi aplikacemi je tady rada jinych a uz zavedenych jazyku.
-
I kdyz ma D nektere zajimave vlastnosti (prace s poli je dost luxusni v porovnani s Javou napriklad), tak za tech ~13 let uz ten jazyk mel dost casu se stat popularnejsim - tudiz se (AFAIK) nahrada C jakozto "vysokourovnoveho assembleru" nekona a pro praci s komplexnejsimi aplikacemi je tady rada jinych a uz zavedenych jazyku.
Tak tohle je pravda, když je to dobré, má to obvykle raketový nástup, proč ne jazyk D?
Možná protože není dramaticky lepší než něco z rodiny C (C/C++/C#...)
Rád bych zmínil mrtvy projekt Asyncro, který se taky nechytil a přitom mi připadal docela pěkný.
Int Y dogma {0 to 10}; //Y nemůže mít jinou hodnotu než 0 až 10
Sub Y Int Palce dogma {result:= Master * 2,54;}; //Y.Palce má vždy hodnotu Y*2,54
Int Z is {0 to 10} ow(Exception oOverlimit){Console.Printf("Ahoj");}; //Podobne jako prvni priklad, ale vyhodnoceni provedu sam
Int X1 evaluate {/* vola se pri uplne kazde zmene X */}; //Kdykoliv se X1 zmeni, chci vedet proc
Int X2 evaluate (%DEBUGMODE%) {/* vola se pri uplne kazde zmene X2 */}; //Vyhodnocuje se, jen kdyz se na programu pracuje
->
Y=10;
Y++;
Console.Printf (Y);
10
Console.Printf (Y.Palce);
25,4
Z=10
Z++;
:Ahoj
Ten jazyk nabízel řadu pěkných věcí, které jde udělat i v C# pres GET/SET, ale ten kód mi přišel čitelnější.
-
Syntaxi mám možná špatně, narychlo jsem si
jí vycucal z prstu vzpomněl, jak to asi vypadalo.
-
No, teď se pokouším vytvořit vlastní object pool (uskladňuje a alokuje objekty pomocí malloc a free místo vestavěného GC). Teda celkem mordor, funguje ale ... jen díky pomoci na fóru a knihovním funkcím (!!). Z toho mě ale chytá rapl ... když bych chtěl v D dělat něco low level, nemůžu používat std knihovnu když si ji neportuju a právě takový object pool je dobrej jenom když dělám něco low level. D je tak jedině dobrý jako high level lang. Low level nikdy ... pak je to celkem pěknej jazyk. Někdy rychlejší než C++.
-
I kdyz ma D nektere zajimave vlastnosti (prace s poli je dost luxusni v porovnani s Javou napriklad), tak za tech ~13 let uz ten jazyk mel dost casu se stat popularnejsim - tudiz se (AFAIK) nahrada C jakozto "vysokourovnoveho assembleru" nekona a pro praci s komplexnejsimi aplikacemi je tady rada jinych a uz zavedenych jazyku.
JJ presne tak. Hlavni problem jazyka D je to ze se primarne mel stat nahradou za jazyky C a C++, bohuzel v oblasti kde se jazyky C a C++ pouzivaji se mu nedari prave kvuli velke zavislosti na GC. Tim padem se zacal vyuzivat spis tam kde se dneska hodne dari jazykum jako je C# nebo Java, se kterymi zase nemuze konkurovat co se tyce rozsireni, existujicich navodu, knihoven, nastroju proste vseho.
Ale co se tyce rozsireni, tak se popravde dostava zase trochu navrchol. Nejvetsi podil na tom ma asi Andrei Alexandrescu. Jinak hodne jazyku D ubraly problemy z dob jazyka D1. Takze co se tyce tech 13let, tak ja bych to spis videl tak na jen na polovina 6-7 let, coz zase neni tak dlouha doba.
-
No, teď se pokouším vytvořit vlastní object pool (uskladňuje a alokuje objekty pomocí malloc a free místo vestavěného GC). Teda celkem mordor, funguje ale ... jen díky pomoci na fóru a knihovním funkcím (!!). Z toho mě ale chytá rapl ... když bych chtěl v D dělat něco low level, nemůžu používat std knihovnu když si ji neportuju a právě takový object pool je dobrej jenom když dělám něco low level. D je tak jedině dobrý jako high level lang. Low level nikdy ... pak je to celkem pěknej jazyk. Někdy rychlejší než C++.
To je to co jsem psal, proste pro low level to neni idealni. Ono ne ze by to neslo, ale ve vysledku si stim clovek zas moc oproti C nebo C++ nepomuze. Zejmena proto ze vetsina dobrych vlastnosti jazyka je zavisla na GC. Ja jazyk D pouzivam proste tam kde bych pouzil C++, ale kde se nejedna vylozene o low level. Napriklad nastroj pro zpracovani binarnich dat s gps jednotek a jejich ulozeni do databaze. Ale pro napsani ovladace pro operacni system bych to nepouzil.
Ale jak jiz bylo uvedeno problem s low level a obecne s gc je priorita cislo jedna pro tento rok. Takze se da ocekavat zlepseni. Dokonce vznikly navrhy i na vznik jazyka D pro low level http://forum.dlang.org/thread/lddug4$jgv$1@digitalmars.com
-
Jinak jako mozne pole pusobnosti bych pro jazyk D videl psani mobilnich aplikaci. Protoze psani mobilnich aplikaci v C++ mi neprijde proste zrovna idealni, psani v Jave je sice celkem fajn, ale celkova vykonost aplikaci na androidu a naroky na systemove pozadavky hovori za vse. V tomto smeru bych jazyk D prirovnal ObjectiveC pro IOS
-
D verze pro low level. Já netuším čím myslej. Sice nejsem žádnej hyper super máster, to ne. Ale vím jak upravit Dčko aby bylo použitelný. Všechny featury a celou STD knihovnu předělat na malloc/free model. Dále vytvořit operátor gnew (alokace GC) a mnew (malloc) a mdelete (free). Přičemž mnew a mdelete by šlo přetížit ve třídě + možnost globálního předefinovaní mnew a mdelete (jako v C++). Pak bych sral na C++ a šel do toho, jenže teď? Teď je to peklo. Ani konstruktor nezavoláš správně bez STD knihovny. Jo, a znormálnit volání konstruktorů a destruktorů aby nebylo třeba hrabat do knihovny. Co sem si všim tak vlastní volání __ctor či __dtor funguje, ale zpětně potom někde schytám SIGSEGV. No vlastně ne. Ani by nemuseli upravovat volání konstruktorů a destruktorů, to by si ošefovali operátory dynamické alokace (jako v C++ ...).
-
Jinak range operátory sou úžasný. A někde sem viděl benchmark ve kterym bylo Dčko výrazně rychlejší než C++ (?~30%).
-
Jo zapomněl sem napsat, vytvořit uživatelem předefinovatelný alokátor pro GC. Aby bylo možné třeba vytvořit ňejaký pool statické paměti a na ní napojit GC. Sice nevím k čemu by to bylo, ale šlo by vytvořit snadno low level program s GC (ačkoli by to pak bylo zkurveně pomalí -> ale zajímaví).
-
Jo zapomněl sem napsat, vytvořit uživatelem předefinovatelný alokátor pro GC. Aby bylo možné třeba vytvořit ňejaký pool statické paměti a na ní napojit GC. Sice nevím k čemu by to bylo, ale šlo by vytvořit snadno low level program s GC (ačkoli by to pak bylo zkurveně pomalí -> ale zajímaví).
To tusim ide pomocou GC.malloc: http://dlang.org/phobos/core_memory.html#.GC.malloc
Skus sa pozret aj sem: https://github.com/andralex/phobos/blob/allocator/std/allocator.d
Das sem kod tvojho object poolu?
-
module test.main;
import std.stdio;
import std.c.stdlib;
import std.traits;
import std.random;
import std.conv;
class example
{
int num;
this()
{
writeln("example ctor");
num = 1000;
}
~this()
{
writeln("example dtor");
}
int callee()
{
return num*2;
}
}
struct simpleVector(T)
{
enum instanceSize = __traits(classInstanceSize, T);
enum padding = instanceSize % std.traits.classInstanceAlignment!T;
enum paddedInstanceSize = instanceSize + padding;
byte * buffer = null;
int size = 0;
this(int _size)
{
size = _size;
buffer = cast(byte*)malloc(paddedInstanceSize*size);
for (int i=0; i<size;++i)
{
emplace!T(buffer[i*paddedInstanceSize .. (i+1)*paddedInstanceSize]);
}
}
~this()
{
for (int i=0; i<size;++i)
{
dtor(cast(T)(buffer+i*paddedInstanceSize));
}
size = 0;
free(buffer);
}
T opIndex(int index)
{
return cast(T)(buffer+index*paddedInstanceSize);
}
void dtor(T ob)
{
ob.__dtor();
}
}
void main()
{
simpleVector!example vc = simpleVector!example(5);
auto gen = Random(unpredictableSeed);
for (int i=0; i<vc.size; ++i)
{
vc.num = uniform(0,1024,gen);
}
for (int i=0; i<vc.size; ++i)
{
write(vc.num);
write(" -> ");
writeln(vc.callee());
}
}
-
Ale bez pomoci bych to fakt nedal. Pokoušel sem se nahradit knihovní funkci emplace svojí verzí, končí to tím že když zavolám funkcí pod uloženým objektem, vyhodí mi to SIGSEGV.
void ctor(T ob)
{
ob.__ctor();
}
Inu, zřejmě to není jen o tom konstruktoru, ta konstrukce objektu ...
BTW, neví tady někdo jak zabránit DMD kompileru v připojení systémových knihoven a dalších kravin (i GC!). Kdyby to bylo GDC tak bych dal -nostdlib, ale GDC mi ňák nefachčilo.
-
Z příspevku mi to odpálilo hranaté závorky. Mají být v obou cyklech ve funkci main. logika napoví kde.
-
GC.malloc nemění alokátor ale pouze si u GC vynutí požadovaný počet bajtů jež de pak vrátit GC.free. Takže na nic (máme operátor new ... a delete pokud ho nestihli zrušit).
-
Ale bez pomoci bych to fakt nedal. Pokoušel sem se nahradit knihovní funkci emplace svojí verzí, končí to tím že když zavolám funkcí pod uloženým objektem, vyhodí mi to SIGSEGV.
void ctor(T ob)
{
ob.__ctor();
}
Inu, zřejmě to není jen o tom konstruktoru, ta konstrukce objektu ...
BTW, neví tady někdo jak zabránit DMD kompileru v připojení systémových knihoven a dalších kravin (i GC!). Kdyby to bylo GDC tak bych dal -nostdlib, ale GDC mi ňák nefachčilo.
a co toto:
void ctor(T ob)
{
byte[] chunk = (cast(byte*)ob)[0 .. instanceSize];
chunk = T.classinfo.init[];
ob.__ctor();
}
-
Perfektní (šlape jak má), z jakého důvodu nestačí proboha zavolat jen konstruktor ? Jinak byte[] je dynamické pole jež je závislé na GC takže s tímhle fakt jako v bootloaderu nemůžu machrovat. Jednu věc nechápu. byte[] chunk = (cast(byte*)ob)[0 .. instanceSize]; překopíruje bajt po bajtu obsah objektu ob do pole chunk. Poté pomocí T.classinfo.init získáme statickou inicializaci jež překopírujeme do chunku. No jo ale ob je netknutý když nad ním volám konstruktor. Tak jak to že to funguje? Co mě v D mate jsou reference. V C++ jsou takové pěkné čisté a vlastně s nimi vůbec nepracujeme (maximálně v argumentech).
-
...
Vector by mohol vyzerat takto (bez emplace/destroy):
import std.stdio;
class example{
int num;
this(int num){
this.num = num;
writeln("example ctor ", num);
}
~this(){
writeln("example dtor ", num);
}
}
struct simpleVector(T)if(is(T == class)){
import core.stdc.stdlib, std.traits;
enum instanceSize = __traits(classInstanceSize, T);
enum padding = instanceSize % std.traits.classInstanceAlignment!T;
enum paddedInstanceSize = instanceSize + padding;
private void[] buffer;
private size_t size_ = 0;
private T data(size_t i){
return cast(T) buffer[i*paddedInstanceSize .. $].ptr;
}
@property size_t size()const{return size_;}
bool emplaceBack(Args...)(Args args){
if(size*paddedInstanceSize < buffer.length){
auto s = buffer.length*2;
auto p = cast(ubyte*) realloc(buffer.ptr, s);
if(!p)return false;
buffer = p[0 .. s];
}
data(size_++).__ctor(args);
return true;
}
void clear(){
for(size_t i=0; i < size; ++i){
data(i).__dtor();
}
size_ = 0;
}
@disable this();
static auto opCall(){
return simpleVector(2);
}
this(size_t x){
assert(x);
auto bytes = paddedInstanceSize*x;
auto p = malloc(bytes);
buffer = p ? p[0 .. bytes] : null;
}
~this(){
for(size_t i=0; i < size; ++i){
data(i).__dtor();
}
free(buffer.ptr);
}
T opIndex(size_t index){
assert(index < size);
return data(index);
}
}
void main(){
auto vc = simpleVector!example();
foreach(int i; 0..10){
vc.emplaceBack(i);
}
foreach(size_t i; 0..5){
vc[i*2].num *= 10;
}
for (int i=0; i<vc.size; ++i){
writeln(vc[i].num);
}
}
-
Perfektní (šlape jak má), z jakého důvodu nestačí proboha zavolat jen konstruktor ? Jinak byte[] je dynamické pole jež je závislé na GC takže s tímhle fakt jako v bootloaderu nemůžu machrovat. Jednu věc nechápu. byte[] chunk = (cast(byte*)ob)[0 .. instanceSize]; překopíruje bajt po bajtu obsah objektu ob do pole chunk. Poté pomocí T.classinfo.init získáme statickou inicializaci jež překopírujeme do chunku. No jo ale ob je netknutý když nad ním volám konstruktor. Tak jak to že to funguje? Co mě v D mate jsou reference. V C++ jsou takové pěkné čisté a vlastně s nimi vůbec nepracujeme (maximálně v argumentech).
Tak zaprve dynamicke pole samo osobe neni zavisle na GC, je to jenstruktura uchovavajici velikost pole a adresu na data. to co vyzaduje GC je napriklad az konkatenace poli atd. Takze to problem neni.
Jinak ob je v podstate reference (v podstate si predstav ze v D se pokazde jedna o ekvivalent k C++ referenci), to znamena ze tim castem na byte* z toho udelam pointer na byte. Potom pomoci slice operatoru vytvorim pole bytu s velikost instanceSize. Jinak jde o to ze objekt v D ma na zacatku v pmaeti strukturu TypeInfo_Class. No a ta musi byt pravne nainicializovana jinak nemuzes volat konstruktor.
-
To kozzi: Jo, dobrý už vím jak to že to funguje. To vitamin: máš to řešení pěkný. Jaks to vohackoval? Nikde tam nevidím T.classinfo.init. Jak je tam staticky inicializován objekt? Je tam jen volání __ctor().
-
To vitamin: Opravuju se, teď sem to vyzkoušel a nefunguje ti to. Chyba bude asi jako u mne, a totiž napřed musíš objekt staticky inicializovat : jménotřídy.classinfo.init
-
No, takže, snažil sem se zase o kus vylepšit svůj výtvor a tady je výsledek. Zase SIGSEGV, a tentokrát vím kůli čemu. Selhává mi tam kopírování bufferu. Ale netuším čím to je...ve výsledku je v bufferu změť kravin, a jen dva (z 10) validní objekty. *chjo
module test.main;
import std.stdio;
import std.random;
import std.c.stdlib;
import std.traits;
class example
{
int num;
this(int _num)
{
writeln("example ctor");
num = _num*10;
}
~this()
{
writeln("example dtor");
}
int callee()
{
return num*2;
}
}
struct objectPool(T) if (is(T==class))
{
private byte * buffer;
private long top;
private long size;
enum sizeOfEl = __traits(classInstanceSize,T);
enum elPadding = sizeOfEl % classInstanceAlignment!T;
enum finalElSize = sizeOfEl + elPadding;
this(int dummy)
{
buffer = null;
top = 0;
size = 0;
}
~this()
{
callDtors();
freeBuff();
size = 0;
}
long retSize()
{
return top;
}
T opIndex(long index)
{
return cast(T)(buffer+index*finalElSize);
}
void createInstance(Args...)(Args args)
{
if (top == size)
{
if (top == 0 && size ==0)
{
size = 1;
allocBuff();
ctor(cast(T)buffer,args);
++top;
}
else
{
size *= 2;
byte * temp = cast(byte*)malloc(size*finalElSize);
temp[0..size/2] = buffer[0..size/2];
free(buffer);
buffer = temp;
ctor(cast(T)(buffer+top*finalElSize),args);
++top;
}
}
else
{
ctor(cast(T)(buffer+top*finalElSize),args);
++top;
}
}
void allocBuff()
{
buffer = cast(byte*)malloc(finalElSize*size);
}
void freeBuff()
{
free(buffer);
buffer = null;
}
void callCtors()
{
for (int i=0; i<size;++i)
{
// To samé co dtor
ctor(cast(T)(buffer+i*finalElSize),0);
}
}
void callDtors()
{
for (int i=0; i<top;++i)
{
// každý objekt je reference (degradovaný pointer), tím pádem můžeme jakoukoli adresu
//přetypovat na referenci ukazující na objekt libovolného typu a opačně referenci na pointer
dtor(cast(T)(buffer+i*finalElSize));
}
}
void dtor(T ob)
{
ob.__dtor();
}
void ctor(Args...)(T ob,Args args)
{
// Statická inicializace objektu [vtable + metadata]
byte * chunk = cast(byte*)ob;
chunk[0..sizeOfEl] = T.classinfo.init[0..sizeOfEl];
// Samotné volání konstruktoru
ob.__ctor(args);
}
};
void main()
{
objectPool!example oPool = objectPool!example(0);
foreach (int i;0..10)
oPool.createInstance(i);
writeln("FINALLLLL STAGEE");
foreach (long i;0..40)
writeln(oPool[i].num);
}
-
Jo fajn, tak sem za debila ... místo
chunk[0..sizeOfEl] = T.classinfo.init[0..sizeOfEl];
má být
temp[0..(size*finalElSize)/2] = buffer[0..(size*finalElSize)/2];
Jaksi sem zapomněl že když to kopíruji po bajtech, nestačí mi zkopírovat jeden bajt na jeden element ... xD
-
zase vedle ...
temp[0..size/2] = buffer[0..size/2];
... *chjo
-
Na inicializáciu som zabudol. Prečo vlastne nepoužieš emplace? Robí to presne čo chceš:
T emplace(T, Args...)(void[] chunk, auto ref Args args) if (is(T == class))
{
enum classSize = __traits(classInstanceSize, T);
testEmplaceChunk(chunk, classSize, classInstanceAlignment!T, T.stringof);
auto result = cast(T) chunk.ptr;
// Initialize the object in its pre-ctor state
(cast(byte[]) chunk)[0 .. classSize] = typeid(T).init[];
// Call the ctor if any
static if (is(typeof(result.__ctor(args))))
{
// T defines a genuine constructor accepting args
// Go the classic route: write .init first, then call ctor
result.__ctor(args);
}
else
{
static assert(args.length == 0 && !is(typeof(&T.__ctor)),
"Don't know how to initialize an object of type "
~ T.stringof ~ " with arguments " ~ Args.stringof);
}
return result;
}
-
Na inicializáciu som zabudol. Prečo vlastne nepoužieš emplace? Robí to presne čo chceš:
T emplace(T, Args...)(void[] chunk, auto ref Args args) if (is(T == class))
{
enum classSize = __traits(classInstanceSize, T);
testEmplaceChunk(chunk, classSize, classInstanceAlignment!T, T.stringof);
auto result = cast(T) chunk.ptr;
// Initialize the object in its pre-ctor state
(cast(byte[]) chunk)[0 .. classSize] = typeid(T).init[];
// Call the ctor if any
static if (is(typeof(result.__ctor(args))))
{
// T defines a genuine constructor accepting args
// Go the classic route: write .init first, then call ctor
result.__ctor(args);
}
else
{
static assert(args.length == 0 && !is(typeof(&T.__ctor)),
"Don't know how to initialize an object of type "
~ T.stringof ~ " with arguments " ~ Args.stringof);
}
return result;
}
Jelikoz se ptal na to jak se zbavit standardni knihovny a podobne, tak predpokladam ze prave proto.
-
Měl bych otázku. Jak napsat bootloader a nebo OS v déčku už víceméně vím (OSDEV -> D bare bones). Jediný problém je DRUNTIME. Bez něj neudělám ani strukturu či nevstoupím do pole -> arr
- . A tady je právě jádro pudla, neznáte někdo nějakou platform independant DRUNTIME? Tím platform independant myslím nezávislost na použité platformě (technicky vzato i na architektuře procesoru). Pokud žádná taková implementace DRUNTIME neexistuje, nezbývá než zahodit D (nemám trpělivost si to implementovat sám). Na OSDEV sem na fóru viděl že ho někdo napsal, ale už to bylo starý a nešlo zpřístupnit zdrojáky :((
-
Najito : https://github.com/xomboverlord/xomb-bare-bones
-
chunk[0..sizeOfEl] = T.classinfo.init[0..sizeOfEl];
temp[0..(size*finalElSize)/2] = buffer[0..(size*finalElSize)/2];
temp[0..size/2] = buffer[0..size/2];
To je nějaký dialekt Brainfucku? ;D
-
xDD, dalo by se říct. Ta syntaxe na tom není to nejhorší. Takže DRUNTIME mám vyřešen, zbývá kompiler, DMD pokud vím tak neumožňuje zakázání přilinkování implicitní DRUNTIME, takže ho nemohu použít. GDC to však umožňuje. Nenašel jsem však žádnou binary relase, pouze zdrojáky na který sem chvilku koukal a pak je smazal protože sem nepřišel na to jak je zkompilovat. Bohužel fedora nemá v repozitáři GDC takže ho nemůžu získat od tud, je tam jen LDC ale to je naprd páč znám jen GNU toolchain (nechce se mi učit další). Byl by někdo tak hodný a dal sem link na nějaký build GDC jež podporuje D2? (D1 build už sem měl...).
-
xDD, dalo by se říct. Ta syntaxe na tom není to nejhorší. Takže DRUNTIME mám vyřešen, zbývá kompiler, DMD pokud vím tak neumožňuje zakázání přilinkování implicitní DRUNTIME, takže ho nemohu použít. GDC to však umožňuje. Nenašel jsem však žádnou binary relase, pouze zdrojáky na který sem chvilku koukal a pak je smazal protože sem nepřišel na to jak je zkompilovat. Bohužel fedora nemá v repozitáři GDC takže ho nemůžu získat od tud, je tam jen LDC ale to je naprd páč znám jen GNU toolchain (nechce se mi učit další). Byl by někdo tak hodný a dal sem link na nějaký build GDC jež podporuje D2? (D1 build už sem měl...).
tak v Archlinuxu se da gdc velmi snadno nainstalovat, pro fedoru je to problem musel by sis to skompilovat sam. Takze bud doporucuji pouzit archlinux, nebo zkusit pouzit balicky z archlinuxu mozna to pojede.
-
Proboha proč kua ... takže měním otázku. Jak to zkompilovat ze source code? Koukal sem na něj a fakt mě nic nenapadlo. Kůli blbýmu kompileru nebudu virtualizovat OS dozadeke ...
-
Proboha proč kua ... takže měním otázku. Jak to zkompilovat ze source code? Koukal sem na něj a fakt mě nic nenapadlo. Kůli blbýmu kompileru nebudu virtualizovat OS dozadeke ...
druha moznost je jak jsem rikal stahnout balicky z archu (pozor jestli mas 386 tak upravit odkazy)
http://mirror.vpsfree.cz/archlinux/community/os/x86_64/gdc-4.8.2-5-x86_64.pkg.tar.xz
http://mirror.vpsfree.cz/archlinux/community/os/x86_64/libgphobos-devel-4.8.2-5-x86_64.pkg.tar.xz
rozbalit je a narvat si je naprase do systemu, mam to odzkouseno ted na fedora20 a podarilo se mi zkompilovat hello world
Jinak pokud si to checs zkompilovat sam, tak tady je jak si to dela arch: https://projects.archlinux.org/svntogit/community.git/tree/trunk/PKGBUILD?h=packages/gdc
-
No, tak sem to rozházel. A háže mi to error : gdc: fatal error: -fuse-linker-plugin, but liblto_plugin.so not found
compilation terminated.
-
No, tak sem to rozházel. A háže mi to error : gdc: fatal error: -fuse-linker-plugin, but liblto_plugin.so not found
compilation terminated.
jj linkovani ti nepujde, jelikoz by sis musel jeste dohrat ty soubory z balicku gcc pro arch, ja to resil tak ze jsem dal gdc -c -o program.o program.d takze se neprovadi linkovani, a linkovani jsme pak dleal zvlast:
gcc -o program program.o -lgphobos2 -pthread -m
-
Vyřešeno, celí obsah adresáře /usr/lib/gcc/x86_64-redhat-linux/4.8.2 sem zkopíroval do /usr/lib/gcc/x86_64-unknown-linux-gnu/4.8.2 a vše šlape jak má
-
RAII: Existují i jazyky, ve kterých bys to Hello World napsal o poznání snáz ;D
-
TO BLA: No, hello world se napíše opravdu snadno:
module core.main;
import std.stdio;
void main()
{
writeln("Hello world");
}
Pokud sleduješ tu anabázi co sem tady řešil, tak to je proto že chci v D napsat bootloader, a bohužel, to není zas tak snadné ... Když máš k dispozici druntime a stdlib, není D problém, až teda na dokumentaci (ale to se zase týká low level)...
-
Přináším pěknej benchmark C++ vs D. Testování spočívá v rychlosti sečtení dvou matic. D je hotovo o !!!30% dřív než C++. Oboje sem kompiloval s -O3 (takže STL kontejnery by měli mít deaktivovaný debug zpomalovadla takže by měli bejt rychlí).
C++
#include <vector>
#include <random>
template <class T>
void addM(vector<vector<T>> & first, vector<vector<T>> & second)
{
if (first.size() == second.size() && first[0].size() == second[0].size())
{
for (int i=0; i<first.size(); ++i)
{
for (int y=0; y<first[i].size(); ++y)
{
first[i][y] += second[i][y];
}
}
}
}
template <class T>
void writeM(vector<vector<T>> & v)
{
for (int i=0; i<v.size(); ++i)
{
for (int y=0; y<v[i].size(); ++y)
{
cout << v[i][y] << " ";
}
cout << endl;
}
}
int main()
{
random_device ranDev;
vector<vector<int>> v;
for (int i=0; i<100; ++i)
v.push_back(vector<int>(100));
for (int i=0; i<v.size(); ++i)
for (int y=0; y<v[i].size(); ++y)
v[i][y] = 10+ranDev()%9;
vector<vector<int>> v2;
for (int i=0; i<100; ++i)
v2.push_back(vector<int>(100));
for (int i=0; i<v2.size(); ++i)
for (int y=0; y<v2[i].size(); ++y)
v2[i][y] = 10+ranDev()%9;
for (int i=0; i<1000000; ++i)
addM(v,v2);
return 0;
}
a D
module zkouskarychlosti;
import std.stdio;
import std.random;
import std.datetime;
void matrixAdd(T)(T[][] firstM, T[][] secondM)
{
if ((firstM.length == secondM.length)&&(firstM[0].length == secondM[0].length))
{
for (int i=0; i<firstM.length; ++i)
{
firstM[i][] += secondM[i][];
}
}
}
void matrixSub(T)(T[][] firstM, T[][] secondM)
{
if ((firstM.length == secondM.length)&&(firstM[0].length == secondM[0].length))
{
for (int i=0; i<firstM.length; ++i)
{
firstM[i][] -= secondM[i][];
}
}
}
void writeM(T)(T[][] mat)
{
for (int i=0; i<mat.length; ++i)
{
for (int y=0; y<mat[i].length; ++y)
{
write(mat[i][y]);
write(" ");
}
writeln(" ");
}
}
void main()
{
StopWatch sw;
sw.start();
const int rowcols = 100;
int[][] fm;
int[][] sm;
fm.length = 100;
sm.length = 100;
foreach (int i;0..rowcols)
{
fm[i].length = 100;
sm[i].length = 100;
foreach (int y;0..rowcols)
{
fm[i][y] = sm[i][y] = uniform(0,9);
}
}
foreach (int i;0..1000000)
{
matrixAdd(fm,sm);
}
writeln(sw.peek().msecs());
sw.stop();
}
-
Přináším pěknej benchmark C++ vs D. Testování spočívá v rychlosti sečtení dvou matic. D je hotovo o !!!30% dřív než C++. Oboje sem kompiloval s -O3 (takže STL kontejnery by měli mít deaktivovaný debug zpomalovadla takže by měli bejt rychlí).
C++
#include <vector>
#include <random>
template <class T>
void addM(vector<vector<T>> & first, vector<vector<T>> & second)
{
if (first.size() == second.size() && first[0].size() == second[0].size())
{
for (int i=0; i<first.size(); ++i)
{
for (int y=0; y<first[i].size(); ++y)
{
first[i][y] += second[i][y];
}
}
}
}
template <class T>
void writeM(vector<vector<T>> & v)
{
for (int i=0; i<v.size(); ++i)
{
for (int y=0; y<v[i].size(); ++y)
{
cout << v[i][y] << " ";
}
cout << endl;
}
}
int main()
{
random_device ranDev;
vector<vector<int>> v;
for (int i=0; i<100; ++i)
v.push_back(vector<int>(100));
for (int i=0; i<v.size(); ++i)
for (int y=0; y<v[i].size(); ++y)
v[i][y] = 10+ranDev()%9;
vector<vector<int>> v2;
for (int i=0; i<100; ++i)
v2.push_back(vector<int>(100));
for (int i=0; i<v2.size(); ++i)
for (int y=0; y<v2[i].size(); ++y)
v2[i][y] = 10+ranDev()%9;
for (int i=0; i<1000000; ++i)
addM(v,v2);
return 0;
}
a D
module zkouskarychlosti;
import std.stdio;
import std.random;
import std.datetime;
void matrixAdd(T)(T[][] firstM, T[][] secondM)
{
if ((firstM.length == secondM.length)&&(firstM[0].length == secondM[0].length))
{
for (int i=0; i<firstM.length; ++i)
{
firstM[i][] += secondM[i][];
}
}
}
void matrixSub(T)(T[][] firstM, T[][] secondM)
{
if ((firstM.length == secondM.length)&&(firstM[0].length == secondM[0].length))
{
for (int i=0; i<firstM.length; ++i)
{
firstM[i][] -= secondM[i][];
}
}
}
void writeM(T)(T[][] mat)
{
for (int i=0; i<mat.length; ++i)
{
for (int y=0; y<mat[i].length; ++y)
{
write(mat[i][y]);
write(" ");
}
writeln(" ");
}
}
void main()
{
StopWatch sw;
sw.start();
const int rowcols = 100;
int[][] fm;
int[][] sm;
fm.length = 100;
sm.length = 100;
foreach (int i;0..rowcols)
{
fm[i].length = 100;
sm[i].length = 100;
foreach (int y;0..rowcols)
{
fm[i][y] = sm[i][y] = uniform(0,9);
}
}
foreach (int i;0..1000000)
{
matrixAdd(fm,sm);
}
writeln(sw.peek().msecs());
sw.stop();
}
To je tím, že ten program v C++ je špatně napsaný. Pokud nahradím všechny for (int i=0; i<v.size(); ++i) za for (int i=0, count = v.size(); i < count; ++i), zrychlí to ten program o 80 %!
-
TO STEN: A zkoušel si to upravit a pustit? Já sem to upravil a hádej co. Úplně stejně rychlé...Možná o pár desítek milisekund rychlejší, D je ale rychlejší o stovky milisekund. Žádných 80% zrychlení se neděje. Ono, schválně se koukni co dělá funkce .size(). Takřka nic, jedná se jen o funkci s jedním řádkem a to returnem hodnoty. Funkce se volaj hodně rychle víme?
-
To je tím, že ten program v C++ je špatně napsaný. Pokud nahradím všechny for (int i=0; i<v.size(); ++i) za for (int i=0, count = v.size(); i < count; ++i), zrychlí to ten program o 80 %!
80% je trochu moc, ale 15 sekund to srazilo na 12. Daleko víc by se IMO dalo získat použitím 1D pole a vlastním indexováním. Takhle se taky matice píšou. Vektor vektorů se sem moc nehodí. Když už nic jiného, tak na rozdíl od matice může být každý z těch vektorů jinak dlouhý a kód by to měl správně kontrolovat.
-
D to dalo za 7,5 s ... C++ za 12 po optimalizaci za 11 ... ano ano, dalo se to vyřešit všemi možnými způsoby, jež by to zrychlily, ale to prosím by šlo udělat i u D ... a opět by bylo rychlejší
-
Tak je to přesně, jak jsem si myslel. Použití 1D pole srazí ten čas na 6 sekund.
template <class T>
void addM(vector<T> & first, vector<T> & second, int r, int c)
{
T * row_first = &first[0];
T * row_second = &second[0];
for(int i = 0; i < r; ++i)
{
for(int j = 0; j < c; ++j)
{
row_first[j] += row_second[j];
}
row_first += c;
row_second += c;
}
}
Nevím jak D implementuje dvorozměrná pole. Ale určitě nepoužívá něco tak neefektivního jako vektor vektorů, ale alokuje to v jednom bloku. Až budeš mít zase chuť porovnávat jazyky, tak se prosím ujisti že neporovnáváš bubble-sort s quick-sortem. Díky.
-
No, je to implementováno jako pole pointerů na pole a navíc je to dynamicky alokováno. S GC. Ještě jednou s GC. Pokud bych to předělal plně na pointery a řešil to jak ty + obešel GC vlastním alokátorem, byl bych zase v úplně jiné dimenzi než C++. Zejtra se na to podívám. Jo a neporovnávám buble sort s quick sortem ale bubble sort s bubble sortem kterej má zasranej pomalej neoptimalizovanej GC xD
-
D to dalo za 7,5 s ... C++ za 12 po optimalizaci za 11 ... ano ano, dalo se to vyřešit všemi možnými způsoby, jež by to zrychlily, ale to prosím by šlo udělat i u D ... a opět by bylo rychlejší
Tohle sis vycucal z levého nebo pravého palce? Až porovnáš dva kódy, kde C++ nebude používat brutálně neefektivní konstrukce, tak to můžeš tvrdit. Hoď sem to nejlepší, co z D dostaneš. Já si schválně pohraju s C++ kódem a uvidíme.
-
D má totiž díky slice a range operátorům možnost dělat SIMD optimalizace což v C++ nejde (ručně ano xD) ... nejsem si ale jistej detailama. Asi se podívám do vnitřností binárky abych zjistil co je na tom pravdy. Jinak objevil sem svojí potencionální chybu ... v C++ používám random_device který je na linuxu pomalí protože čerpá z /dev/random (nebo /dev/urandom ?? nejsem si jist jedno je pomalejší a random_device používá to rychlejší). V D používám implicitní pseudo generátor náhodných čísel. Ale stejně má chyba nemá měřitelný vliv...náhodná čísla sou generována jen 2000krát. Dobře přijímám výzvu, du to začít tunit.
-
To je tím, že ten program v C++ je špatně napsaný. Pokud nahradím všechny for (int i=0; i<v.size(); ++i) za for (int i=0, count = v.size(); i < count; ++i), zrychlí to ten program o 80 %!
80% je trochu moc, ale 15 sekund to srazilo na 12. Daleko víc by se IMO dalo získat použitím 1D pole a vlastním indexováním. Takhle se taky matice píšou. Vektor vektorů se sem moc nehodí. Když už nic jiného, tak na rozdíl od matice může být každý z těch vektorů jinak dlouhý a kód by to měl správně kontrolovat.
Nahradil jsem všechny fory, ne jen jeden. Na mém počítači se tím srazil čas ze 12 sekund na 2,5.
D to dalo za 7,5 s ... C++ za 12 po optimalizaci za 11 ... ano ano, dalo se to vyřešit všemi možnými způsoby, jež by to zrychlily, ale to prosím by šlo udělat i u D ... a opět by bylo rychlejší
To není pravda. Moje zrychlení jen řekne C++, že to pole nemění velikost během výpočtů. Což je přesně to, co dělá i D.
-
Nahradil jsem všechny fory, ne jen jeden. Na mém počítači se tím srazil čas ze 12 sekund na 2,5.
Nepoužil jsi náhodou icc? Ten mi jednou podobný kód zoptimalizoval tak, až mi ho vyoptimalizoval úplně pryč.
-
No, tak sem to pěkně zoptimalizoval no ... na 700 ms takže 0,7s xDDD BTW mám 2 core pentium 3 GHZ (jo jasně někdo to bude mít i v C++ za pár milisekund když použije ňáký monstrum). BTW, ten source code tady nedávám jen tak pro srandu, zkuste si zkompilovat i ten kód Dčka. Jasně máš to za 2,5 sekundy, ale jakej máš stroj? Skus tam zkompilovat i D a uvidíš že bude zase rychlejší!
module zkouskarychlosti;
import std.stdio;
import std.random;
import std.datetime;
import std.c.stdlib;
pure void mAdd(T)(T * matA, const T * matB, in long sm)
{
matA[0..sm] += matB[0..sm];
}
void mWrite(T)(const T * mat,in long width, in long heigth)
{
long totalS = width * heigth;
foreach (long i;0..totalS)
{
if (i == width)
writeln(" ");
write(mat[i]);
write(" ");
}
writeln(" ");
}
void main()
{
StopWatch sw;
sw.start();
int * v0 = cast(int*)malloc(1000*int.sizeof);
int * v1 = cast(int*)malloc(1000*int.sizeof);
foreach (int i;0..1000)
{
v0[i] = v1[i] = uniform(0,9);
}
foreach (int i;0..1000000)
{
mAdd(v0,v1,1000);
}
writeln(sw.peek().msecs());
sw.stop();
free(v0);
free(v1);
}
-
Nahradil jsem všechny fory, ne jen jeden. Na mém počítači se tím srazil čas ze 12 sekund na 2,5.
Nepoužil jsi náhodou icc? Ten mi jednou podobný kód zoptimalizoval tak, až mi ho vyoptimalizoval úplně pryč.
Nn, g++ --std=c++11 -O3 -o test test.cc. Nejspíš to provede částečný unroll (při neustálém ptaní se na size to nejde udělat, protože se mezitím může změnit), takže se tam přes superskalární frontu bude počítat několik hodnot najednou.
-
Nechci do toho kecat, ale ta blbá funkce size tam byla zbytečně volaná zhruba 2200. Tobě se to zrychlilo o 10s. Takže by jedno volání funkce size sežralo 22ms. A TO JE blbnost. Laskavě se koukni na to, co funkce size() dělá. Nevolá žádnou další funkci a její tělo tvoří jediný return jež dojde k vrácené hodnotě přímo. Tam opravdu 22ms nanejdeš. PS. to s tou superskalární frontou ... napiš jakej máš processor...
-
No, tak sem to pěkně zoptimalizoval no ... na 700 ms takže 0,7s xDDD BTW mám 2 core pentium 3 GHZ (jo jasně někdo to bude mít i v C++ za pár milisekund když použije ňáký monstrum). BTW, ten source code tady nedávám jen tak pro srandu, zkuste si zkompilovat i ten kód Dčka. Jasně máš to za 2,5 sekundy, ale jakej máš stroj? Skus tam zkompilovat i D a uvidíš že bude zase rychlejší!
module zkouskarychlosti;
import std.stdio;
import std.random;
import std.datetime;
import std.c.stdlib;
pure void mAdd(T)(T * matA, const T * matB, in long sm)
{
matA[0..sm] += matB[0..sm];
}
void mWrite(T)(const T * mat,in long width, in long heigth)
{
long totalS = width * heigth;
foreach (long i;0..totalS)
{
if (i == width)
writeln(" ");
write(mat[i]);
write(" ");
}
writeln(" ");
}
void main()
{
StopWatch sw;
sw.start();
int * v0 = cast(int*)malloc(1000*int.sizeof);
int * v1 = cast(int*)malloc(1000*int.sizeof);
foreach (int i;0..1000)
{
v0[i] = v1[i] = uniform(0,9);
}
foreach (int i;0..1000000)
{
mAdd(v0,v1,1000);
}
writeln(sw.peek().msecs());
sw.stop();
free(v0);
free(v1);
}
Mám trochu výkonnější procesor (Intel Core i7 2 GHz) ale tohle proběhne za 5 ms (ano, pět) měřeno přes time, samotný výpočet je za méně než 1 ms (pod hranicí měřitelnosti):
#include <iostream>
#include <random>
template <typename T,
unsigned size>
void mAdd(T (&matA)[size],
const T (&matB)[size])
{
for (long i = 0; i < size; ++i)
matA[i] += matB[i];
}
int main()
{
std::random_device rnd;
int v0[1000];
int v1[1000];
for (int i = 0; i < 1000; ++i)
v0[i] = v1[i] = rnd() % 10;
for (int i = 0; i < 1000000; ++i)
mAdd(v0,v1);
return 0;
}
-
Nechci do toho kecat, ale ta blbá funkce size tam byla zbytečně volaná zhruba 2200. Tobě se to zrychlilo o 10s. Takže by jedno volání funkce size sežralo 22ms. A TO JE blbnost. Laskavě se koukni na to, co funkce size() dělá. Nevolá žádnou další funkci a její tělo tvoří jediný return jež dojde k vrácené hodnotě přímo. Tam opravdu 22ms nanejdeš. PS. to s tou superskalární frontou ... napiš jakej máš processor...
Superskalární frontu má každý procesor od Pentia Pro ;)
-
Nechci do toho kecat, ale ta blbá funkce size tam byla zbytečně volaná zhruba 2200. Tobě se to zrychlilo o 10s. Takže by jedno volání funkce size sežralo 22ms. A TO JE blbnost. Laskavě se koukni na to, co funkce size() dělá. Nevolá žádnou další funkci a její tělo tvoří jediný return jež dojde k vrácené hodnotě přímo. Tam opravdu 22ms nanejdeš. PS. to s tou superskalární frontou ... napiš jakej máš processor...
Ne, volá se tam zbytečně asi tak 1 000 000× (ty iterace jsou kvadratické, nesčítají se, ale násobí). Ta funkce se samozřejmě inlineuje, ale neustálé čtení paměti a porovnávání kromě spousty zbytečných tiků taky dost rozbíjí branch predictions a klidně to může mít takovýhle brutální zpomalení.
-
To víme taky ... mě jen zajímalo kolik pipeline má tvoje jedno jádro ... a ano. U mě to trvá taky jen kolem 2ms (to ukazuje vždy ať je v programu cokoli -> chyba měření). Na první pohled si mne tvrdě rozdrtil ale ... ty máš v tý funkci tvrdě přes šablony zadrátovaný počet prvků, jako argument můžeš dát pouze staticky alokované pole (v době kompilace). A to je právě kámen úrazu. Můj kód není závislí na ničem takovém. Vše je pružné, nic není nutno znát v době kompilace. Takže opět, nemůžu uznat porážku neb tvůj program nezvládne to co můj. P.S. Nezdá se mi to tvoje zrychlení o 10s jen díky odstranění tý funkce size, i přesto že máš i7, je to nesmysl (já mám Pentium za tisícovku).
-
Dostals mě, funkce size se tam volá 20200. To dává zhruba 0,5 ms za sekundu. Ve funkci size() je 13 instrukcí. Najdi si jaký IPS má tvůj procesor. Ale ve zkratce: 0,5ms na 13 instrukcí je hezká pohádka. Přístup do cache je zhruba 8 ns a jedna instrukce je 1ns. Přístup do registru zhruba 2ns. Zpočítej si to a uvidíš ten extrém xD (ty hodnoty sem někde vyděl na stránkách, netušim jak starý sou, dneska může být všechno rychlejší + i7...)
-
Hovno " To dává zhruba 0,5 ms za sekundu." -> má být To dává 0.5ms na jedno volání funkce size
-
To víme taky ... mě jen zajímalo kolik pipeline má tvoje jedno jádro ... a ano. U mě to trvá taky jen kolem 2ms (to ukazuje vždy ať je v programu cokoli -> chyba měření). Na první pohled si mne tvrdě rozdrtil ale ... ty máš v tý funkci tvrdě přes šablony zadrátovaný počet prvků, jako argument můžeš dát pouze staticky alokované pole (v době kompilace). A to je právě kámen úrazu. Můj kód není závislí na ničem takovém. Vše je pružné, nic není nutno znát v době kompilace. Takže opět, nemůžu uznat porážku neb tvůj program nezvládne to co můj. P.S. Nezdá se mi to tvoje zrychlení o 10s jen díky odstranění tý funkce size, i přesto že máš i7, je to nesmysl (já mám Pentium za tisícovku).
Vytáhl jsem to nejlepší, co dokáže C++, tak zněla pravidla ;)
Pokud tam dám dynamické velikosti, zpomalí se to na 210 až 230 ms. To Déčko zkompilované přes gdc -O3 to udělá za 770 až 800 ms.
Dostals mě, funkce size se tam volá 20200. To dává zhruba 0,5 ms za sekundu. Ve funkci size() je 13 instrukcí. Najdi si jaký IPS má tvůj procesor. Ale ve zkratce: 0,5ms na 13 instrukcí je hezká pohádka. Přístup do cache je zhruba 8 ns a jedna instrukce je 1ns. Přístup do registru zhruba 2ns. Zpočítej si to a uvidíš ten extrém xD (ty hodnoty sem někde vyděl na stránkách, netušim jak starý sou, dneska může být všechno rychlejší + i7...)
Ne, volá se to víc než milionkrát. Jen v:
for (int i=0; i<first.size(); ++i)
{
for (int y=0; y<first[i].size(); ++y)
{
se volá 1 001 000 × (1 000 × ve vnějším foru a 1 000 × ve vnitřním foru pro každou iteraci vnějšího foru).
-
juj, chybka, Zapoměl sem započítat ještě cykli v sčítací funkce takže je to 30200 volání (jakej milion?? 100*100=10000*3=30000). Takže místo 0,5 ms to je 0,33 ms což je 330 ms -> 330 000 mikros -> 330 000 000 ns. Nahoře sem psal 13 instrukcí a kolik zhruba zaberou času. Takže furt máš co vysvětlovat. Jo jasně, nedohodli sme jasná pravidla...vyhrál si. ALE, stejnou schopnost má i D (staticky alokovaná pole + hodnotové parametry šablon). takže to můžu na 2ms dát jak ty.
-
jo...zase chyba. Bude to ten 1000 000...
-
On je v tom originálním kódu ještě jeden brutální zádrhel a to
a[i][j] += b[i][j]
Při každém indexování prvku to leze nejdřív do vnějšího vektoru aby z něj vytáhlo znova a znova odkaz na stejný řádek. Proč to překladač nedokáže zoptimalizovat nevím, možná kvůli potenciálnímu aliasu. Hlavně to nejspíš nikdo neřeší, protože tenhle typ kódu se v kritických částech nevyskytuje a pokud ho někdo použije, dobře mu tak.
Dčkový přístup odpovídá tomu, kdyby se v C++ použila místo vnitřního vectoru valarray. Ta se ovšem takřka nepoužívá, protože tam kam se hodí valarray se obvykle hodí i daleko mocnější expression templates.
-
Takže 0,01 ms. to je 10 ms -> 10 000 000 ns. Furt mi to na těch 13 instrukcí nesedí xDD ale máš pravdu, to s tím počítáním funkcí sem posral totálně
-
Takže 0,01 ms. to je 10 ms -> 10 000 000 ns. Furt mi to na těch 13 instrukcí nesedí xDD ale máš pravdu, to s tím počítáním funkcí sem posral totálně
0,01 ms je 10 000 ns ;)
-
No tak nic no :( zkusil sem to zoptimalizovat šablonama a dalo to 880 ms (!VÍC!). V tomto má GDC mezeru, není to ale chyba jazyka ...
-
BTW, všimli jste si že při tom přechodu od 2D k brutálně zoptimalizovanému kódu 1D vám tam vypadla jedna nula, že? Těch 700ms najednou není kdovíjak brutální zrychlení.
-
vidíš 0,01s -> to je 10 ms ... přepsal sem se xD
-
TO JSH -> to brutální zrychlení udělalo G++ díky šablonám. Stejnou schopnost má i D, akorát GDC to nezoptimalizovalo :(. Jinak pokud se bavíme o dynamických maticích (nezadrátovaných v době kompilace), je D vítěz.
-
A ještě jsem si teď všiml, že ty počty nesedí. Ta funkce size se v původním kódu jenom v tom vnitřním foru volala 10 000 000 000× (100 vnitřní for × 100 vnější for × 1 000 000 iterací sčítání). Po úpravě na count se snížil počet volání na 100 000 000×.
Jinak pokud se bavíme o dynamických maticích (nezadrátovaných v době kompilace), je D vítěz.
Cože? Když jsem v tom mém kódu tu šablonu změnil na dynamickou velikost, tak C++ to dalo za cca 220 ms a D za cca 780 ms.
-
dáš sem prosím ten 220 ms C++ kód? Asi si ho už někde dával, ale nechce se mi hledat. AD, no funkce size se spustí 10 100 022 200. 990 ns na jedno volání funkce. Je v ní 13 instrukcí. Jedno se ti musí nechat, počítat umíš. Ale 13 instrukcí prostě 990 ns nedá ani zbla. Když budu počítat že každá instrukce přistupuje do registru a paměti. Tak jedna instrukce sežere 1ns + 2ns + 8ns = 11ns. Za 990 ns bys stihnul kolem 90 takovejhle instrukcí. A to počítám s odhadem, tvůj procák bude mnohem rychlejší (IPS ??). BTW, některé instrukce pracují jen s registry takže v tom případě jen 5ns
-
TO JSH -> to brutální zrychlení udělalo G++ díky šablonám. Stejnou schopnost má i D, akorát GDC to nezoptimalizovalo :(. Jinak pokud se bavíme o dynamických maticích (nezadrátovaných v době kompilace), je D vítěz.
Nikoliv, pořád jsi neposlal zoptimalizovaný kód který by sčítal co nejrychleji dvě matice 100x100 (dohromady 10k prvků). Co jsi sem dal byl zoptimalizovaný kód který sčítá 1000 prvků, tedy 10x míň. Pokud budu vařit z vody a vynásobím dobu běhu 10x, tak jsem na 7 sekundách. To je +- to samé, co jsi psal ve 20:08 o svém původním kódu.
-
Další chybka. Zapoměl sem jednu 0 ... no výsledek je 6,7s...škoda 700ms vypadalo cool ...
-
HEJ drsný. Zkusil sem DMD, 10 000 prvků to dalo za 1,7s. Ještě zkusim u DMD optimalizaci šablonama. Vypadá to že GDC je pěkne neoptimalizovaný.
-
Tak nic s šablonovejma optimalizacema je to 2s. Jak vidno ani DMD to nemá zoptimalizováno (to spíš zhoršuje xD zajímavé).
-
dáš sem prosím ten 220 ms C++ kód? Asi si ho už někde dával, ale nechce se mi hledat.
#include <iostream>
#include <random>
template <typename T>
void mAdd(T *matA,
const T *matB,
unsigned size)
{
for (long i = 0; i < size; ++i)
matA[i] += matB[i];
}
int main()
{
std::random_device rnd;
int v0[10000];
int v1[10000];
for (int i = 0; i < 10000; ++i)
v0[i] = v1[i] = rnd() % 10;
for (int i = 0; i < 1000000; ++i)
mAdd(v0,v1,10000);
return 0;
}
Tohle už má 10 000 prvků a běží 2,4 až 2,5 s.
AD, no funkce size se spustí 10 100 022 200. 990 ns na jedno volání funkce. Je v ní 13 instrukcí. Jedno se ti musí nechat, počítat umíš. Ale 13 instrukcí prostě 990 ns nedá ani zbla. Když budu počítat že každá instrukce přistupuje do registru a paměti. Tak jedna instrukce sežere 1ns + 2ns + 8ns = 11ns. Za 990 ns bys stihnul kolem 90 takovejhle instrukcí. A to počítám s odhadem, tvůj procák bude mnohem rychlejší (IPS ??). BTW, některé instrukce pracují jen s registry takže v tom případě jen 5ns
10 sekund / 10 100 022 200 = 0,99 ns (http://www.wolframalpha.com/input/?i=10+s+%2F+10100022200) ;D
-
to Sten: jinak ten kod v c++ co si udaval jak mel bezt 5ms, tak neni divu ze bezel tak malo, zkus si tam pridat treba vypis jednoho prvku z toho pole, aby to kompilator zcela nesmazal ten cyklus :D. Takze to vlastne vubec nic nedelalo, proto tech 5ms ;-), tech 239ms je uz spravne, D to dokaze za 520ms to znamena ze je priblizne 2x pomalejsi v tomto pripade.
-
Takže rekapitulace -> tvůj kód bez šablonovejch optimalizací sežere 2,4 s. Muj 1,7s. 10 sekund / 10 100 022 200 = 0,99 ns. Hele, furt na to koukám a kroutím hlavou. Neuvědomuju si kde sem udělal chybu ale to je teď jedno. Jaký IPS má proboha tvůj procák? Na jedno volání funkce s 13 instrukcema totiž padne 1ns... ...
-
to Sten: jinak ten kod v c++ co si udaval jak mel bezt 5ms, tak neni divu ze bezel tak malo, zkus si tam pridat treba vypis jednoho prvku z toho pole, aby to kompilator zcela nesmazal ten cyklus :D. Takze to vlastne vubec nic nedelalo, proto tech 5ms ;-), tech 239ms je uz spravne, D to dokaze za 520ms to znamena ze je priblizne 2x pomalejsi v tomto pripade.
ta rychlost pro D je v pripade gdc, pokud se pouzije oficialni kompilator dmd tak to ma vykon okolo 200ms takze je to srovnatelne, pravdepodobne nejaka chybka, zareportuji to jak na gdc tak i na ldc, protoze se to tyka obou kompilatoru. Ackoliv je mozne ze je problem uz ve frontu, kde dmd je ve verzi 2.65 kdezto ldc i gdc maji teprve front ve verzi 2.63
-
Takže rekapitulace -> tvůj kód bez šablonovejch optimalizací sežere 2,4 s. Muj 1,7s.
Na různých procesorech a nejspíš s různým měřením, což je porovnání jak noha. Když jsem vzal poslední zde postovaný kód (http://forum.root.cz/index.php?topic=8481.msg87552#msg87552), změnil 1000 na 10000, vyhodil StopWatch a změřil to přes time, trvá to Déčku 7,7 s na procesoru, kde C++ sežere 2,4.
10 sekund / 10 100 022 200 = 0,99 ns. Hele, furt na to koukám a kroutím hlavou. Neuvědomuju si kde sem udělal chybu ale to je teď jedno. Jaký IPS má proboha tvůj procák? Na jedno volání funkce s 13 instrukcema totiž padne 1ns... ...
Jestli to nebude tím, že se žádná funkce nevolá, protože se inlineuje, a pak to použije jen jednu instrukci ;)
-
to Sten: jinak ten kod v c++ co si udaval jak mel bezt 5ms, tak neni divu ze bezel tak malo, zkus si tam pridat treba vypis jednoho prvku z toho pole, aby to kompilator zcela nesmazal ten cyklus :D. Takze to vlastne vubec nic nedelalo, proto tech 5ms ;-), tech 239ms je uz spravne, D to dokaze za 520ms to znamena ze je priblizne 2x pomalejsi v tomto pripade.
Hmm, tak to jsem nezkoumal, čekal jsem, že to kompilátor celé vypočte za compile time :)
-
To stopWatch tam funguje perfektně jen ho tam nech. Pokud si navíc čet diskuzi, tak víš že po kompilaci na DMD to sežere 2s u mne. Když použiju GDC tak 7,6. Ale to je bug GDC.
-
Jo tak ji udělá inline, a co? tak se holt vypustí pár instrukcí (call a ret + stack frame zůstane (možná)). Neprovede se jen jedna instrukce.
-
To stopWatch tam funguje perfektně jen ho tam nech. Pokud si navíc čet diskuzi, tak víš že po kompilaci na DMD to sežere 2s u mne. Když použiju GDC tak 7,6. Ale to je bug GDC.
Akorát to měří něco jiného (a menšího) než time při měření toho C++. Pokud to má k něčemu být, je potřeba to měřit to stejným způsobem.
Jo tak ji udělá inline, a co? tak se holt vypustí pár instrukcí (call a ret + stack frame zůstane (možná)). Neprovede se jen jedna instrukce.
OK, neprovádí to jednu instrukci, ale dvě. Načte to konec pole a odečte jej od začátku (který se načítá stejně, protože se s ním v tom cyklu pracuje v operator []).
mov 0x8(%rcx,%r14,1),%rax
sub %rdx,%rax
-
To stopWatch tam funguje perfektně jen ho tam nech. Pokud si navíc čet diskuzi, tak víš že po kompilaci na DMD to sežere 2s u mne. Když použiju GDC tak 7,6. Ale to je bug GDC.
No tak jsem delal testy a neni to bug gdc ale obecne toho frontendu ktery je v aktualni verzi gdc co pouzivas. Takze jakmile vyjde gdc a i ldc s verzi frontendu 2.64 ci vyssi tak uz to bude ok.
-
Průměrně líný programátor v C++ použije tohle:
#include <boost/numeric/ublas/matrix.hpp>
#include <random>
int main()
{
boost::numeric::ublas::matrix<int> m1(100, 100);
boost::numeric::ublas::matrix<int> m2(100, 100);
std::random_device rnd;
for (unsigned i = 0; i < m1.size1(); ++i)
for (unsigned j = 0; j < m1.size2(); ++j)
m1(i, j) = m2(i, j) = rnd() % 10;
for (int i = 0; i < 1000000; ++i)
m1 += m2;
return 0;
}
Zkompilované bez debugů:
g++ -O3 -std=c++0x -DBOOST_UBLAS_NDEBUG main.cc -o main
Je to o 25% pomalejší než Stenův ručně optimalizovaný kód, což mi přijde jako akceptovatelná cena za obecnost matice a za 2D reprezentaci v paměti. Mimochodem jak někdo psal, že v C++ se neprovedou SIMD optimalizace, není to pravda, počítá se to SIMD instrukcemi:
.L28:
movdqu (%rax,%rcx), %xmm1
movdqu (%rax,%rdx), %xmm0
paddd %xmm1, %xmm0
movdqa %xmm0, (%rdx,%rax)
addq $16, %rax
cmpq $400, %rax
jne .L28
-
To sem říkal já. Je to pravda, kompiler c++ není schopen udělat simd optimalizace oproti D compileru. Tys tam pouzivas člověkem optimalizovanou knihovnu která pogužívá simd instrukceh nic víc nic min. A pokud si nepochopil tak mi jsme souperili o to který jazyk je rychlejší ne o znalosti knihoven. Omluvte můj psanej projev, píšu na mobilu a navíc to psací pole tady na fóru je luxusně bugli takže co napisu uz neopravim.
-
To sem říkal já. Je to pravda, kompiler c++ není schopen udělat simd optimalizace oproti D compileru. Tys tam pouzivas člověkem optimalizovanou knihovnu která pogužívá simd instrukceh nic víc nic min. A pokud si nepochopil tak mi jsme souperili o to který jazyk je rychlejší ne o znalosti knihoven. Omluvte můj psanej projev, píšu na mobilu a navíc to psací pole tady na fóru je luxusně bugli takže co napisu uz neopravim.
To se mýlíš, boost je portabilní source code knihovna, ve které žádný assembler není, SIMD instrukce generuje překladač. Stenův kód se na SIMD instrukce rozgeneruje úplně stejně:
.L12:
movdqa (%rbp,%rax), %xmm0
paddd (%rbx,%rax), %xmm0
movdqa %xmm0, (%rbx,%rax)
addq $16, %rax
cmpq $40000, %rax
jne .L12
Jediný rozdíl oproti boostu je v tom, že se to všechno spočítá v jednom cyklu místo dvou vnořených (1D versus 2D pole), proto je boost o něco pomalejší.
-
To sem říkal já. Je to pravda, kompiler c++ není schopen udělat simd optimalizace oproti D compileru. Tys tam pouzivas člověkem optimalizovanou knihovnu která pogužívá simd instrukceh nic víc nic min. A pokud si nepochopil tak mi jsme souperili o to který jazyk je rychlejší ne o znalosti knihoven. Omluvte můj psanej projev, píšu na mobilu a navíc to psací pole tady na fóru je luxusně bugli takže co napisu uz neopravim.
Nee tady se pletes. Jde o to ze ty optimalizace dela v obou pripadech az gcc. Takze ekvivaletni kod v D a v C++ je v takto jednoduchych prikladech velmi casto v podstate stejny. Jde o to ze gdc je jen frontend D + gcc jako backend. A jelikoz se vetsina optimalizaci provadi az na urovni backendu, tak je velka sance ze vysledny vygenerovany kod jak pro D verzi tak i pro C++ verzi bude velmi podobny. Samozrejme je treba takto porovnavat stejne backendy (gdc x g++, ldc x llvm-clang, dmd vs dmc).
Samozrejme i frontend ma vliv na rychlost a "optimalizace", viz treba ten bug v D frontendu ve verzich nizsich jak 2.64.
-
Ok, přiznávám svou chybu. Asi se do toho ponořím a kouknu se pořádně do vnitřností, nechci se ohánět bludy.
-
Zdravim, tak jak vidim tak sa tu konecne zacalo riesit nieco ohladom Dcka :D
Ja ho uz 2. rokom pouzivam na programovanie svojho operacneho systemu a frameworku. Zatial som z Dckom nemal ani najmensi problem a vsetko sa v nom dalo elegantne vyriesit. A oproti C++ je to prechadzka ruzovym sadom :D
Niekdo sa tu pital nieco ohladom druntime platform independant a programovaniu OS.
No ja pouzivam vzdy najnovsiu verziu Dcka aktualne 2.065, kompilator DMD a originalny druntime v ktorom som si akurat odstavil GC a premostil ho na pouzitie vlastnych funkcii malloc, free. Vsetky casti druntimu ktore pouzivam su platform independant tak ze s tym problem nemam.
-
TO Bloodman: Neleakují paměť některé funkce v druntime? Pokud ne tak je to celkem fajn. Škoda je, že paměť se leakne bez GC dost snadno. Ne všude kde by se hodilo volat funkci z druntime, se volá funkce z druntime:
int[] arr1 = [47,85,96];
int[] arr2 = [88,96,75];
arr1 = arr2;
Původní paměť v arr1 leakne a pokud vím, tak při týhle operaci se nevolá žádná funkce v druntime, což je škoda.