Mit tett értünk?!

Mármint nem a rómaiakról van itten szó, hanem Nyomosek Bobó kollégánkról [kitalált név, korábban más volt itt]
Ide töltöttem fel: http://lzsiga.users.sourceforge.net/stevejr.html

Szerk: köszönöm a hibajelzéseket, igyekszem átvezetni őket.

Magyarításról egy pont: http://lzsiga.users.sourceforge.net/stevejr.html#Q0034

Meg egy másik: http://lzsiga.users.sourceforge.net/stevejr.html#Q0035

Ékezetes fájlnevek:
http://lzsiga.users.sourceforge.net/stevejr.html#Q0036

Hozzászólások

Fasza gyűjtemény. Ezek szerint az említett úriember már a '60-as, '70-es években is sokat tett a világért.

Typo: "szerinte a kérdéses memóriát azután már úgysem használ a program" => "szerinte a kérdéses memóriát azután már úgysem használja a program"

Ma én is találkoztam egy alkotásával:


# nodetool clearsnapshot --help
Requested clearing snapshot(s) for [--help]

# echo $?
0

# nodetool clearsnapshot -t nincsilyen
Requested clearing snapshot(s) for [all keyspaces] with snapshot name [nincsilyen]

# echo $?
0

"Hogyne, ő találta ki az exception-öket"

Eddig olvastam.

Hát jah. A goto az evil. Még ha csak 3 sorral akarnék arrébb ugrani, akkor is. Java-ban nincs is. De ha arról van szó, hogy a call stack-ben 10-zel feljebb lévő fv-be akarok ugrani (ami akár egy teljesen másik modulban van, köze sincs a hiba keletkezési helyéhez), vagy igaziból azt se tudom, hogy hova fog ugrani a kód, akkor az jó.

Én egy szóval nem mondtam, hogy használjon mindenki goto-t, mert az milyen jó. Csak arra céloztam, hogy az ilyen dogmatikus kijelentések, hogy ez vagy az az ördögtől való, azok általában nem állják meg a helyüket. A break és a continue is tkp. goto, csak nem úgy írják és nem címkére ugrik, mégis használják.

az ilyen dogmatikus kijelentések, hogy ez vagy az az ördögtől való, azok általában nem állják meg a helyüket.

Az ilyen dogmatikus kijelentések valóban nem állják meg a helyüket teljes általánosságban. Azonban ezeket nem teljes általánosságban értik. Pl. a goto nagyon jó egy alacsony szintű nyelvben. Egy magas szintű nyelvben már ördögtől való, mert vannak helyettük sokkal jobb konstrukciók.

A break és a continue is tkp. goto, csak nem úgy írják és nem címkére ugrik, mégis használják.

Sok nyelvben ezért nincsenek is ezek meg (mert tkp. goto).

> Pl. a goto nagyon jó egy alacsony szintű nyelvben.

Nem tudom mit értesz alacsonyszintű nyelven. Ha az assemblyt, akkor ott nem "jó", hanem muszáj, mert nincs más. A többi nyelvben pedig mint lehetőség, jó, hogy van. Az, hogy a balfácánok rosszul használják, az nem érv ellene. Az OOP-t is lehet rosszul használni és használják is. Akkor az is rossz? Nem, a rossz használat a rossz, nem pedig egy eszköz.

> Sok nyelvben ezért nincsenek is ezek meg (mert tkp. goto).

Elég baj. Hogy szállsz akkor ki egy ciklusból, ha épp a közepén kell? Plusz szemafor változókat veszel fel, újrastruktúrálod az egész flow-t, meg beleírod a ciklus folytatási feltételébe, hogy "és ha ez a szemafor hamis". Egy-egy ilyen kiszállási igénynél még csak-csak elmegy, de amikor sok van a cikluson belül, akkor ezzel már sokkal nagyobb káoszt csinál az ember, mint egy nyamvadt break-kel. Arról nem is beszélve, hogy lassabb is lesz.

Nem tudom mit értesz alacsonyszintű nyelven.

Minél alacsonyabb szintű egy nyelv, annál részletesebben el kell magyarázni benne, hogy mit és hogyan csináljon.
Pl.: Assembly < C < C++ < Java < Kotlin < Haskell

Az, hogy a balfácánok rosszul használják, az nem érv ellene.

Az az érv ellene, amit írtam is: vannak annál jobb konstrukciók felsőbb szintű nyelvekben, pl. for, foreach, if, switch, ..., sőt még ezeknél is vannak jobbak (filter, map, flatMap, ...), így sok nyelvben még azok sincsenek.

Az OOP-t is lehet rosszul használni és használják is. Akkor az is rossz?

Igen, rossz :-), de nem azért, mert rosszul használják, hanem, mert van annál is jobb konstrukció.

Hogy szállsz akkor ki egy ciklusból, ha épp a közepén kell?

Talán nem ciklust használok rá? :-) Pl. Haskellben nincs is ciklus :-), mégis megoldható, hogy egy ismétlődés közepén "kiszállj belőle".

> Az az érv ellene, amit írtam is: vannak annál jobb konstrukciók felsőbb szintű nyelvekben, pl. for, foreach, if, switch, ..., sőt még ezeknél is vannak jobbak (filter, map, flatMap, ...), így sok nyelvben még azok sincsenek.

if nélkül mégis hogy lehet épeszű keretek között programozni?

> Talán nem ciklust használok rá? :-) Pl. Haskellben nincs is ciklus :-), mégis megoldható, hogy egy ismétlődés közepén "kiszállj belőle".

Nem ismerem a Haskellt. De kiváncsi lennék, hogy milyen teljesítményt tud hozni egy Haskellben írt parser egy C++-ban írttal szemben. (Mondjuk most lecsekkoltam, hogy hogy néz ki benne egy for ciklus és ez a kép jutott eszembe: https://blog.toggl.com/wp-content/uploads/2017/04/toggl-how-to-kill-the-dragon-with-9-programming-languages.jpg)

if nélkül mégis hogy lehet épeszű keretek között programozni?

Más ész kell hozzá! ;-)

kiváncsi lennék, hogy milyen teljesítményt tud hozni egy Haskellben írt parser egy C++-ban írttal szemben

Az alacsonyabb szintű nyelveknél az az előny megvan, hogy bizonyos dolgokat (esetleg sokkal több munkával) gyorsabbra meg lehet írni.
Gondolom azt senki sem vitatja, hogy Assembly-ben lehet mindent a leggyorsabbra írni. ;-)
Az, hogy ez mindig sikerül-e, az már más kérdés.

A Haskell-ben írt programok azért a gyorsabbak közé szoktak kerülni. Itt olvasgathatsz a performanciájáról.

> Más ész kell hozzá! ;-)

Miféle? Matematikusi? Attól, hogy valaki jó matekból, attól még nem lesz automatikusan jó programozó, ez nem csak matek.

> bizonyos dolgokat

Azaz kb. mindent.

> (esetleg sokkal több munkával)

Esetleg? Ez azért elég sok mindentől függ. Egy algoritmust C++ vagy Pascal nyelvben kb. egy az egyben meg lehet feleltetni a gondolkodásodnak, csak le kell írnod, Haskellben meg még azt is ki kell találnod, hogy hogy lehet megoldani a rendelkezésre álló nyelvi elemekkel.

> Gondolom azt senki sem vitatja, hogy Assembly-ben lehet mindent a leggyorsabbra írni. ;-)

Hát, ha fejből tudod a CPU összes kis trükkjét (pl. spekulatív és nonlineáris végrehajtás) és fejben tudod tartani, hogy mit hová kell pakolni, meg milyen címekre kell igazítani ahhoz, hogy egy adag kód még éppen beférjen a csőbe és még sorolhatnám, akkor igen. Amúgy a CLang vagy a GCC simán jobb kódot fordít annál, amit kézzel írnál.

> A Haskell-ben írt programok azért a gyorsabbak közé szoktak kerülni. Itt olvasgathatsz a performanciájáról.

Thx. Mondjuk látok ellenvéleményt is, hogy egyre lassabb lesz: https://wiki.theory.org/index.php/YourLanguageSucks#Haskell_sucks_because

> Egy algoritmust C++ vagy Pascal nyelvben kb. egy az egyben meg lehet feleltetni a gondolkodásodnak, csak le kell írnod, Haskellben meg még azt is ki kell találnod, hogy hogy lehet megoldani a rendelkezésre álló nyelvi elemekkel.

Azért ez előny is lehet a Haskell számára. Nézd meg pl. az alap quicksortot Haskellben. Még ha nem is ismered a nyelvet, kb. látszik rajta, hogy ez miért quicksort. És igaziból, ha a fordítója csúcsszuper lenne, akkor ez az alap implementáció olyan gyors lehetne, mint a kézzel szénné optimalizált C++-os verzió (mondjuk ez nem mostanában fog bekövetkezni, de elméletileg lehetséges volna). Szóval nem mindig rossz, hogy nem procedurálisan írunk le egy problémát. Bár amúgy tőlem is távol áll ez az egész, de azért mondjuk meglepő volt számomra, hogy egy quicksort ennyire rövid és frappáns Haskellben.

> Amúgy a CLang vagy a GCC simán jobb kódot fordít annál, amit kézzel írnál.
Nálam ugyan nem :) Inkább az a ritka, hogy teljesen optimális kódot csinálnak. Egyre jobbak, ez kétségtelen, de még mindig messze vagyunk attól, hogy elérjék azt a szintet, amit egy tapasztalt asm programozó tud.

> Nézd meg pl. az alap quicksortot Haskellben. Még ha nem is ismered a nyelvet, kb. látszik rajta, hogy ez miért quicksort.

Megnéztem. Négy sor az egész, míg a C++ verzió 18 soros. Csakhogy ez nem jelent semmit. A kérdés, hogy milyen kód fordul belőle...

> És igaziból, ha a fordítója csúcsszuper lenne, akkor ez az alap implementáció olyan gyors lehetne, mint a kézzel szénné optimalizált C++-os verzió (mondjuk ez nem mostanában fog bekövetkezni, de elméletileg lehetséges volna).

...amit már meg is válaszoltál helyettem. A "lehetne" az nagyon tág fogalom. Miért nem az? Talán mert a CPU elemi utasításokból építkezik, ezért egy szintén elemi utasításokból építkező nyelvhez viszonylag egyszerűen lehet fordítót írni, míg ehhez meg nem?

> Szóval nem mindig rossz, hogy nem procedurálisan írunk le egy problémát. Bár amúgy tőlem is távol áll ez az egész, de azért mondjuk meglepő volt számomra, hogy egy quicksort ennyire rövid és frappáns Haskellben.

Én nem is mondtam ilyet. De a quicksortnál még azt is látni kell, hogy a belseje egy viszonylag egyszerű szabályrendszerre épül. Mit kezd a Haskell egy olyan parse-oló algoritmussal, aminek a belsejében egy raklap feltétel és specifikus elágazás van? Sokkal nagyobb kínkeserv lesz megírni és lassabb is lesz.

> Nálam ugyan nem :) Inkább az a ritka, hogy teljesen optimális kódot csinálnak.

Az lehet, hogy nem teljesen optimális kódot csinálnak, de hacsak nem számítógépet visel az ember a nyakán fej helyett, akkor elég nehezen fog lépést tartani egy mai CPU-n a fordítóval. Azokon a mikrokontrollereken, ahol nincs nonlineáris/spekulatív végrehajtás, elágazás-becslés, csövezés és még sorolhatnám, na ott tuti jobb kódot írsz, mint a C fordító. De ahol van, ott alig hinném. Egyszer láttam egy olyan ASM kódot, hogy a szubrutint a meghívás után paraméterezte fel a kód, azért, mert azok az utasítások mindenképpen a csőbe kerültek már és mindenképpen végre fognak hajtódni, mielőtt még a szubrutint betöltené a csőbe a rendszer és végrehajtaná. Egy mai CPU-ban annyi "szarság" van, hogy ezt ember egyszerre mind kissé nehezen tartaná fejben.

> Négy sor az egész, míg a C++ verzió 18 soros. Csakhogy ez nem jelent semmit. A kérdés, hogy milyen kód fordul belőle...
Igen, ez így van. De itt most az elv a fontos. A 4 soros Haskell kódból lefordulhat az optimális kód, míg a C++-osból nem annyira (csak akkor, ha az algoritmust úgy implementáltad. Quicksortra van egy rakat optimalizálási trükk, amit kézzel kell megcsinálni C++-ban, míg Haskellben - elméletben - a fordító odateheti ezeket automatán. De amúgy az igaz, hogy ezek egy jó részét nem teszi oda, szóval C++ simán megveri a Haskell kb. bármiben. Jelenleg.).

> Talán mert a CPU elemi utasításokból építkezik, ezért egy szintén elemi utasításokból építkező nyelvhez viszonylag egyszerűen lehet fordítót írni, míg ehhez meg nem?
Igen, ez így van. Nyilván sokkal bonyolultabb egy jó Haskell fordító (valszeg ilyen még nem is létezik amúgy...), mint egy C++/java.

> Mit kezd a Haskell egy olyan parse-oló algoritmussal, aminek a belsejében egy raklap feltétel és specifikus elágazás van? Sokkal nagyobb kínkeserv lesz megírni és lassabb is lesz.
Ez egyáltalán nem ilyen biztos. Haskellben megírod az illeszkedési szabályokat, és kész vagy (ha olyan a nyelv szerkezete, természetesen). Pont egy olyan példát választottál szerintem, amit jól meg lehet csinálni Haskellel. De nem vagyok szakértője a nyelvnek, szóval lehet tévedek. De az biztos, hogy van egy csomó olyan probléma, amit Haskellben körülményes megcsinálni. De talán majd erre jár valaki, aki jobban ért hozzá, és helyreteszi ezt :)

Optimalizálásról: ezt pont, hogy fordítva van. Ha a CPU egyszerű, akkor a fordítók pont, hogy tudnak rá jó kódot csinálni. Lehet, hogy nem lesz 100% optimális, de közel lesz hozzá. Azért, mert a keresési tér pici. A mostani CPU-kra sokkal bonyolultabb optimális kódot csinálni, mert annyi szabály van. És igen, egy tapasztalt asm programozó ezeket ismeri. Azért nem annyira nagy kaland ez, ha valakinek ez a munkája. Tapasztalatból beszélek, tudnék egy rakat példát mondani, ahol a fordító rendkívül rossz kódot csinál. És mennyit kell vele szívni, mire hajlandó lesz olyan kódot írni, amire már azt mondom, hogy okés, közel van az optimálishoz (hogy aztán egy újabb fordító/fordító verzió újra rosszabb kódot csináljon...). De egyébként ez nyilvánvaló, hacsak rénézel egy C++ fordító benchmarkra: elég nagy különbségeket tudnak felmutatni ugyanarra a kódra a különböző fordítók. Ebből látszik, hogy messze vagyunk még attól, hogy a fordítók az optimálishoz nagyon közeli kódot csináljanak az esetek nagy részében.

> A 4 soros Haskell kódból lefordulhat az optimális kód, míg a C++-osból nem annyira

Ez nem igaz. Ha az egyikre lehet optimális fordítót írni, akkor a másikra is.

> míg Haskellben - elméletben - a fordító odateheti ezeket automatán. De amúgy az igaz, hogy ezek egy jó részét nem teszi oda, szóval C++ simán megveri a Haskell kb. bármiben. Jelenleg.

Meg még egy jó darabig biztos; sokkal komplexebb a probléma, mint egy imperatív nyelvnél.

> Ez egyáltalán nem ilyen biztos. Haskellben megírod az illeszkedési szabályokat, és kész vagy (ha olyan a nyelv szerkezete, természetesen). Pont egy olyan példát választottál szerintem, amit jól meg lehet csinálni Haskellel. De nem vagyok szakértője a nyelvnek, szóval lehet tévedek. De az biztos, hogy van egy csomó olyan probléma, amit Haskellben körülményes megcsinálni. De talán majd erre jár valaki, aki jobban ért hozzá, és helyreteszi ezt :)

Minél bonyolultabb az algoritmus belülről, minél több kihatása van más kódrészekre, annál nehezebb lesz Haskellben feltételrendszert szabni rá.

> Ha a CPU egyszerű, akkor a fordítók pont, hogy tudnak rá jó kódot csinálni. Lehet, hogy nem lesz 100% optimális, de közel lesz hozzá. Azért, mert a keresési tér pici.

Ez igaz, de ugyanezzel párhuzamosan az ember is annál könnyebben ír rá jó kódot. Egy egyszerű CPU-nál bármilyen nyelvet is használsz, az mindig rak plusz részeket a kódba, ami az ő saját ökoszisztémájának a része, szerkezetek, paraméterátadás, interface, stb. Te nem vagy ilyen korlátok közé szorítva. Gondolom azért csak jobb kódot írsz kézzel 6502-re, mint a CC65.

> A mostani CPU-kra sokkal bonyolultabb optimális kódot csinálni, mert annyi szabály van. És igen, egy tapasztalt asm programozó ezeket ismeri. Azért nem annyira nagy kaland ez, ha valakinek ez a munkája.

Nem elég ismerni, folyamatosan fejben kell tartani és nézni egy csomó dolgot, pl. hogy fog ez a kódrész beférni a csőbe, hogyan optimális felépíteni az utasítások sorrendjét és ráadásul egy csomó dolog még azonos architektúrán belül is egyedi lesz egy adott CPU fajtára nézve. Ezt kézzel csinálni elég favágómeló lehet.

> Tapasztalatból beszélek, tudnék egy rakat példát mondani, ahol a fordító rendkívül rossz kódot csinál. És mennyit kell vele szívni, mire hajlandó lesz olyan kódot írni, amire már azt mondom, hogy okés, közel van az optimálishoz (hogy aztán egy újabb fordító/fordító verzió újra rosszabb kódot csináljon...).

Ezt én nem vitatom, hogy van olyan algoritmus, amiből a fordító trágyát csinál és kézzel kell kihajavítani, de ezek specifikus esetek - itt inkább arról van szó, hogy egy adott részt írsz meg jobban kézzel, nem mindent.

> De egyébként ez nyilvánvaló, hacsak rénézel egy C++ fordító benchmarkra: elég nagy különbségeket tudnak felmutatni ugyanarra a kódra a különböző fordítók. Ebből látszik, hogy messze vagyunk még attól, hogy a fordítók az optimálishoz nagyon közeli kódot csináljanak az esetek nagy részében.

Igen, de még mindig gyorsabban írod át a C/C++ kódot úgy, hogy nagyjából olyat adjon ki, amit szeretnél, mint írod meg az egészet assemblyben úgy, hogy perfekt legyen.

>Ez nem igaz. Ha az egyikre lehet optimális fordítót írni, akkor a másikra is.

A C++-nál van egy csomó kötöttség, a fordító nem csinálhat jelentősen mást, mint amit a programozó odaírt. Haskellnél meg le se írod a konkrét algoritmust. A fordító olyan algoritmust tesz oda, amilyet akar. Ez a jelentős különbség a kettő között.

> Ez igaz, de ugyanezzel párhuzamosan az ember is annál könnyebben ír rá jó kódot. Egy egyszerű CPU-nál bármilyen nyelvet is használsz, az mindig rak plusz részeket a kódba, ami az ő saját ökoszisztémájának a része, szerkezetek, paraméterátadás, interface, stb. Te nem vagy ilyen korlátok közé szorítva. Gondolom azért csak jobb kódot írsz kézzel 6502-re, mint a CC65.

Itt én inkább arra reflektáltam, hogy maga a C++ fordító milyen jó, betartva az ABI szabályait. Az a különbség, hogy míg egy egyszerű CPU-ra egy jó fordító szinte mindig optimálishoz közeli kódot csinál, addig a bonyolultabb CPU-ra sokszor nem. És ha lenne 6502-re normális C++ fordító, az szinte biztosan az optimálishoz közeli kódot tudna csinálni szinte minden esetben.

> Nem elég ismerni, folyamatosan fejben kell tartani és nézni egy csomó dolgot, pl. hogy fog ez a kódrész beférni a csőbe, hogyan optimális felépíteni az utasítások sorrendjét és ráadásul egy csomó dolog még azonos architektúrán belül is egyedi lesz egy adott CPU fajtára nézve. Ezt kézzel csinálni elég favágómeló lehet.
Nyilván. Ezért ehhez külön érteni kell. Aki ebben dolgozik, annak mint mondtam, nem egy olyan nagy kaland ezeket a dolgokat fejben tartani. Ha egyszer felvetted a fonalat, utána egy újabb CPU-nál "csak" a különbségeket kell megtanulni.

> Ezt én nem vitatom, hogy van olyan algoritmus, amiből a fordító trágyát csinál és kézzel kell kihajavítani, de ezek specifikus esetek - itt inkább arról van szó, hogy egy adott részt írsz meg jobban kézzel, nem mindent.
Sajnos nem. Nézz meg egyszer egy lefordított kódot. Szinte mindenhol fogsz találni részeket, amiken lehet javítani. Legalábbis nekem ez a tapasztalatom, bármikor ránéztem egy fordított kódra, mindenhol találtam nem optimális részeket. Néha még a legegyszerűbb dolgokat is rosszul fordítja le a fordító,

>Igen, de még mindig gyorsabban írod át a C/C++ kódot úgy, hogy nagyjából olyat adjon ki, amit szeretnél, mint írod meg az egészet assemblyben úgy, hogy perfekt legyen.
Persze, C++-ban sokkal gyorsabban lehet haladni, ez nem vitás. Én csak azzal az állításoddal nem értek egyet, hogy "a CLang vagy a GCC simán jobb kódot fordít annál, amit kézzel írnál.". Nekem az a tapasztalatom, hogy bármikor, mikor szükséges volt gyors kódot írni, mindig tudtam javítani a fordított kódon. Ráadásul úgy, hogy én se vagyok tisztában a mostani CPU-k minden apró részletével. Elég pár CPU specifikus dolgot tudni ahhoz, hogy javítani tudjak a fordítók által generált kódon.

Nu mind1, úgy látom nagyjából egyetértünk egy csomó mindenben, csak a fordítókat látjuk kicsit másképpen.

Csak hogy egy példát is írjak: pár éve csináltam egy tömörítőt, aminek egy része az volt, hogy egy 6x-osan interleave-elt huffman streamet kellett kitömörítenie (azaz egy adatblokkban volt egymás után 6 huffman stream, amiknek a kimenetét kellett interleave-elni). Ez alapból olyan 150MB/sec-kel működött. Miután leoptimalizáltam (az algoritmus nem változott!), lett belőle 1.5GB/sec. És közben volt olyan is, hogy egy apró módosítás 2.5x lassabb kódot eredményezett. Szóval össze-vissza változott az, hogy az adott kód milyen sebességgel fut. Szóval ennyit arról, hogy mennyire optimális kódot csinálnak a mai fordítók... Sok ilyen példát tudnék hozni, bár ennyire durva különbség ritkán van, azt elismerem. De 2x szorzó simán szokott lenni.

> A C++-nál van egy csomó kötöttség, a fordító nem csinálhat jelentősen mást, mint amit a programozó odaírt.

De azt sokféleképpen valósíthatja meg; a valódi kötöttség annyi, hogy a kódnak ugyanazt kell csinálnia, de az nincs megkötve, hogy ugyanúgy.

> Haskellnél meg le se írod a konkrét algoritmust. A fordító olyan algoritmust tesz oda, amilyet akar. Ez a jelentős különbség a kettő között.

Igen, csak az imperatív nyelveknél a kóddal már meg is mondtad, hogy mit szeretnél (a fordító csak a hogyant dönti el), a Haskellnél meg van egy feltételhalmazod, amiből neki nem csak a hogyant, de a mit is ki kell találnia. És ha ebben a "kitalálóban" bug van és rossz algoritmust ír, akkor esélyed sincs kijavítani. Ha meg te írtad fel a feltételrendszert rosszul, akkor nagy öröm lesz debuggolni... Tényleg, lehet-e Haskellnél egyáltalán step-by-step debugot csinálni?

> Itt én inkább arra reflektáltam, hogy maga a C++ fordító milyen jó, betartva az ABI szabályait. Az a különbség, hogy míg egy egyszerű CPU-ra egy jó fordító szinte mindig optimálishoz közeli kódot csinál, addig a bonyolultabb CPU-ra sokszor nem. És ha lenne 6502-re normális C++ fordító, az szinte biztosan az optimálishoz közeli kódot tudna csinálni szinte minden esetben.

Én értem, de én meg pont ezt magyarázom, hogy hiába fog jobb kódot kiadni a fordító egy egyszerűbb CPU-ra, mint egy bonyolultabbra, itt mindenképpen az ember van előnyben, mert ő szűkebb, gyorsabb kódot is tud írni, nincsenek azok a belső megkötések, mint a fordítónál. Meg lehet nézni, hogy ha megírod C-ben, hogy mondjuk nullázza ki a 4-es lapot, akkor az mekkora és milyen gyors kódot fog eredményezni és utána mennyiből tudod ugyanezt megvalósítani tiszta 6502 ASM-ben.

> Nyilván. Ezért ehhez külön érteni kell. Aki ebben dolgozik, annak mint mondtam, nem egy olyan nagy kaland ezeket a dolgokat fejben tartani. Ha egyszer felvetted a fonalat, utána egy újabb CPU-nál "csak" a különbségeket kell megtanulni.

Lehet, csak kérdés, hogy nyersz-e annyit rajta, a megfelelően megírt algoritmusra a megfelelő optimalizációs paraméterekkel rászabadított fordítóhoz képest, hogy megérje-e szórakozni vele.

> Sajnos nem. Nézz meg egyszer egy lefordított kódot. Szinte mindenhol fogsz találni részeket, amiken lehet javítani. Legalábbis nekem ez a tapasztalatom, bármikor ránéztem egy fordított kódra, mindenhol találtam nem optimális részeket. Néha még a legegyszerűbb dolgokat is rosszul fordítja le a fordító,

Elhiszem. De éppen ezért van ötvenezer féle forgatásra vonatkozó kapcsolója a compilereknek, hogy megtaláld az optimálisat. Meg hát kódot írni akkor is tudni kell, ha a fordító jól optimalizál. Rossz algoritmust nem lehet jól optimalizálni.

> Persze, C++-ban sokkal gyorsabban lehet haladni, ez nem vitás. Én csak azzal az állításoddal nem értek egyet, hogy "a CLang vagy a GCC simán jobb kódot fordít annál, amit kézzel írnál.". Nekem az a tapasztalatom, hogy bármikor, mikor szükséges volt gyors kódot írni, mindig tudtam javítani a fordított kódon. Ráadásul úgy, hogy én se vagyok tisztában a mostani CPU-k minden apró részletével. Elég pár CPU specifikus dolgot tudni ahhoz, hogy javítani tudjak a fordítók által generált kódon.

De most akkor azt az egész algoritmust nulláról újraírtad assemblyben a leforgatott kódban, vagy csak belejavítottál? Mert az utóbbi esetben még mindig a fordító generálta a kódot, te csak javítottál rajta és spóroltál pár (vagy akár sok) órajelet.

> Nu mind1, úgy látom nagyjából egyetértünk egy csomó mindenben, csak a fordítókat látjuk kicsit másképpen.

Meg a goto kérdését. Szerintem nonszensz, hogy evil lenne. Ez egy eszköz; csak a használója lehet gonosz, vagy hülye, az eszköz nem. Egyébként ha te kézzel tolod assemblyben, akkor nem is értem, hogy mi bajod vele, hiszen ASM-ben csak goto, meg gosub van. (Illetve jmp, jsr/call, meg a különféle branch-ek, de érted mire akarok kilyukadni.)
Update: Közben látom lent, hogy nem úgy értetted, hát nem jött át; formában vagyok ma. :/

> Csak hogy egy példát is írjak: pár éve csináltam egy tömörítőt, aminek egy része az volt, hogy egy 6x-osan interleave-elt huffman streamet kellett kitömörítenie (azaz egy adatblokkban volt egymás után 6 huffman stream, amiknek a kimenetét kellett interleave-elni). Ez alapból olyan 150MB/sec-kel működött. Miután leoptimalizáltam (az algoritmus nem változott!), lett belőle 1.5GB/sec. És közben volt olyan is, hogy egy apró módosítás 2.5x lassabb kódot eredményezett. Szóval össze-vissza változott az, hogy az adott kód milyen sebességgel fut. Szóval ennyit arról, hogy mennyire optimális kódot csinálnak a mai fordítók... Sok ilyen példát tudnék hozni, bár ennyire durva különbség ritkán van, azt elismerem. De 2x szorzó simán szokott lenni.

Érdekes. Jó volna a kódot is látni, meg a fordítási opciókat, meg az azokra adott assemblyt. Persze nem vitatom, hogy néha eléggé hulladék a végeredmény, amit ki tudnak dobni magukból a fordítók, de elég sok múlik a "bemeneten", meg a különféle beállításokon.

Nekem ennél jobb tapasztalataim vannak a C fordítókkal, amikor a PNG => IFF konverteremet írtam, akkor az volt a cél, hogy Amigán is le tudjam épeszű módon futtatni. Először úgy voltam vele, hogy ha megvagyok a C forrással a többi platformra, akkor majd Amigán a kritikus részeket (Wu kvantizálás, Lánczos újramintavételezés, stb.) átírom 68000-es és 68020-as assemblyre. De a SAS/C tűrhető sebességet produkált így is, így az átírás elmaradt, pedig 68000-esben elég sokat ügyködtem, biztos bírtam volna valamennyit javítani rajta. (Persze, ha valaki 7.14 MHz-en akar egy 1920x1200-as, 64-bites színmélységű PNG-t átkonvertálni, akkor az nem két perc lesz, de megcsinálja.)

> De azt sokféleképpen valósíthatja meg; a valódi kötöttség annyi, hogy a kódnak ugyanazt kell csinálnia, de az nincs megkötve, hogy ugyanúgy.

A C++ szabvány eléggé megköti a fordító kezét. Nem lehet nagyobb módosításokat csinálni. Haskellben azt csinál a fordító mondjuk azzal a quicksort-tal, amit akar, csak a végeredmény legyen jó.

> Haskellnél meg van egy feltételhalmazod, amiből neki nem csak a hogyant, de a mit is ki kell találnia.

Pont ez az egyik lényege. Kitalálja ő neked, neked nem kell vele foglalkoznod. Most az, hogyha bug van a fordítóban, akkor mi van, azt inkább hagyjuk, szerintem nem annyira érv :)

> éppen ezért van ötvenezer féle forgatásra vonatkozó kapcsolója a compilereknek, hogy megtaláld az optimálisat.

Ja, csak az egyik kódrészre egyik fajta kapcsoló kell, a másikra meg másik. Ez így nem igazán használható.

> De most akkor azt az egész algoritmust nulláról újraírtad assemblyben a leforgatott kódban, vagy csak belejavítottál?

Szerintem ez nem számít. Az a lényeg, hogy amit a compiler generál, lehet jobbat csinálni. De egyébként nyilván az ember először megnézi, mit csinált a fordító, és ha majdnem jó, akkor inkább azt javítja ki, mint nulláról írja meg. De sokszor van olyan, hogy sajnos nulláról kell megírni.

> Meg a goto kérdését. Szerintem nonszensz, hogy evil lenne.

Félreértettél. Pont arra próbáltam meg felhívni a figyelmet, hogy a goto-ról azt mondják, hogy evil, és az exceptionról, pedig hogy jó (jó, vannak ellenvélemények, de messze nem olyan a megítélése, mint a goto-nak). Pedig az exception sokkal durvább ugrásokat csinál, mint egy goto. Nekem semmi bajom a goto-val, ha épp azzal lehet megoldani valamit a legjobban, simán leírom.
Update: OK :)

> Jó volna a kódot is látni, meg a fordítási opciókat, meg az azokra adott assemblyt. Persze nem vitatom, hogy néha eléggé hulladék a végeredmény, amit ki tudnak dobni magukból a fordítók, de elég sok múlik a "bemeneten", meg a különféle beállításokon.

Nem volt benne semmi komoly amúgy. De amúgy, miért is elfogadott az, hogy beállítások kellenek? Egy dolgot mondok meg neki, hogy milyen CPU-ra optimalizáljon. Miért is kéne neki segíteni mindenféle beállítással, ha jó kódot tud generálni? A CPU adott, szeretném a leggyorsabb kódot, ennyi. És miért is múlik a bemeneten bármi is? Leírom egy absztrakt nyelven az algoritmust. Szeretném, hogy ez az algoritmus a lehető leggyorsabban fusson le. Azt nem várom el tőle, hogy egy buborék rendezésből quicksortot csináljon. De egy csomó mást elvárhatnék tőle. Ezért mondom, hogy a mostani fordítók még nagyon nincsenek ott, hogy bármilyen szinten is versenyre keljenek egy tapasztalt asm programozóval.

> Nekem ennél jobb tapasztalataim vannak a C fordítókkal...
Pont ezt mondom :) Ha régi architektúrára dolgozol, akkor ott nagy általánosságban jó kódot csinálnak. A mostani CPU-kra csinálnak szinte mindig suboptimális kódot, és esetenként pedig nagyon rosszat. Ami egy egyszerűbb CPU-nál nem igazán (volt) megszokott. Régi CPU-ra tudsz jobb kódot írni kézzel, mint amit a compiler csinál, de általánosságban nem lesz sokkal jobb. Az új, bonyolultabb CPU-knál meg egy apróságot elront a fordító, és hopsz, 30%-kal lassabb lesz a kód.

> A C++ szabvány eléggé megköti a fordító kezét. Nem lehet nagyobb módosításokat csinálni.

Ehhez képest te magad mondtad, hogy elég vaskos eltérések vannak fordítónként, vagy akár opciónként. Akkor pedig mégis elég sok különbség lehet a generált kódok közt, nem?

> Haskellben azt csinál a fordító mondjuk azzal a quicksort-tal, amit akar, csak a végeredmény legyen jó.

Elméletben. És a quicksort egy egyszerű példa.

> Pont ez az egyik lényege. Kitalálja ő neked, neked nem kell vele foglalkoznod. Most az, hogyha bug van a fordítóban, akkor mi van, azt inkább hagyjuk, szerintem nem annyira érv :)

De én nem a fordítóban lévő bugról beszélek, a fordító az az algoritmust fordítja le neked gépi kódra. Nem tudom minek hívják ezt a Haskellben, azt, ami kitalálja, hogy egyáltalán te milyen algoritmust akartál ott alkalmazni. Na, ha abban van a hiba, az nem vicces és nem is tudsz vele mit kezdeni.

> Ja, csak az egyik kódrészre egyik fajta kapcsoló kell, a másikra meg másik. Ez így nem igazán használható.

Dehogynem. Maximum csak akkor nem, ha ezt egyetlen függvényen belül kéne így. De ha mondjuk két külön függvényünk van, amikre kétféle paraméterezést kéne alkalmazni, akkor azokat le lehet fordítani két külön objectté, két külön paraméterlistával, majd a linker összerakja a végén.

> Szerintem ez nem számít. Az a lényeg, hogy amit a compiler generál, lehet jobbat csinálni. De egyébként nyilván az ember először megnézi, mit csinált a fordító, és ha majdnem jó, akkor inkább azt javítja ki, mint nulláról írja meg. De sokszor van olyan, hogy sajnos nulláról kell megírni.

Hát lehet. Csak akkor meg írhatod meg azt a kódrészletet minden CPU minden fajtájára.

> De amúgy, miért is elfogadott az, hogy beállítások kellenek? Egy dolgot mondok meg neki, hogy milyen CPU-ra optimalizáljon. Miért is kéne neki segíteni mindenféle beállítással, ha jó kódot tud generálni? A CPU adott, szeretném a leggyorsabb kódot, ennyi.

Azért, mert még egy CPU architektúrán belül is durva eltérések lehetnek a CPU fajták között. Más utasítások, más cacheméret, más csőméret, más elágazásbecslő, meg még mittudomén mi minden. K8-ason teljesen máshogy fog futni ugyanaz a kód és teljesen máshogy kell rajta optimalizálni mint Zen+-on, pedig AMD64 mind a kettő.

> És miért is múlik a bemeneten bármi is? Leírom egy absztrakt nyelven az algoritmust. Szeretném, hogy ez az algoritmus a lehető leggyorsabban fusson le.

Mert egy rossz algoritmuson hiába is próbál optimalizálni a fordító. Vagy egy rosszul megszervezetten. Példa: ha van két egymásra súlyosan támaszkodó kódrészleted, amik egy böhöm nagy kódban jó messze vannak egymástól, akkor a csőbe hol az egyiket, hol a másikat kell behúzni. Ha a fordító egymás mellé tudta őket szervezni, akkor egyszerre is beférhetnek. Lehet, hogy ez így egy kissé pontatlan volt, de szerintem érted mire akarok célozni. Na, pont egy rosszul megszervezett OOP kódban, ahol a fordítók mindig is bajban voltak a függőségek kezelésével, ott pláne kijöhet ilyesmi.

> Azt nem várom el tőle, hogy egy buborék rendezésből quicksortot csináljon. De egy csomó mást elvárhatnék tőle. Ezért mondom, hogy a mostani fordítók még nagyon nincsenek ott, hogy bármilyen szinten is versenyre keljenek egy tapasztalt asm programozóval.

Hát, nem tudom. Én a nálam okosabbaktól (pl. demoscenerek) az ellenkezőjét hallottam. Pl. BoyC azt mondta, hogy amikor a Crystal Vision-t próbálta kézzel optimalizálni ASM-ben, akkor kiderült, hogy a C fordító kisebb és gyorsabb kódot adott. Az igaz, hogy ez már másfél évtizede volt, de spekulatív végrehajtással már akkor is szórakoztak az x86-ban.

>Ehhez képest te magad mondtad, hogy elég vaskos eltérések vannak fordítónként, vagy akár opciónként. Akkor pedig mégis elég sok különbség lehet a generált kódok közt, nem?

Igen, de ez messze van attól, amit egy Haskell fordító megenged. Nem is lehet összehasonlítani a kettőt, annyira más. Már többször leírtam, de leírom még 1x: C++-ban magához az algoritmushoz nem igazán nyúlhat a fordító. Haskellben simán. Hogy hozzak egy analógiát, talán tisztább lesz: SQL SELECT-ben se írod le az algoritmust, csak azt, hogy az adatokra, amik visszajönnek, milyen feltételeknek kell teljesülniük. Azt, hogy ezt pontosan hogyan éri el a DB szerver, az rá van bízva (persze sokszor érdemes neki segíteni, hogy hogyan csinálja). Ha ugyanezt C++-ban írod meg, akkor odaírsz valami konkrét algoritmust. SQL-ben nem.

> Maximum csak akkor nem, ha ezt egyetlen függvényen belül kéne így.

Használható alatt azt értem, hogy nem kell vele különösebben sokat foglalkozni. Mikor mondjuk egy sok százezer sorból álló kódod van, aminek kb. az 5%-a hot, akkor nem fogsz nekiállni egyesével csekkolni, vajon melyik részt milyen kapcsolókkal kell fordítani... És itt a PGO (profile guided optimization) se segít sokat (egyelőre - ezen talán fejlesztenek majd a későbbiekben. Ha tudná valahogy futtatni PGO közben a kódot, az sokat segítene).

> Hát lehet. Csak akkor meg írhatod meg azt a kódrészletet minden CPU minden fajtájára.

Igen, a nagyobb különbségekkel rendelkező CPU-kra külön kód lesz. És ez nyilván sokkal több meló, mint egy adott verziót megírni C++-ban, aztán lefordítani. Ritkán csinálok ilyet.

> Azért, mert még egy CPU architektúrán belül is durva eltérések lehetnek a CPU fajták között.

CPU alatt konkrét típust értek. GCC-nek, Clang-nak van ilyen opciója: -mtune=XXX. Ide elég konkrét típust kell írni (ami egyébként nyilván a másik sok paraméter értékeit állítgatja). De ez az opció messze nem tökéletes. Sokszor van, hogy egy másik típust kiválasztva gyorsul a kód. Olyan is van, hogy valami ősrégi CPU-t mondok meg neki, és gyorsul 20%-t.

> Mert egy rossz algoritmuson ...
Nu, nem egészen értem, itt mire gondolsz. Nem különösebben számít, hogy 2 kódrész milyen távol van egymástól. Amire a kódok távolsága kihatással van, az a cache. De ez sem okoz jelentős különbséget. A pipeline kiürülésének (vagy nemtom mit értesz azon, hogy a csőbe be kell húzni) semmi köze sincs a kódok távolságához.

> Pl. BoyC azt mondta, hogy amikor a Crystal Vision-t próbálta kézzel optimalizálni ASM-ben, akkor kiderült, hogy a C fordító kisebb és gyorsabb kódot adott.

Minden tiszteletem BoyC-é (szép dolgokat csinálnak, tényleg), de akkor ehhez a dologhoz nem ért(ett) kellő képpen (anno). Ez nem jön meg csak úgy, gyakorolni kell. Ő egy kicsit későbbi demoscene generáció tagja, mint én, valszeg kevesebbet asm-ezett...

> Hogy hozzak egy analógiát, talán tisztább lesz: SQL SELECT-ben se írod le az algoritmust, csak azt, hogy az adatokra, amik visszajönnek, milyen feltételeknek kell teljesülniük. Azt, hogy ezt pontosan hogyan éri el a DB szerver, az rá van bízva (persze sokszor érdemes neki segíteni, hogy hogyan csinálja). Ha ugyanezt C++-ban írod meg, akkor odaírsz valami konkrét algoritmust. SQL-ben nem.

Értem mit akarsz mondani, de az analógia kicsit sántít, mert nem egy szint a kettő. SQL-ben semmi egyebet nem lehet csinálni, mint adatot lekérni, vagy beírni az adatbázison belül, a direkt erre a célra kialakított rétegeken keresztül, míg a Haskell univerzális, elméletileg bármit le kéne tudnia algoritmizálni a direktíváid alapján, beleértve pl. a perifériákhoz való hozzáférést, vagy akármit.

> Használható alatt azt értem, hogy nem kell vele különösebben sokat foglalkozni. Mikor mondjuk egy sok százezer sorból álló kódod van, aminek kb. az 5%-a hot, akkor nem fogsz nekiállni egyesével csekkolni, vajon melyik részt milyen kapcsolókkal kell fordítani...

Hát ebben mondjuk igazad van, ennyi erővel akár kézzel is optimalizálhatod ASM-ben.

> CPU alatt konkrét típust értek. GCC-nek, Clang-nak van ilyen opciója: -mtune=XXX. Ide elég konkrét típust kell írni (ami egyébként nyilván a másik sok paraméter értékeit állítgatja). De ez az opció messze nem tökéletes. Sokszor van, hogy egy másik típust kiválasztva gyorsul a kód. Olyan is van, hogy valami ősrégi CPU-t mondok meg neki, és gyorsul 20%-t.

És a -march=XXX-szel? Az mtune esetében a végeredménynek a többi CPU-n is futnia kell, tehát extrém dolgokat nem nagyon csinálhat, nem használhat specifikus utasításokat, tartania kell a közös ABI-t, míg a march ezt eltörheti; azaz a march jobb eredményt hozhat.

> Nu, nem egészen értem, itt mire gondolsz. Nem különösebben számít, hogy 2 kódrész milyen távol van egymástól. Amire a kódok távolsága kihatással van, az a cache. De ez sem okoz jelentős különbséget. A pipeline kiürülésének (vagy nemtom mit értesz azon, hogy a csőbe be kell húzni) semmi köze sincs a kódok távolságához.

Akkor ezt rosszul tudtam.

> Értem mit akarsz mondani, de az analógia kicsit sántít, mert nem egy szint a kettő.

Okés, csak ennyi volt a cél :) Persze, sántít az analógia, de tényeg csak azért írtam ide, hogy rávilágítsak, mire gondolok.

> És a -march=XXX-szel?

Szokott számítani bizonyos esetekben (ha pont van egy olyan utasítás, amit ez megenged, és jobb lesz tőle a kód). De a lényegen nem változtat. Nagy vonalakban, AMD64-en nincsen már olyan nagy különbség az első AMD64 kompatibilis proci, és a mostani legújabb között utasítások tekintetében. Nyilván az újabb SIMD-ek nagy különbségeket tudnak okozni (AVX512) az arra alkalmas kódban, de nagy általánosságban nagy különbségek nincsenek.

> Okés, csak ennyi volt a cél :) Persze, sántít az analógia, de tényeg csak azért írtam ide, hogy rávilágítsak, mire gondolok.

Érteni értem, de ez tiszta elmélet, mert jelen pillanatban szerintem nincs ember a földön, aki tudná, hogy hogyan kéne megírni az "igazi" Haskell algoritmizálót és szerintem egy darabig nem is lesz. Úgyhogy ezeket a magas nyelveket egyelőre továbbra is hanyagolom.

> Szokott számítani bizonyos esetekben (ha pont van egy olyan utasítás, amit ez megenged, és jobb lesz tőle a kód). De a lényegen nem változtat. Nagy vonalakban, AMD64-en nincsen már olyan nagy különbség az első AMD64 kompatibilis proci, és a mostani legújabb között utasítások tekintetében. Nyilván az újabb SIMD-ek nagy különbségeket tudnak okozni (AVX512) az arra alkalmas kódban, de nagy általánosságban nagy különbségek nincsenek.

Köszi az okítást. Ha másért nem is, ezért már érdemes volt végigvinni ezt a szálat; revideálni kell pár dolgot nálam...

> nincs ember a földön, aki tudná, hogy hogyan kéne megírni az "igazi" Haskell algoritmizálót és szerintem egy darabig nem is lesz

Jó kérdés. Erről keveset tudok. De az biztos, hogy van egy csomó eredmény, és aktív kutatási terület lehet. Most még kicsi a penetrációja az ilyen nyelveknek, de elképzelhető, hogy mondjuk 50 év múlva már sokkal több mindenre lesz használható. Manapság tényleg kevés dologra használják. De amúgy ennek az is az oka, hogy más gondolkodásmódot igényel. Nekem tetszik a lisp (csak ne lenne annyira hülye a szintaktikája), egy csomó dolgot tök frappánsan meg lehet benne oldani. Olyan helyeken is használják, ahol az ember nem annyira várná, pl: https://en.wikipedia.org/wiki/Game_Oriented_Assembly_Lisp. Szóval érdemes megismerkedni ezekkel a nyelvekkel kicsit, tágítja a gondolkodásmódot (na nem mintha én annyira ismerném, de látom bennük a potenciált).

> Köszi az okítást. Ha másért nem is, ezért már érdemes volt végigvinni ezt a szálat; revideálni kell pár dolgot nálam...

Nincsmit :) Olvass utána dolgoknak azért, mert így egy-egy hozzászólásban azért nem lehet kifejteni az igazság minden szegletét.

Miért alapvetés, hogy a keletkezési helyén jobban tudod, hogy mit akarsz vele kezdeni? Egy alacsony szinten lévő osztálynak az a dolga, hogy a kapott paraméterek alapján elvégezze a dolgát, ha meg nem tudja, akkor részletesen (ne new Exception("Hiba!")) elmondja, hogy mi baja. Hogy ezzel mit kell kezdeni, az a magasabb szintű hívó dolga.

Szerintem én nem mondtam olyat, hogy "alapvetés, hogy a keletkezési helyén jobban tudod, hogy mit akarsz vele kezdeni". Csak arra hívtam fel a figyelmet, hogy a goto-t evil-nek mondják, közben a throw részben ugyanazt csinálja, mint egy goto. Sőt, rosszabb, mert nem lehet tudni, hogy hova ugrik. A goto-nál legalább látszik.

De amúgy azzal egyetértek, hogy "elmondja, hogy mi baja. Hogy ezzel mit kell kezdeni, az a magasabb szintű hívó dolga.". Csak szerintem erre az exception nem annyira jó megoldás, legalábbis a mostani formájában nem. Szerintem sokkal jobb, ha az exception-t nem dobjuk, hanem valami error objectet visszaadunk, amit aztán a hívó fél lekezel. A hívási ponton, és nem máshol. Az esetek nagy részében ez tisztább, átláthatóbb megoldás.

Amúgy is, mi dönti el, hogy valami exception-e, vagy sem? Úgy értem, exception az valami kivételes dolgot jelent, valami olyat, amit nem vártunk. Ha egy file-nál olvasás közben EOF van, akkor az vajon exception-e? Hibát jelent-e? Ha sima text file, akkor nem biztos, hogy hiba, simán lehet, hogy csak vége a file-nak. Ha egy struktúrával rendelkező file (bináris file-ok, XML, stb.), ahol még folytatódnia kellene, akkor hibát jelent.

Szerintem egy fv hívás adja csak vissza szépen az hívás eredményét, bármi is legyen az (rendben végetért, ilyen-olyan hiba). Aztán majd a hívó fél eldönti, hogy mit csinál az eredménnyel. A fordító pedig lehetőség szerint figyelmeztessen arra, ha valami potenciális hiba nincs lekezelve.

"Csak arra hívtam fel a figyelmet, hogy a goto-t evil-nek mondják, közben a throw részben ugyanazt csinálja, mint egy goto. Sőt, rosszabb, mert nem lehet tudni, hogy hova ugrik."

Miért kell feltétlenül érdekeljen, hogy hova ugrik? A normális visszatérési értéknél se tudom, hogy ki, hol és mire fogja használni. Csak annyit tudok, hogy mit kapok, és azzal mit kell csinálni.

"Szerintem sokkal jobb, ha az exception-t nem dobjuk, hanem valami error objectet visszaadunk, amit aztán a hívó fél lekezel. A hívási ponton, és nem máshol. Az esetek nagy részében ez tisztább, átláthatóbb megoldás."

Ez egyenértékű egy checked exceptionnel, lehet öt szinten feleslegesen lesz a kódban egy error object lekezelése/továbbadása, mert csak felsőbb szinten tudunk kezdeni vele valamit. Pl microserviceknél le lehet kezelni 250 helyen, hogy rossz a json input, amíg elér a controllerig, vagy lehet írni egy darab exception handlert, ami adott típusú exceptionre visszaad 400-as kódot, meg az exception szövegét.

> Miért kell feltétlenül érdekeljen, hogy hova ugrik? A normális visszatérési értéknél se tudom, hogy ki, hol és mire fogja használni. Csak annyit tudok, hogy mit kapok, és azzal mit kell csinálni.

Csak párhuzamot vontam a goto és a throw között. Ebben a tekintetben eléggé hasonlítanak. Amúgy azért lehet érdekes, hogy hova ugrik, mert azokban a nyelvekben, ahol az exception objectben nincs benne, hogy honnét jött, lehet nyomozni, hogy mégis kicsoda, és hol dobta az exceptiont (azaz honnét került a kód futattása oda). De amúgy én se tartom annyira rossz dolognak (mmint azt, hogy nem tudjuk pontosan, hogy a throw hova ugrik), szóval nem kell meggyőznöd :)

> Ez egyenértékű egy checked exceptionnel, lehet öt szinten feleslegesen lesz a kódban egy error object lekezelése/továbbadása, mert csak felsőbb szinten tudunk kezdeni vele valamit.

Igen, és ebből lesznek a rossz minőségű hibaüzenetek, hibakezelések. Mert így az öt szinten megtalálható információt, a hibának a kontextusát eldobod.

Bármire. Miért lenne meg minden információ ott, ahol az exceptiont eldobod? Teljesen szokásos, hogy az egyes szinteken az ember elkapja az exceptiont, hozzádob plusz infokat, majd továbbdobja a felhízlalt információkkal felszerelt exceptiont.

De hogy egy példát mondjak: tegyük fel, hogy van egy JSON parsered, ami stringből csinál valami JSON struktúrát. Tehát input: String, output: valami struktúra. Ha ez dob egy exceptiont, akkor abban mondjuk az lesz, hogy "Invalid char 'X', line 23". Filenév nincs, mivel a JSON parser nem tudja. 5 szinttel feljebb pedig már nem lehet tudni, hogy melyik JSON-nal van a baj, mert amúgy abból a fv-ből kiindulva 300 JSON parse-olás indul el. Szóval valahol bele kell tenni az infot arról, hogy melyik JSON-nal van a baj. Az is lehet, hogy file-ból jött a JSON, az is lehet, hogy hálózatról lett lehúzva, bármi.

Persze lehet ilyenkor azt csinálni, hogy a JSON parsenek átmegy ez a forrásnak az infoja, hogy tudjon jobb Exceptiont küldeni. De ez csak 1 példa volt. 5 szint alatt összegyűlhet annyi info, amit nem tudsz/akarsz/koncepcióellenes átadni a JSON parsernek.

Szerintem az a kód, ami átugrik 5 szintet egy throw-nál, minimum furcsa. Valszeg lusta volt a programozója.

Semmi se akadályoz meg. De ha ezt megteszed, akkor ugyanott vagy, mint az error object kezelése. Igen, az exception kényelmesebb akkor, ha nem kell hozzáadni plusz infot az exceptionhoz. Nekem viszont az az állításom, hogy rendesen megírt kódban, ahol minőségi hibakezelés, hibakiírás van, ott ez ritka. Általában lustasából van az, hogy sokkal feljebb kezelődik le egy exception.

Exception heavy kódot, ahol a catch sokkal feljebb van, mint a throw, nehezebb megismerni, követni, debugolni.

Szerintem ugyanazt mondjuk. Én csak azt mondom, hogy a "csak ha tudsz hozzáadni plusz infot" általában fent áll. Ha nem teszi meg a programozó, akkor lusta, és ennek az az eredménye az esetek egy részében, hogy rossz minőségű hibaüzenetet kap a user (vagy kerül a logba). Vagy épp az, hogy a hibát le lehetne kezelni normálisan, és nem a usernek kellene újra megpróbálnia.

Szerintem mindenki látott már "Hiba történt, próbáld újra" jellegű hibaüzenetet. Kb. ez az eredménye annak, ha vki a kód tetején kapja el az exceptiont.

> Szerintem mindenki látott már "Hiba történt, próbáld újra" jellegű hibaüzenetet. Kb. ez az eredménye annak, ha vki a kód tetején kapja el az exceptiont.

szerintem meg fogalmad sincs, miről beszélsz

az, hogy mi kerül a logba/UI-ba, az egy programozói döntés, _sehogy_ nem függ össze az exception elkapásának helyével

Figy, én asszem többet nem reagálok a hozzászólásaidra. Látszólag write-only üzemmódban vagy, és te vagy az, aki nem ért túlságosan sokat ehhez az egészhez. Kapásból pár hozzászólással feljebb megcáfolom azt, hogy "az, hogy mi kerül a logba/UI-ba, az egy programozói döntés, _sehogy_ nem függ össze az exception elkapásának helyével".

Már írtam fentebb, nem azt kell dobni, hogy Exception("Hiba!"), hanem olyasmit, hogy DuplicateResourceException("User already exists with email x@y.com"), és akkor bárhol kapod el tudni fogod, hogy erre 403-as kódot kell visszadni, üzenetnek meg az exception message-t. És meg tudod írni egy helyen, kismillió servicere, hogy ha DRE jön, akkor 403-at adok vissza, nincs kismillió*hívásmélyég helyen if.

Ez nyilvánvaló. Nem egészen értem, hogy ez hogy függ össze az error objectekkel. Ott is van nyilván ennek megfelelője. És ugyanúgy lehet tudni, hogyha DuplicateResourceException error object jön, akkor arra 403-mal lehet reagálni. Ez teljesen ugyanaz mind2 esetben.

Ami a különbség lehet, hogy szerintem ritka az, hogy egy fv-ben nincs semmi olyan context, amit hozzá lehetne csatolni egy adott hibához. Ergo, egy jó hibakezeléssel rendelkező kód kb. minden szinten elkapja a keletkező exceptiont, hozzáadja a saját kis információmorzsáit, majd továbbdobja. De ezt már többször leírtam. Ha szerinted ez ritka, akkor más a tapasztalatunk, ennyi.

Nem értem, hogy jön ide a goto.

>> Szerintem az a kód, ami átugrik 5 szintet egy throw-nál, minimum furcsa. Valszeg lusta volt a programozója.

> Az fel sem merül, hogy adott esetben az "5 szinttel feljebb" akár egy másik kontinensen is lehet?

Miért releváns, hogy az 5 szinttel feljebb az egy másik kontinensen van, annak a tekintetében, hogy a throw/catch páros milyen messze van egymástól?

Miért kell feltétlenül érdekeljen, hogy hova ugrik?

A legfontosabb dolog a programozásnál, hogy tudjam mikor, mi és miért történik.
Ha valahová beteszek egy exception dobást, akkor tudnom kell, hogy az minden hívási ágon megfelelőn van-e kezelve, ahol nincs, ott kezelnem kell megfelelően.
Általában ezzel a résszel nem sokan törődnek.

A hibaérték visszaadás (illetve a checked exception) kierőszakolja ezt, ami jó.

Ez egyenértékű egy checked exceptionnel

Nem egyenértékű! Rengeteg hátránya van a hibaérték visszaadáshoz képest.

Pl microserviceknél le lehet kezelni 250 helyen, hogy rossz a json input, amíg elér a controllerig

Ha microservice, akkor 250 sorosnak se kellene legyen, nemhogy 250 mélységűnek :-)
Egyébként fontos lenne a kód tervezés, amire nem szoktak a programozók sok gondot fordítani.
Ha 250 mélységben kell hibát visszaadni, akkor legalább jól látszik, hogy ott el van rontva a kód tervezés, de nagyon.
Ahelyett, hogy a hívás lista legalján derül ki, hogy hiba van, azt fel kellene mozgatni minél feljebb.
A json a hívás lista elején jön be, akkor miért megy le hibásan az aljáig?
Már ott a tetején lehetne ellenőrizni, hogy rendben van-e, majd az ellenőrzött, értelmezett adat (nem json) mehet lefelé.
Ha hiba van, akkor meg egyből mehet a hiba vissza a hívónak, nem kell még köröket futni előtte.

"Ha valahová beteszek egy exception dobást, akkor tudnom kell, hogy az minden hívási ágon megfelelőn van-e kezelve, ahol nincs, ott kezelnem kell megfelelően."

Egynél több embert foglalkoztató projecten ez nem valósítható meg. Ha modult fejlesztesz, nem valósítható meg.

"Ha microservice, akkor 250 sorosnak se kellene legyen, nemhogy 250 mélységűnek :-)"

Nem egy helyen van 250 mélység, hanem 50+ helyen 10+ projecten át használnak json-t. Foglalkozhatok vele ennyi helyen, vagy berakhatok egy egységes hibakezelést egy dependenciába, és azon se kell izgulni, hogy az 5+ csapat milyen egyedi hibakezeléseket talál ki.

Egynél több embert foglalkoztató projecten ez nem valósítható meg. Ha modult fejlesztesz, nem valósítható meg.

Így van, exceptionnél ezt nagyon nehéz megvalósítani. Így majd az első elszállásnál, rejtélyes adatvesztésnél, inkonzisztenciánál talán rendbe lesz rakva az az ág is, ahol nincs lekezelve.

Hibaérték visszaadásnál viszont könnyű megvalósítani, mert nem fog fordulni a program, amíg le nincs kezelve a hiba.

Az explicit hibakezelesnek szerintem a legnagyobb elonye az, hogy mar fejlesztes alatt az osszes hibalehetoseget at kell gondolnod. Tehat sokkal kevesbe lesz olyan, hogy egy prodban futo szoftvernek a logjat kell turnod, hogy mi tortenhetett, amire nem gondoltal.
Plusz a jo szoftverek jol kezelik a hibakat, amiknek az irasahoz egyebkent is kell, hogy eleg pontosan tudd, hogy mikor milyen hibak lehetnek.

Igen, ilyesmire gondolok, mint az Either.
Ha eddig String-et adott vissza, ezután meg Either -et, akkor nem fog fordulni, amíg az összes hívónál át nem írják a kódot (valahogy kezelik a hibát).
Exception dobásnál továbbra is minden megy tovább, nem kell semmin se változtatni.

> a throw részben ugyanazt csinálja, mint egy goto. Sőt, rosszabb, mert nem lehet tudni, hogy hova ugrik

Dehogynem lehet tudni. Az igaz, hogy az exception "ugrása" nem 1:1 kapcsolat, több helyen lehet kezelni (vagy éppen szándékosan nem kezelni), inkább előny a gyakorlatban, mint hátrány.

Ad absurdum ha nagyon nem megy az előre gondolkodás, Ctrl-click (vagy hasonló, attól függ mit használsz) az exceptionre, és már ott is a lista, mi történhet. Még csak fejben sem kell tartani! \o/

> Amúgy is, mi dönti el, hogy valami exception-e, vagy sem?
> A fordító pedig lehetőség szerint figyelmeztessen arra, ha valami potenciális hiba nincs lekezelve.

Az exception pont arra jó, hogy a "kivételes" (exception, you see?) le legyenek kezelve. A fordító nem tud minden létező programot szemantikailag elemezni, ott még nem tart a technológia, hogy a fejlesztő szándékát kitalálja. Nem lehet compile-time warningot adni olyan nem várt dolgokra, hogy el fog fogyni a RAM futtatáskor, vagy nem lesz hálózat, vagy 503-at ad a webservice, vagy nullát ír be a user egy osztás inputjába, vagy február 29-et állítunk be nem szökőévben, stb.

Amúgy te ebben a szoftverfejlesztés dologban mennyire vagy járatos? :)

> Dehogynem lehet tudni.

Tehát te azt mondod, hogy csak a throw-t nézve, meg tudod mondani, hogy hova ugrik a kód?

> Ad absurdum ha nagyon nem megy az előre gondolkodás, Ctrl-click (vagy hasonló, attól függ mit használsz) az exceptionre, és már ott is a lista, mi történhet. Még csak fejben sem kell tartani! \o/

Elképzelésem sincs, hogy itt mire gondolsz.

> Az exception pont arra jó, hogy a "kivételes" (exception, you see?) le legyenek kezelve

Nézd meg egy korábbi hozzászólásomat, ahol épp ezt feszegetem, hogy mi az, hogy "kivételes". Ha gondolod, reagálj rá.

> A fordító nem tud minden létező programot szemantikailag elemezni, ott még nem tart a technológia, hogy a fejlesztő szándékát kitalálja.

Ilyet senki se mondott. Mielőtt reagálsz valamire, szerintem egy picit jobban olvasd el.

> hogy el fog fogyni a RAM futtatáskor

A programok 99.9%-a ezt az esetet nem tudja/akarja lekezelni. Ha elfogyott a RAM, az már régen rossz.

> vagy nem lesz hálózat, vagy 503-at ad a webservice

Már hogy a fenébe ne tudná ezt compile time, hogy ennek a lehetősége fent áll? Simán írok neked olyan kódot, ahol a C++ compiler figyelmeztetni fog warninggal erre.

> nullát ír be a user egy osztás inputjába, vagy február 29-et állítunk be nem szökőévben

Ez pedig nem feltétlenül jelent exceptiont, az én értelmezésem szerint. A user inputot minden esetben ellenőrizni kell.

> Amúgy te ebben a szoftverfejlesztés dologban mennyire vagy járatos? :)

Ezt meg nemtom minek kellett ideírnod. Viccesnek nem vicces, a személyeskedésnek pedig nincs túl sok értelme.

Szorry, de ez nem ragequit. A normális beszélgetőpartnereket szeretem, te pedig nem úgy viselkedsz, ennyi. Semmit nem tesznek hozzá a hozzászólásaid ehhez a beszélgetéshez. Semmire nem reagálsz, csak írod. Itt a lehetőség, hogy valamit hozzátegyél akkor:

> C++-t hoztad példának, szerintem nézd meg egy magasabb szintű nyelvben is az exception kezelést

Mik a jelentős különbségek a magasabb szintű nyelvekben a C++-hoz képest az exceptionkezelés tekintetében, amik megváltoztathatják a véleményemet?

Ő találhatta ki a 8-bites karakterkódolást, meg a nemzeti kódlapokat is.

--
eutlantis

Majdnem felsírtam olvasás közben. Nagyon jó gyűjtemény!

---
"Errors are red
My screen in blue
Someone help me
I've deleted Sys32"

Kieg: egyik kollégánk hőbörgött azon, hogy én hőbörögtem az Exceptionok miatt, ezért íme egy kis érdekesség:

Normálisan így lenne egy file-megnyitó függvény:


    public java.io.FileInputStream OpenFile (String fname) {

        java.io.File f= null,
        java.io.FileInputStream fi= null;

        f= new java.io.File (fname);
        if (f==null) goto RETURN;
        fi= new java.io.FileInputStream (f);

RETURN:
        return fi;
    }

Ugyanez Exception-nel, goto-nélkül:


    public java.io.FileInputStream OpenFile (String fname) {
        java.io.File f= null,
        java.io.FileInputStream fi= null;

try { do {

        f= new java.io.File (fname);
        if (f==null) continue;
        fi= new java.io.FileInputStream (f);

} while (false); } catch (Exception e) {}

        return fi;
    }

goto RETURN; helyett return fi;? A goto-t azert nem szereti aki nem szereti, mert nem lehet rola rogton tudni, hogy hova ugrik. Errol meg igen.

--
When you tear out a man's tongue, you are not proving him a liar, you're only telling the world that you fear what he might say. -George R.R. Martin

Nemtriviális esetekben a RETURN címke az, ahol felszabadítjuk az erőforrásokat, kiírunk valamilyen debug-üzenetet, adott esetben a debuggerben ide rakjuk a megállási pontot... Csupa olyasmi, ami Nyomasek Bobó kis világában nem is létezik, ezért nem is aggódik miattuk.

Arra valo a finally, illetve a destruktorban felszabaditott mindenfele. Jo, utobbi nem jo, mert ez pont java kod.
Szerencsere a peldadban pont nem volt ilyesmi, csak egy return.

--
When you tear out a man's tongue, you are not proving him a liar, you're only telling the world that you fear what he might say. -George R.R. Martin

Sikerült megtalálnod a leglényegtelenebb pontot. Semmit nem változtat a kódon, ha a new File-t előbb változóba rakod, nem az a példa lényege. Antipattern, agyam eldobom, az, hogy meg kell majd nyomni egy billentyűkombinációt az ide-ben, ha mégis kell? YAGNI, KISS, premature assumption?

Abban igazad van, hogy az f==null ellenőrzése felesleges, egyből ugrik az Exception-re -- ahol találgathatunk, hogy a 'new File' nem sikerült, vagy a 'new FileInputStream'

Szerk: sajnos nem minden ilyen frenkizős kifejezést ismerek, de cserébe elárulom, hogy épp most állok át a B.U.S.H.-ról a B.R.U.S.H.-ra.

Nem találtagni kell, hanem megfelelő catch-et használni. A File constructora csak nullpointerexceptiont dob, ha a filenév null. Nem végez semmilyen egyéb ellenőrzést, a File objektum nem invalid, ha nem létezik a file (hogy mást ne mondjak, van exists metódusa). Nem létező vagy nem olvasható filera hiba majd csak a FileInputStreamnél lesz, mert ott már meg is próbálja nyitni, mert ő már tudja, hogy mit akarsz vele kezdeni.

"sajnos nem minden ilyen frenkizős kifejezést ismerek"

Nekem meg rá kellett gugliznom, hogy mi fán terem a frenkizés, hát gratulálok. Előbbit (guglizás) ajánlom neked is, mert közel 20 éves fogalmakról van szó.