Homebrew (Wikipedie), správce balíčků pro macOS a od verze 2.0.0 také pro Linux, byl vydán ve verzi 4.5.0. Na stránce Homebrew Formulae lze procházet seznamem balíčků. K dispozici jsou také různé statistiky.
Byl vydán Mozilla Firefox 138.0. Přehled novinek v poznámkách k vydání a poznámkách k vydání pro vývojáře. Řešeny jsou rovněž bezpečnostní chyby. Nový Firefox 138 je již k dispozici také na Flathubu a Snapcraftu.
Šestnáctý ročník ne-konference jOpenSpace se koná 3. – 5. října 2025 v Hotelu Antoň v Telči. Pro účast je potřeba vyplnit registrační formulář. Ne-konference neznamená, že se organizátorům nechce připravovat program, ale naopak dává prostor všem pozvaným, aby si program sami složili z toho nejzajímavějšího, čím se v poslední době zabývají nebo co je oslovilo. Obsah, který vytvářejí všichni účastníci, se skládá z desetiminutových
… více »Richard Stallman přednáší ve středu 7. května od 16:30 na Technické univerzitě v Liberci o vlivu technologií na svobodu. Přednáška je určená jak odborné tak laické veřejnosti.
Jean-Baptiste Mardelle se v příspěvku na blogu rozepsal o novinkám v nejnovější verzi 25.04.0 editoru videa Kdenlive (Wikipedie). Ke stažení také na Flathubu.
TmuxAI (GitHub) je AI asistent pro práci v terminálu. Vyžaduje účet na OpenRouter.
Byla vydána nová verze R14.1.4 desktopového prostředí Trinity Desktop Environment (TDE, fork KDE 3.5, Wikipedie). Přehled novinek i s náhledy v poznámkách k vydání. Podrobný přehled v Changelogu.
Bylo vydáno OpenBSD 7.7. Opět bez písničky.
V Tiraně proběhl letošní Linux App Summit (LAS) (Mastodon). Zatím nesestříhané videozáznamy přednášek jsou k dispozici na YouTube.
Jako priklad si vezmeme faktorial:
int fact_rec(int n) { if (n == 0) return 1; return n * fact_rec(n - 1); } int fact_iter(int n) { int r = 1; for (int i = n; i >= 1; i--) r *= i; return r; }
Pro rekurzivni faktorial generuje GCC (4.7.1) s prepinacem -O0 opravdu naivni kod.
0: push rbp 1: mov rbp,rsp 4: sub rsp,0x10 8: mov DWORD PTR [rbp-0x4],edi b: cmp DWORD PTR [rbp-0x4],0x0 f: jne 18 <fact_rec+0x18> 11: mov eax,0x1 16: jmp 29 <fact_rec+0x29> 18: mov eax,DWORD PTR [rbp-0x4] 1b: sub eax,0x1 1e: mov edi,eax 20: call 25 <fact_rec+0x25> 25: imul eax,DWORD PTR [rbp-0x4] 29: leave 2a: ret
S prepinacem -O1 uz je to o neco lepsi, jsou odstranena zbytecna prirazeni, nevytvari se zbytecne ramec na zasobniku, jenom obsah registru ebx se uklada na zasobnik, ale porad je to kod, ktery se podoba tomu vstupnimu.
0: push rbx 1: mov ebx,edi 3: mov eax,0x1 8: test edi,edi a: je 17 <fact_rec+0x17> c: lea edi,[rdi-0x1] f: call 14 <fact_rec+0x14> 14: imul eax,ebx 17: pop rbx 18: ret
S prepinacem -O2 uz dostaveme neco trochu prekvapivejsiho, rekurze (vsimnte si, ze to neni bezna koncova rekurze) je prevedena na cyklus:
0: test edi,edi 2: mov eax,0x1 7: je 1a <fact_rec+0x1a> 9: nop DWORD PTR [rax+0x0] 10: imul eax,edi 13: sub edi,0x1 16: jne 10 <fact_rec+0x10> 18: repz ret 1a: repz ret
Pro srovnani uvadim kod vygenerovany pro funkci fact_iter
s prepinacem -O2:
0: test edi,edi 2: mov eax,0x1 7: jle 1a <fact_iter+0x1a> 9: nop DWORD PTR [rax+0x0] 10: imul eax,edi 13: sub edi,0x1 16: jne 10 <fact_iter+0x10> 18: repz ret 1a: repz ret
Jak kazdy snadno nahledne, oba vystupy jsou stejne. Takze jake z toho plyne moralni ponauceni? Modre nebo zelene lentilky, stejne skonci vsechny stejnou barvou...
A jako bonus uvadim vysledek pro -O3.
0: test edi,edi 2: je 12a <fact_rec+0x12a> 8: mov edx,edi a: mov esi,edi c: shr edx,0x2 f: lea ecx,[rdx*4+0x0] 16: test ecx,ecx 18: je 130 <fact_rec+0x130> 1e: cmp edi,0x6 21: jbe 130 <fact_rec+0x130> 27: lea eax,[rdi-0x1] 2a: mov DWORD PTR [rsp-0x18],edi 2e: movdqa xmm4,XMMWORD PTR [rip+XXXXX] 35: 36: mov DWORD PTR [rsp-0x14],eax 3a: lea eax,[rdi-0x2] 3d: movd xmm2,DWORD PTR [rsp-0x14] 43: mov DWORD PTR [rsp-0x10],eax 47: lea eax,[rdi-0x3] 4a: movd xmm1,DWORD PTR [rsp-0x10] 50: mov DWORD PTR [rsp-0xc],eax 54: xor eax,eax 56: movd xmm0,DWORD PTR [rsp-0xc] 5c: punpckldq xmm1,xmm0 60: movd xmm0,DWORD PTR [rsp-0x18] 66: punpckldq xmm0,xmm2 6a: punpcklqdq xmm0,xmm1 6e: movdqa xmm1,XMMWORD PTR [rip+XXXXX] 75: 76: nop WORD PTR cs:[rax+rax*1+0x0] 7d: 80: movdqa xmm3,xmm1 84: psrldq xmm1,0x4 89: add eax,0x1 8c: movdqa xmm2,xmm0 90: cmp edx,eax 92: pmuludq xmm3,xmm0 96: paddd xmm0,xmm4 9a: psrldq xmm2,0x4 9f: pmuludq xmm2,xmm1 a3: pshufd xmm1,xmm3,0x8 a8: pshufd xmm2,xmm2,0x8 ad: punpckldq xmm1,xmm2 b1: ja 80 <fact_rec+0x80> b3: movdqa xmm2,xmm1 b7: sub edi,ecx b9: cmp esi,ecx bb: psrldq xmm2,0x8 c0: movdqa xmm0,xmm2 c4: psrldq xmm2,0x4 c9: pmuludq xmm0,xmm1 cd: psrldq xmm1,0x4 d2: pshufd xmm0,xmm0,0x8 d7: pmuludq xmm1,xmm2 db: pshufd xmm1,xmm1,0x8 e0: punpckldq xmm0,xmm1 e4: movdqa xmm1,xmm0 e8: psrldq xmm1,0x4 ed: movdqa xmm2,xmm1 f1: psrldq xmm1,0x4 f6: pmuludq xmm2,xmm0 fa: psrldq xmm0,0x4 ff: pmuludq xmm1,xmm0 103: pshufd xmm0,xmm2,0x8 108: pshufd xmm1,xmm1,0x8 10d: punpckldq xmm0,xmm1 111: movd DWORD PTR [rsp-0x18],xmm0 117: mov eax,DWORD PTR [rsp-0x18] 11b: je 137 <fact_rec+0x137> 11d: nop DWORD PTR [rax] 120: imul eax,edi 123: sub edi,0x1 126: jne 120 <fact_rec+0x120> 128: repz ret 12a: mov eax,0x1 12f: ret 130: mov eax,0x1 135: jmp 120 <fact_rec+0x120> 137: ret
Tuhle davku vogonske poezie se mi opravdu nechce rozebirat, ale vypada to, ze pro male hodnoty n (<= 6) se pouzije bezny algoritmus a na zbytek vypocet pomoci vektorovych instrukci.
Tiskni
Sdílej:
o tomto som tiez pisal blog pred 3 rokmi, ked som sa k tomu dostal niekde cez reddit.Občas se vracím k tématům, o kterých jsme už v Receptáři hovořili. Není divu, každý se nemůže dívat na všechny pořady. Tak třeba paní Emilie Bezoušková...
implementovane to je v tree-tailcall.cDiky za info, presne tohle jsem chtel vedet. Cekal jsem, ze to bude sofistikovanejsi a vychazet primo z vnitrni SSA reprezentace.
ale podla standardu sa na to neda spoliehat (a to prekvapivo ani v common lispe)V CommonLispu se podle standardu neda spolehat ani na ty tail cally.
a v jiných jazycích to tolik nevadí, protože se o to postarají prostředky samotného jazyka i bez optimalizace.V některých jazycích to jde vypnout – zejména kvůli ladění (např. v F#, k němuž se vztahovala původní diskuze).
Je sice hezké, že překladač je dost chytrý na to, aby sám převáděl rekurze na cykly, ale když se objeví nějaké chybky, tak skutečnost, že neoptimalizovaný kód, který používá k ladění, se silně liší od optimalizovaného, může být za určitých okolností docela prolbematická...To ano, ovšem v GCC by mělo jít nějakým CFLAG zapnout jednu konkrétní optimalizaci, ne? Případně by mělo jít debugovat optimalizovaný kód apod...
PS: Ty oprefixované NOPy (obvlášť ten v poslední verzi vygenerované přes -O3) mně dostalyKvůli čemu tam je? Zarovnání? Jinak jednoduché NOPy se afaik nevidí už vlastně nikdy (na x86), pokud najdeš v binárce jednoduché NOPy, nejspíš se v ní někdo hrabal
Kvůli čemu tam je? Zarovnání?Jo, kvuli zarovnani. AMD64 ma instrukcni cache o velikosti 16B, takze ma rado, kdyz jsou skoky zarovnane na 16B. Teoreticky by mely jit pouzit slozitejsi NOP i pro prefetch, ale na to mi prijde rozumnejsi pouzit primo instrukce prefetchX z SSE.
AMD64 ma instrukcni cache o velikosti 16BJako velikost jednoho cacheline?
Ty oprefixované NOPy (obvlášť ten v poslední verzi vygenerované přes -O3) mně dostalyOn je zajímavej i ten "repz ret". Tipoval bych to na ten prefetch jak psal deda.jabko. Pro ostatní co neví (
Jakó... To mi zní dost jako černá magie. Evidentně udělaly optimalizační metody v překladačích kus cesty kupředu od dob, kdy nás o nich nesměle učili, jak jakože umí třeba občas za vhodnej okolností vytáhnout mimo cyklus invariant... :)
Což o to, pokrok jde kupředu, ale todle mi přijde už fakt jako hodně moc. To je skoro jako kdyby ten překladač věděl, že počítáte faktoriál a prostě to celé udělal jinak, lépe a radostněji. Myslím tu poslední verzi. To mi jako někdo vysvětlete, jak todle pozná. Pochopit co dělá cizí zdroják je kolikrát problém pro člověka, ne tak pro stroj... :D
To je skoro jako kdyby ten překladač věděl, že počítáte faktoriál a prostě to celé udělal jinak, lépe a radostněji.A za chvíli ti řekne, aby sis s tou vázou nedělal starosti
Ne, váženě, ten překladač neví, že počítáš faktoriál, ale ví, že něco cyklicky děláš pomocí tail callu,Vtip je v tom, ze v tom prikladu neni pouzity tail call, a presto to prekladac dokazal prelozit jako cyklus.
tree-tailcall.c
.
V kostce: on pracuje s tail callem ve tvaru (obecně) return a + m * func(...);
, kde a
ani m
nezávisí na výsledku toho tail volání. Předeve to na cyklus s akumulátory (zvlášť pro aditivní a multiplikativní proměnnou podle potřeby). Klasický tail call (tedy return func(...);
) je pouze specielním případem, kdy a = 0
, m = 1
, tedy bez akumulátoru.
Chytrej trik.
let rec map mapper = function | [] -> [] | x::xs -> mapper x :: (map mapper xs)tento map sa prelozi ako cyklus:
let map mapper list = let rec map mapper acc = function | [] -> acc | x::xs -> xs |> map mapper (mapper x :: acc) list |> map mapper [] |> List.rev
tento map sa prelozi ako rekurzia, pri velkych zoznamoch hrozi stackoverflow:Záleží na sémantice a implementaci. Např. v GHC Haskellu by byla preferována první verze.
tento map sa prelozi ako cyklus:Bohužel toto řešení není příliš efektivní a ani přehledné.
let rec map1 mapper = function | [] -> [] | x::xs -> mapper x :: (map1 mapper xs) let map2 mapper list = let rec map mapper acc = function | [] -> acc | x::xs -> map mapper (mapper x :: acc) xs List.rev (map mapper [] list) let map3 mapper list = let rec map3 cnt mapper = function | [] -> cnt [] | x::xs -> map3 (fun l -> cnt (mapper x :: l)) mapper xs map3 id mapper listPodľa môjho testu:
List.map
. Pro OCaml jsou nějaká měření v článku Optimizing List.map.
00000000 <fact_rec>: 0: 55 push %ebp 1: 89 e5 mov %esp,%ebp 3: 83 ec 18 sub $0x18,%esp 6: 83 7d 08 00 cmpl $0x0,0x8(%ebp) a: 75 07 jne 13 <fact_rec+0x13> c: b8 01 00 00 00 mov $0x1,%eax 11: eb 10 jmp 23 <fact_rec+0x23> 13: 8b 45 08 mov 0x8(%ebp),%eax 16: 48 dec %eax 17: 89 04 24 mov %eax,(%esp) 1a: e8 fc ff ff ff call 1b <fact_rec+0x1b> 1f: 0f af 45 08 imul 0x8(%ebp),%eax 23: c9 leave 24: c3 ret-O1
00000000 <fact_rec>: 0: 53 push %ebx 1: 83 ec 18 sub $0x18,%esp 4: 8b 5c 24 20 mov 0x20(%esp),%ebx 8: 85 db test %ebx,%ebx a: 74 10 je 1c <fact_rec+0x1c> c: 8d 43 ff lea -0x1(%ebx),%eax f: 89 04 24 mov %eax,(%esp) 12: e8 fc ff ff ff call 13 <fact_rec+0x13> 17: 0f af c3 imul %ebx,%eax 1a: eb 05 jmp 21 <fact_rec+0x21> 1c: b8 01 00 00 00 mov $0x1,%eax 21: 83 c4 18 add $0x18,%esp 24: 5b pop %ebx 25: c3 ret-O2
00000000 <fact_rec>: 0: 8b 54 24 04 mov 0x4(%esp),%edx 4: b8 01 00 00 00 mov $0x1,%eax 9: 85 d2 test %edx,%edx b: 74 0a je 17 <fact_rec+0x17> d: 8d 76 00 lea 0x0(%esi),%esi 10: 0f af c2 imul %edx,%eax 13: 4a dec %edx 14: 75 fa jne 10 <fact_rec+0x10> 16: c3 ret 17: c3 ret-O3 == -O2 -Os
00000000 <fact_rec>: 0: 55 push %ebp 1: 89 e5 mov %esp,%ebp 3: 8b 55 08 mov 0x8(%ebp),%edx 6: b8 01 00 00 00 mov $0x1,%eax b: 85 d2 test %edx,%edx d: 74 06 je 15 <fact_rec+0x15> f: 0f af c2 imul %edx,%eax 12: 4a dec %edx 13: eb f6 jmp b <fact_rec+0xb> 15: 5d pop %ebp 16: c3 ret
00000000 <fact_rec>: 0: 8b 54 24 04 mov 0x4(%esp),%edx 4: b8 01 00 00 00 mov $0x1,%eax 9: 85 d2 test %edx,%edx b: 74 0d je 1a <fact_rec+0x1a> d: 8d 76 00 lea 0x0(%esi),%esi 10: 0f af c2 imul %edx,%eax 13: 83 ea 01 sub $0x1,%edx 16: 75 f8 jne 10 <fact_rec+0x10> 18: f3 c3 repz ret 1a: f3 c3 repz retKromě přidání těch repz (slackware default je i486) se změnil "dec" na "sub" (zajímavý, sub má delší opkód). Ještě zajímavější je ten "lea", což je jestli se nepletu taky jen "nop".