Portál AbcLinuxu, 30. dubna 2025 21:23
Tento blog byl smazán autorem
Tiskni
Sdílej:
7) ne, to nezávisí na architektuře, ale jen a jen na překladači.
Teď jsem to zkoušel na msvc 2003. Výsledky v debug a release se liší.
Bohužel, gcc trvá na stejném výsledku bez ohledu na optimalizaci, a pak že výsledný kód nemá být pomalý.
Doporučuju prostudovat normu C (ISO/IEC 9899:1999 (E)), paragraf 6.5, odstavec 2.
int x = 0; printf("%d %d %d %d\n", x++, x++, ++x, ++x);A výsledek je naprosto nečekaný, řekl bych - vadný. Ponaučení z tohoto příkladu mělo být totiž takové, že se vyplatí něco takového nepoužívat (tedy pokud se nejedná o jeden parametr).
Asi mi unika nejaky trivialni poznatek, nebot vypadate, ze o c/c++ par veci vite, a zda se mi velmi nepravdepodobne, ze byste neznal tu o "sequence points" a "undefined behavior". Ale muj prvotni nazor by byl, ze tohle je klasicka ukazka nedefinovaneho chovani, ty parametry se mohou vyhodnotit v jakemkoliv poradi, takze jaky vysledek pro vas byl "naprosto necekany"?
int x = 0; printf("%d %d %d %d\n", x++, x++, ++x, ++x);
„Zkoušel jsem na VC8.0 toto: printf("%d %d %d %d\n", x++, x++, ++x, ++x); A výsledek je naprosto nečekaný, řekl bych - vadný.“
Žádný výsledek není vadný, může Vám vyjít naprosto cokoliv a je to úplně v pořádku. Všichni programátoři, a všechny normy C/C++ hovoří, že zde je výsledek nedefinován – takže věta„vadný výsledek“ není na místě. Je to nepochopení funkce C/C++.
Chcete říct, že konvenci cdecl (printf) může každý překladač implementovat jinak?Konvence
cdecl
je čistě microsoftí záležitost a navíc s pořadím aplikování side-effectů ve výrazech moc nesouvisí.
„Chcete říct, že konvenci cdecl (printf) může každý překladač implementovat jinak ?“
Může. A také implementuje.
Cdecl není žádný standard – je to pouze princip. Proto také při mixování funkcí mezi různými kompilátory (například sdílená knihovna přeložená kompilátorem X a používaná programem vyšlým z jiného kompilátoru) se používá jen podmnožina možností cdecl/stdcall.
Řada věcí se v konvenci nedefinuje vůbec. Například co se stane, když uložíte char a vyberete int. Ve vyšších bajtech můžete mít bordel, nebo nuly – dle situace. Není nikterak standardizováno, jak se uloží třeba struktury, ani jak se vrací. Není mezi kompilátory standardizován ani způsob a forma návratové hodnoty. Například v řadě případů si kompilátor optimalizuje tak, že namísto návratové hodnoty skrytě přidá na zásobník před první parametr další parametr, který pak slouží k předání návratové hodnoty funkce, zatímco jiný kompilátor to tak neudělá.
Každý programátor v C/C++ by si měl ve věcech udělat nejdříve pořádek. Jsou věci standardizované a normované, které fungují vždy přesně stejně. Pak jsou věci de facto standardizované, jako třeba, že char má 8 bitů (což norma nenařizuje).
A pak jsou extenze, jako třeba stdcall, nebo cdecl, které nemají naprosto žádný standard a každý kompilátor jej implementuje s odchylkami. Základ je, že stejně se implementuje jen předávání/vracení int a pointerů – a zbytek je compiler depended.
Proto se mi tak strašně nelíbí Vaše implicitní předpoklady, které nemusí vůbec platit. Je hezké si domýšlet, ale daleko lepší je vědět a být si jistý. To druhé je lepší cesta.
8) Pokud bereme float jako normalizovane IEEEx.x, tak bych řekl, že by to na každé platformě, kde je int 32 bitů (mohl jsem tam napsat radší int32_t) mělo být stejné.... a kde se obojí ukládá ve stejném pořadí bytů [a pokud ten int bude unsigned]
Souhlas. Spousta lidi se snazi byt "chytra", a pritom akorat udelaji kod temer necitelny.. http://dl.fefe.de/optimizer-isec.pdf
Pokud by to 1024 nebylo číslo, ale proměnná,a pokud to nebudou mocniny cisla 2?
Já jen používám mě blízké číslaaha... vzhledem ke stupidnosti predchozich (a vlastne vsech) otazek jsem predpokladal, ze to chces taky resit bitovyma operacema
ono ale ze su 1.5x vacsie binarky (aj ked sa mi to nezda, urcite sa to vzdy nejak doladit, stipnut a pod.) neznamena, ze to produkuje 1.5x dlhsi kod... a aj keby bol 1.5x dlhsi ten kod, tak to neznamena, ze bude bezat dlhsie ako ten z msvc...
optimalizovat velkost kodu pri aktualnych stavoch diskov a ram sa mi zda nepodstatne - aj ked, samozrejme, treba mat hranice... nedavno sa mi podarilo vygenerovat z 50 riadkov 1MB binarku (kvoli wrapperom), to uz je vela, no :))
nedavno sa mi podarilo vygenerovat z 50 riadkov 1MB binarku (kvoli wrapperom), to uz je vela, noTo jsou ty header-only knihovny
Asi jste minul poučku, že libovolný program lze libovolně krát urychlit na úkor nabobtnání jeho velikost.
Takže kromě velikosti ještě změřte čas běhu programu.
Třeba takový firefox kompilovaný MSVC měl být až o 10% rychlejší (teda aspoň to margetingově prohlašovali:)Ak je tá verzia, čo sa dá stiahnuť zo stránok Mozilla kompilovaná pomocou MSVC, tak je naozaj (aspoň pokiaľ ide o javascript) výrazne rýchlejšia ako tá pre Linux (u mňa je V8 test a Dromaeo vo Firefox cez Wine rýchlejšie o viac ako 20% ako vo verzii pre Linux). Test som robil po tom, ako som si prečítal Browser benchmarks 2: even Wine beats Linux Firefox. :)
No, mně je C++ úplně ukradený. Ale tohle:
Zanedbejte možnost přetečení datového typu.
mi teda žlučí pěkně hnulo.
Jednicka je trivialni, dvojka ma divne zadani, o "zaokrouhlení proměnné na 4" jsem neslysel. Jestli se mysli nasobek 4, tak dtto. Trojka je docela znama ale k nicemu protoze to prekladac zcela bezne dela sam, takze tim programator pouze prasi kod. Ctyrka je pekna, to uz moc lidi nezna, ale v dobe MMX/SSE/... taky passe. Petka viz 3. Sestka ma chybne zadani, chovani STDCALL je platformove zavisle (cdecl na nixu, pascal na winech, jedno pobezi druhe ne). Navic nechapu proc se tam blbne s uchylnym C++ castem, misto toho obvykleho zavorkoveho. Sedmicka je docela znama, to bude kazdy znat. Osmicka dtto- IEEE formaty z hlavy nevim, ale tenhle bit je jasnej. Devitka- na tom se snad nekdy spalil kazdy, tohle mi na C docela vadilo. Veci jako typedef struct { .. } Foo[1]
maji sve vyhody, ale nevyhody podle me prevazuji.
Jojo, mate pravdu, overeno, omlouvam se. Nikdy jsem STDCALL nepouzival, ale protoze je to makro co se nastavuje kdesi hluboko v headerech tak jsem cekal ze je to platformove zavisle jako "OS nativni" konvence.
Řada otázek (ty bitové) nemá nic moc společného s Cčkem a lépe by vyzněla do assembleru. A v tom už jsou podobné a řada dalších dost hezky popsané třeba v Knuthově Bitwise tricks and techniques, dokonce to snad teď jde i stáhnout.
ad (7); ano, dokonce C99 primo explicitne mluvi i o funkcich
6.5.2.2 Function calls
10. The order of evaluation of the function designator, the actual arguments, and subexpressions within the actual arguments is unspecified, but there is a sequence point before the actual call.
(5) je zajímavá ... ale proč by někdo něco takového dělal?Třeba aby z algoritmu s kvadratickou složitostí udělal algoritmus s lineární složitostí, když optimalizace je to triviální a na čitelnosti neubere?
No, neco zkusim:
ad B - je klasicky POPCOUNT, je o tom cela kapitola v Beautiful Code.
ad C - bud x vstup, y vystup, x obsahuje jednickove i nulove bity:
z=1;
while ((mask=((x+z)^x))==z) z<<=1;
y=((x&mask)<<1)|(x&~mask)
Mohlo by to tak byt?
Zkusim jeste A.
Ha, uz mam asi i to A, staci otestovat:
(x - ~((~x)+1)) == 1
Pokud plati, je cislo x mocninou dvojky.
Tim se zlepsuje i moje reseni C nahore (mel jsem to podezreni), staci misto cyklu udelat:
z = ((x ^ ~((~x)+1))+1)>>1;
mask= (x+z)^x
;A to uz by mohlo byt ono.
To je určitě dobrý začátek, ale jde to ještě daleko přímočařeji.
(x - ~((~x)+1)) == 1
Hlavne je to spatne, jak jsem zjistil rano. Ale skutecne to jde primocarejii:
(x & (x-1)) == 0
Ze by?
ad B - je klasicky POPCOUNT, je o tom cela kapitola v Beautiful Code.Jo. A zhruba v půlce té kapitoly jsem přestal chápat
No, me na tom nejvic udivil ten fakt, ze staci ta prorotovana cisla proste secist (a mozna vysledek jeste nejak zpracovat, nejsem si ted jisty). Ale neresil jsem to take proto, ze kdybych to nahodou potreboval (jako ze na modernich procesorech je to trochu passe), tak vim, ze to Martin Mares ma ve sve knihovne (dela to tam jinak, pres bitove manipulace, coz je primocarejsi a efektivnejsi).
Spousta nadhernych hacku v C je zde http://www.cl.cam.ac.uk/~am21/hakmemc.html
union { int i; float f; } u; if ((float)u.i == u.f) /* then we have u.i = 0x00000000 */je v pořádku, kdežto já když použiju union, tak se hned objeví rýpalové
size_t sum(const unsigned char* str) { register size_t result = 0; for(; *str; result += *str++); return result; }6) Pokud je STDCALL definován jako __stdcall a CDECL jako __cdecl a FASTCALL jako __fastcall, tak se u __stdcall stane to, že zeserete zásobník, protože uklízí volaná fce (z toho důvodu nelze použít ... operátor). U __fastcall se první dva argumenty "kydnou" do registrů a zbytek jako __cdecl. BTW ve Win64 je to všechno __fastcall. 7) "3 2 1 0"? (podváděl jsem, myslel jsem si "0 1 2 3"
Ten specifikátor storage class, nebo jak se to správně nazývá, "register", snad není třeba. Doufám, že překaldače nejsou úplné cvičené opice. Při -O2 se snad proměnné zbytečně na stack nedávají. Mýlím se?
Jinak některé správné odpovědi:
ad 5) správné řešení neexistuje – je to silně závislé na konkrétním optimalizátoru toho kterého kompilátoru, také na volbávh překladače, a správné řešení se tedy liší podle kompilátoru, a dokonce se může lišit i podle verze. Navíc na každém procesoru to rovněž může být jinak. Optimalizátor může kód naprosto a totálně překopat.
ad 6) může se stát cokoli, třeba pokud bude #define STDCALL __fastcall, určitě se stane něco jiného, než když bude #define STDCALL __cdecl. Žádná slova STDCALL, STDCALL atd.. nejsou součástí standardu C/C++ kompilátorů, a ani standardní součástí kompilátorů na x86
ad 7) výsledek je nedefinovaný a je závislý na kompilátoru
ad 8) Na každém procesoru, nejenom x86, dojde k obrácení znaménko reálného čísla. Zde je výsledek de facto nezávislý na procesoru, protože způsob reprezentace reálných čísel je určen multiplatformovou normou IIEE 754. Čistě teoreticky float jí nemusí vyhovovat, čistě v praxi téměř vždy.
ad 9) První printf – počet členů, druhé printf – délku pointeru / délku bázového typu pointeru, v tomto případě 1.
„Tak pokud jste nepochopil, co asi může znamenat STDCALL“
Samozřejmě, že pochopil, ale problém je, že to tak znamenat nemusí. Pokud se zadává test, měl by být jednoznačný.
Nejvíce chyb v programech vzniká na základě faktu „programátor něco předpokládá, zato program na rozdíl od předpokladu provede co je napsáno, často zcela něco jiného, než si programátor myslí“. Tak vznikají nejzapeklitější chyby.
Já si myslím, že ta optimalizace existuje, projeví se snad všude, je naprosto kardinální, jednoduchá a přímočará, ale taky jsem si ji hned nevšiml.
Jestli myslíš to s tím register, jak psal někdo nademnou, tak to jsem nemyslel. Myslel jsem to s tou složitostí O(n) a O(n^2), jak psal někdo taky někde tady. Pravda je, že bych si toho nevšiml, ač jsem se s tím už jednou setkal => kvíz byl určitě užitečný a nevim, proř jsi ho nedal do zdejších kvízů. www.abclinuxu.cz/hry
To platilo kdysi, ale dnes už moc ne. Naopak dnes je nejlepší psát tak, aby kód šel co nejlépe udržovat a s použitím rozumně efektivních algoritmů.
Jediné co by snad šlo v bodě 5 zoptimalizovat je vyhození strlen před for, i když teoreticky vynikající optimalizátor by měl být schopen toto udělat sám (funkce strlen je pro něho známá a obsah pole se nemění – třeba fortran takové optimalizace na známých funkcích dělá zcela běžně).
Pro dobře optimalizující kompilátor není rozdíl, zda k poli přistupujete přes indexy, nebo přes pointer, který postupně inkrementujete. Pokud je mezi tím rozdíl, pak kompilátor špatně optimalizuje. Navíc na platformě x86 je i na úrovni strojového kódu rychlost přístupu přes index a přes pointer velmi blízká, u jiných procesorů jsou rozdíly často větší, nicméně i tak dobře optimalizující kompilátor ve zdrojov=m kódu převede jedno na druhé.
Dobře optimalizující kompilátor by dokonce měl být schopen celou smyčku převést na vektorové operace na SIMD strojových instrukcích, kdy najednou paralelně v jednom taktu vykonává třeba 16 průchodů cyklem v jednom taktu, protože daný algoritmus je dobře paralelizovatelný. A třeba Intel kompilátor to často i takto vymyslí. Zároveň mu toto nejste schopni moc v kódu napovědět, protože C/C++ nemá syntaxi na to to vůbec napsat, on na to Intel kompilátor analýzou přijde sám.
Ono stejně tak kdysi platilo třeba, že při tiskařské sazbě je třeba používat montérky, protože se jinak se umažete, ale to vám dnes třeba u Scribusu, nebo TeXu nehrozí. A stejně tak heslo „Thinking Low Level, Writing High Level“ už nemá zcela platnost a často jím naděláte více škody a vyrobíte pomalejší programy. Kdysi totiž věci bývaly jednodušší, kompilátory špatné, optimalizátory velmi mizerné, instrukční sada procesorů nebyla paralelizovatelná, struktury procesorů průzračné a procesory pomalé. Dnes už je všechno složitější, a troufám si říct, že lidí, kteří dokážet dobře vydedukovat, jak co bude na low level úrovni probíhat je naprosto mizivé množství. Daleko více je tu mýtů o low level úrovni.
Ad 1)
Já jsem jenom pochopil, že kdybyste zadával programátorské zadání Vy, tak by vzniklo obrovská spousta zmatků.
Já bych naopak doporučil Thinking High Level – v prvé řadě bych úplně zapomněl, že nějaké funkce typu strlen, strcat a strxxx existují. Zapomněl bych, že řetězce jsou bajtíky zakončené nulou a odnaučil se to. Dneska každý používá třídu, která nehledá nulu, ale má interně uložený počet znaků – například ::std::string. To je právě důsledek té blbosti Thinking Low Level – v ASCIIZ řetězcích miliónkrát hledáte nulu. Pokud budete mít rozumný řetězec, který bude mít řetězec s explicitně uloženou délkou, pak concatenation dvou stringů na 1 GB poli je leckdy i velmi efektivní operace – na rozdíl od blbých stringů zakončených nulou. Takže moje doporučení je Thinking High Level, Tiny Correct Low Level.
Překladač za Vás neudělá zázraky, ale naprosto zábavné je, jak si myslíte, že existuje přímé mapování zdrojový kód -> stroják. Dobře optimalizující kompilátor poznáte tak, že nejste schopni v dissasembleru, či debuggeru na strojové úrovni poznat, které kusy strojáku patří ke které části zdrojového kódu. Leckdy totiž ani funkce a rozdělení do podprogramů nesouhlasí s Vaším zdrojovým kódem. A v příkladu 5 si myslím, že po vyhození strlen není pro kompilátor problém poznat, že lze paralelizovat na úrovni SIMD. Pravda u gcc se toho zcela určitě nedočkáte, ale já mluvím o dobře optimalizujícím kompilátoru.
Ten strlen() nejenomže je bez problémů možné zoptimalizovat, dokonce ani norma C/C++ proti tomu nic nemá, ale dokonce to řada kompilátorů už minimálně 20 let dělá. Ono totiž optimalizací počtu volání strlen() často trhnete větší rychlostní zisk, než hledání pikosekund v tom, jak převrátit dvě instrukce v cyklu. A často se sejde několik volání strlen(), aniž by programátor musel být prase – uvědomte si, že v C++ se Vám ve funkci snadno složí X jiných, nezávislých inline funkcí a Y šablon.
std::string
, nýbrž Haskell Já si matně nemůžu vzpomenout na jedinou výhodu řetězce zakončeného nulou. Pokud budu chtít pracovat se spoustou subřetězců z jednoho řetězce, pořád se dá pracovat s trojicí (řetězec, start_index, délka_subřetězce), která je velice efektivní.
Ohledně řetězců právě tyto věci jako endianitu řešit musíte. Hlavně zmodernizujte svoje názory, doby, kdy se znaková sada vešla do 8mi bitových znaků už jsou dost let neodvratně pryč. Mnohé asijské sady, nebo Unicode nenacpete do zažitého schématu starých Céčkarů, či unixových zakladatelů, které zní „znak je bajt“ – dneska už to neplatí.
Takže tak jako tak v praxi řešíte formát uložení znaků (který se nevejde dnes už do jednoho bajtu) do bajtů – a tím pádem Vám naplno vyvstává ta endianita, formát uložení znaků a další problémy. Jistě – můžete se nadále tvářit, že Vám znaková sada ISO-8859-? stačí, ale pak budete muse mít spousty výjimek, pokud hodláte produktovat programy, které mají hovořit dnešní řečí a pracovat s dnešními texty.
Takže znovu říkám, u dnešních řetězců a textů nevidím ani jeden přínos řetězců zakončených nulou. Veškeré operace nad takovými řetězci jsou dvojnásobně pomalé – najdříve musíte řetězec projít, najít nulu a podruhé teprve nad ním dělat danou operaci, což znamená znovu paměťová operace. Pokud provádíte operace nad subřetězci, pak při explicitně uložené délce nepoužívané části řetězců může procesor i os zapomenout (pokud se bavíme o těmch mnohaset megabajtových polích, jak bylo řečeno výše), zatímco při hledání nuly znovu a znovu nutíte projít celé pole (tím natáhnout vše do cpu cache a vyhodit tam něco co bude v cache užitečnější, dále os donutíte nahrát swapované stránky zpět do paměti, i když ty kusy paměti k ničemu momentálně nepotřebujete).
Čisté C je potřeba (jako emulátor asm), ale až budete propagovat low level úroveň (která je také důležitá), tak nezapomínejte, že na nejnižší úrovni je ve vodě bahno a slabý výhled.
Veškeré operace nad takovými řetězci jsou dvojnásobně pomalé – najdříve musíte řetězec projít, najít nulu a podruhé teprve nad ním dělat danou operaci, což znamená znovu paměťová operace. [...]U drtivé většiny operací lze vše potřebné udělat už na první průchod, aniž by byla celková délka známá. Takže žádná režie navíc se nekoná.
Jenže ten jeden průchod je většinou pomalejší, než ty dva průchody. To je taková malá potíž. Snadno nahlédne každý, kdo nějak výraněji rozumí programování a optimalizaci v současných procesorech (a koneckonců i v os). Proto také všechny knihovní funkce typu strxxx, které potřebují kromě zjištění délky i něco udělat z toho zásadně dělají prchody dva (snadno nahlédnete do zdrojových kódů standardních knihoven C/C++) – a to i přesto, že by to šlo udělat na jeden průchod.
To je právě to, co tu kritizuji – low level úroveň chce úplně odlišné myšlení, než běžný programátorská úroveň. Proto všichni chytrolíni, co mixují běžné programování s low level úrovní jsou vedle jak ta jedle. Dnes jsou obě úrovně velmi odlišné – a nedá se podle jedné programovat v druhé – tedy pokud Vám záleží na efektivitě a rychlosti výsledného kódu.
Prostě pro low level práci se stringem je rychlejší udělat dva průchody pro dvě akce nad stringem zakončeným nulou, než jeden průchod, ve kterém zkombinujete obě akce. Profesionálním assembleristům to přijde naprosto samozřejmé a ani by je nenapadlo pokusit se o jeden průchod, protože znají rychlostní poměry na procesoru.
Takže znovu si uvědomte, že doba se mění, a procesory i os jsou jiné, než před řekněme dvaceti lety. Mnohá pravidla a mnohé knihy dnes nejsou zcela použitelné bez přemýšlení.
Asi mluvíš o X86 instukcích pracujících se stringy, s prefixem REP*, že? Myslel bych si, že blábolíš koniny, kdybych se s těmihle instrukcemi nesetkal. Měl bys být příště asi více konkrétní, jinak to tady bude jeden velký flame.
Nikdy jsem tohle moc neměřil a moc jsem toho v životě zatím nezoptimalizoval, ale mám za to, že čím víc informací o struktuře mám, tím _efektivnější_ funkce nad ní _možná_ můžu napsat. Slovo "efektivnější" jsem v použil proto, protože si myslím, že je důležité zvážit, jak je daná instrukce implementována. Může třeba běžet pomaleji, ale používat méně prostředků CPU a ty zbylé mohou být využity třeba pro něco jiného (Třeba HyperThreading?).
Pokud budu třeba duplikovat řetězec, použiji instrukci s REP* a C string (ukončený nulou), tak se něco v procesoru musí koukat na každé načtené slovo(byte,znak) a zjišťovat, jestli aktuálně zpracovávaný znak již není sentinel (nula, konec), musí se také inkrementovat zdrojová adresa a cílová adresa. To vše může, ale nemusí dělat třeba ALU. V případě použití Pascal stringů (s daným počtem znaků) bych musel používat ALU k počítání kolik slov(bytů, znaků) ještě musím zpracovat, a také musím počítat adresy zdroje a cíle. Výsledek je tedy asi 3:3. C stringy mají oproti Pascal stringů ale výhodu v tom, že jsou nekonečné a že procesor snad dokáže REP* lépe zoptimalizovat, než by to mohl dokázal překladač.
Napsal jste to perfektně. Jen oprava: REP prefix bude efektivnější naopak pro hodně dlouhé stringy. REP je hodně pomalý, ale v některých případech dokáže být dost rychlý, pokud je hodně opakování. (Viz Agnerův optimalizační manuál – základní to bible všech assembleristů. Je to tam dokonce i všechno explicitně změřené a rozložené, kolik cyklů ta která instrukce stráví v jednotlivých jednotkách procesoru. Zároveň je to rozebráno pro řadu různých modelů procesoru.)
Takže tak jako tak v praxi řešíte formát uložení znaků (který se nevejde dnes už do jednoho bajtu) do bajtů – a tím pádem Vám naplno vyvstává ta endianita, formát uložení znaků a další problémy.
Ne nutně. U UTF-8 endianitu řešit nepotřebujete.
UTF-8 je sice dobrý importní/exportní formát, ale to je tak asi všechno. Uvnitř programu není efektivní pracovat s UTF-8, a většinou se tak (s výjimkou některých zvěrstev, či případně některých uřžitečných aplikací) také nepracuje.
Pokud chcete argumentovat pomocí UTF-8, pak můžeme zapomenout na nějakou rychlost a efektivitu. Třeba vyhledání koncové nuly je marginální a nezajímavý program a nic oproti problémům, které člověka čekají při práci s UTF-8. UTF-8 se používá jako náhražka tam, kde lidé nebyli schopni stvořit Unicodové API a chtějí to hnát přes staré 8mi bitové API, pak se využívá jeho schopnost zmást „nepřítele“ – tedy jistá míra maskovat se za 8mi bitový string (v některých případech).
Takže UTF-8 si sice zrušíte problém s endianitou, abyste si vyrobili několik dalších.
Není, protože zabírá několikanásobně víc paměti, takže se často už nevejde do cache a sbohem, efektivito, bylo nám spolu pěkně. Co se UniCode a spol. týče, tak existenci UTF-8 Vám už připomněl kolega KubečekJá si matně nemůžu vzpomenout na jedinou výhodu řetězce zakončeného nulou. Pokud budu chtít pracovat se spoustou subřetězců z jednoho řetězce, pořád se dá pracovat s trojicí (řetězec, start_index, délka_subřetězce), která je velice efektivní.
Mohl byste mi, prosím, říci, jak jste to myslel s tím nevejitím do cache?
Pokud budu mít subřetězec, a nebude sahat na paměť mimo ten subřetězec – jak by mělo být potřeba více cache, než když to bude samostatný řetězec?
„Pokud píši program, u kterého mi na efektivitě nijak zvlášť nezáleží, nepoužiji std::string
, nýbrž Haskell“
Jeden z velkých omylů, které dělá mnoho lidí je, že si myslí, že low_level = efektivita a high_level = neefektivita. Nic není více vzdáleno pravdě. C++ má high level konstrukce proto, aby mohl se slušným pohodlím generovat programy stejně rychlé jako zbytečně low level jazyk C. Takže pokud mi na efektivitě a rychlosti výsledného programu záleží, použiji C++ a klidně třídu ::std::string. Největší sranda je fakt, že operace s řetězci pomocí třídy ::std::string jsou rychlejší, než operace psané low level pomocí strlen, strcopy, strcat, a další strxxx hovadiny. Výsledné komplexní operace nad řetězci běží o dost rychleji.
Pokud píšu program, kde mi na efektivitě nezáleží, použiji většinou problémově orientovaný jazyk zaměřený na danou oblast. Haskell je ale skvělý v tom, že namísto programování řešíte rébusy. Navíc se jako jazyk moc nerozšířil, a užitečných programů v něm věru mnoho napsáno nebylo.
Haskell je ale skvělý v tom, že namísto programování řešíte rébusy.
A proč to děláte?
std::string
se stringovými operacemi z LibUCW a pak mi řekněte Haskell je ale skvělý v tom, že namísto programování řešíte rébusy.Pokud chcete, můžete. Já jsem se raději Haskell pořádně naučil
Haskell je ale skvělý v tom, že namísto programování řešíte rébusy.
Pokud chcete, můžete. Já jsem se raději Haskell pořádně naučil
To by me zajimalo, Martine, napsal jsi nekdy v Haskellu nejaky vetsi program (myslim ne skolsky priklad)? Ja jsem cetl pres vanoce nejaky tutorial k Haskellu (tusim ten oficialni), a znechucene jej odlozil, protoze mi opravdu prislo, ze tam resi problemy, ktere uz jsou davno vyresene a bez funkcionalniho programovani by neexistovaly.
Pak jsem se koukal na prednasku / cetl knihu Practical Common Lisp, a prislo mi to daleko lepsi, z duvodu urcite pragmaticnosti tvurcu Lispu (coz je stejna vlastnost, pro kterou mam rad Python). Rad bych se tedy poucil, v cem je Haskell pro tebe tak zajimavy.
Haskell má v sobě také spoustu elegance, asi i víc než většina LISPů.muzes to rozvest? ja jsem se uz nekolikrat zkousel haskell naucit/pouzivat... a vzdycky jsem to zahodil, protoze jsem nenasel nic zasadniho v cem by byl haskell lepsi nez LISP/Scheme. ma to oproti temto jazykum nejakou killer feature?
Moc pěkná featurka je třeba líné vyhodnocování.
Na druhou stranu věc, která se může z hlediska spotřeby paměti velice negativně projevit, když si nedáte pozor.
Jen bych místo oficiálního tutoriálu doporučil knihu Real World Haskell.
Jasně, že lze udělat mnohem lepší třídu, než je ::std::string. Nikde jsem netvrdil, že je to nejlepší třída pro stringy na světě. Ale rozhodně je lepší, a dává rychlejší program, než při použití strxxx funkcí – což je to, s čím jsme srovnávali.
Ohledně Haskellu – pozor! Nabíháte mi na smeč. Já Vám snadno pak opáčím, abyste se pořádně naučil X, přičemž za X mohu dosadit cokoli, a asi bych začal C/C++.
Ohledně Haskellu – pozor! Nabíháte mi na smeč. Já Vám snadno pak opáčím, abyste se pořádně naučil X, přičemž za X mohu dosadit cokoli, a asi bych začal C/C++.Ejhle, pan kolega mne asi chce vyzvat na souboj. Volím tedy z Vámi nabízených zbraní Céčko. Předveďte se
Jenže podle Ponkráce céčko neexistuje, jenom dohromady s lomítkem a ještě jedním céčkem s dvěma flusítkama. A pak vás umlátí nějakou obskurní templatovou knihovnou, přičemž za důkaz její kvality bude považovat to že nějaký zcestně nereálný případ kde bude všecko const to překladač vyoptimalizuje tak, že z hromady C++ zvratek vypadnou pouhé 3 instrukce :)
Já bych naopak doporučil Thinking High LevelJá osobně bych tedy doporučil staré dobré přemýšlení na všech úrovních současně. Teprve tak pochopíte, co doopravdy děláte. Jak praví staré přísloví: "To understand a program you must become both the machine and the program."
Jak praví staré přísloví: "To understand a program you must become both the machine and the program."Tohle je pěkné, to jsem neznal, díky...
Souhlasím. Řekl jste to lépe, než já. Já jsem hlavně dělal protiváhu k převaze názorů, že low level úroveň je spasitel.
Na všech úrovních současně? A co my, kteří nemáme quad-brain(TM) hlavu? :o)Tak nezbývá než simulovat paralelní běh přepínáním kontextu
Proc by strlen() nemohl prekladac optimalizovat? Ja toho o x86 prekladacich pravda moc nevim, ale vim, kompilator IBM C pro zSeries nahrazuje strlen() jedinou instrukci, kterou mainframove procesory za tim ucelem maji. (Podobne nahrazuje i strncpy(), memset() a memcpy().)
Prosim vas, mohl byste si opravit "reseni" u sedmicky? Zbytecne tim matete neznale ctenare. S predavanim argumentu zprava nebo zleva to nema nic spolecneho, poradi vyhodnoceni argumentu funkce podle stadardu neni urcene, v kombinaci s hrabanim do jedne promenne to vytvari nedefinovane chovani, ten program muze udelat cokoliv (treba zpusobit implozi vesmiru) a bude to v souladu se standardem.
Bylo vam to v diskuzi nekolikrat vysvetleno.
No, mozna by me nejaka dlouhonoha dobre stavena dvacitka s velkym vystrihem dokazala ukecat, ze nedefinovane chovani se da povazovat za "vypise ruzne veci", ale hacek je v tom, ze clovek, ktery o nedefinovanem chovani v c/c++ nic nevi, ze slova "ruzne" nema sanci tento pravy duvod zpetne vycist. Korunu tomu nasazujete filozofovanim o volacich konvencich, logice vyhodnocovani a jeji poruseni optimizerem... to by u me nezaretusovala ani ta dvacitka i kdyby mela jen fikovej list a v ruce flasku s lubrikantem.
ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.