Dnes a zítra probíhá vývojářská konference Google I/O 2025. Sledovat lze na YouTube a na síti 𝕏 (#GoogleIO).
V Bostonu probíhá konference Red Hat Summit 2025. Vybrané přednášky lze sledovat na YouTube. Dění lze sledovat na síti 𝕏 (#RHSummit).
Společnost Red Hat oficiálně oznámila vydání Red Hat Enterprise Linuxu 10. Vedle nových vlastností přináší také aktualizaci ovladačů a předběžné ukázky budoucích technologií. Podrobnosti v poznámkách k vydání.
Tuto sobotu 24. května se koná historicky první komunitní den projektu Home Assistant. Zváni jsou všichni příznivci, nadšenci a uživatelé tohoto projektu. Pro účast je potřebná registrace. Odkazy na akce v Praze a v Bratislavě.
Troy Hunt představil Have I Been Pwned 2.0, tj. nový vylepšený web služby, kde si uživatelé mohou zkontrolovat, zda se jejich hesla a osobní údaje neobjevili v únicích dat a případně se nechat na další úniky upozorňovat.
Microsoft představil open source textový editor Edit bežící v terminálu. Zdrojové kódy jsou k dispozici na GitHubu pod licencí MIT.
V Seattlu a také online probíhá konference Microsoft Build 2025. Microsoft představuje své novinky. Windows Subsystem for Linux je nově open source. Zdrojové kódy jsou k dispozici na GitHubu pod licencí MIT.
Z příspěvku Turris Sentinel – co přinesl rok 2024 na blogu CZ.NIC: "Za poslední rok (únor 2024 – únor 2025) jsme zachytili 8,3 miliardy incidentů a to z 232 zemí a z jejich závislých území. Tyto útoky přišly od 6,2 milionu útočníků (respektive unikátních adres). SMTP minipot je stále nejlákavější pastí, zhruba 79 % útoků bylo směřováno na tento minipot, 16 % útoků směřovalo na minipot Telnet, 3 % útoků směřovaly na minipot HTTP a 2 % na minipot FTP. Dále jsme zaznamenali 3,2 milionu unikátních hesel a 318 tisíc unikátních loginů, které útočníci zkoušeli."
Byla vydána (Mastodon, 𝕏) nová verze 3.0.4 svobodné aplikace pro úpravu a vytváření rastrové grafiky GIMP (GNU Image Manipulation Program). Přehled novinek v oznámení o vydání a v souboru NEWS na GitLabu. Nový GIMP je již k dispozici také na Flathubu.
Byla vydána nová stabilní verze 7.4 webového prohlížeče Vivaldi (Wikipedie). Postavena je na Chromiu 136. Přehled novinek i s náhledy v příspěvku na blogu.
Potřeboval jsem převést pole hodnot o rozměrech cca 800x400 na pole o rozměrech 1600x800. Nové body se počítaly v závislosti na okolních bodech v původním poli. Po spuštění, kdy se mělo zpracovat 9 datových polí paralelně přes async.parallel(), se cca půl minuty nic nedělo a pak se vypsal výsledek. První zklamání bylo, že ten async se využije jen pokud funkce v něm volané provádí nějaké IO, což není tenhle případ. Takže to vlastně paralelně vůbec neběží... OK, nevadí, alespoň na to stažení dat z internetu se to využije, když už ne na samotnej výpočet. Jaká by asi byla rychlost v Pythonu, napadlo mě. Napsal jsem krátkej kód pro porovnání.
Na začátku se vytvoří pole a vloží se tam nejaký čísla, se kterýma poté proběhne výpočet.
var width = 1600; var height = 1200; var nwidth = width * 2; var nheight = height * 2; var data = []; var newdata = new Array(nwidth * nheight); var someval = [0, 0, 0, 0.4, 1, 1.1, 8]; var it = 0; var size = width * height; for (var i = 0; i < size; i++) { data.push(someval[it++ % 7]); } for (var y = 0; y < height-1; y++) { for (var x = 0; x < width - 1; x++) { var nx = x*2; var ny = y*2; newdata[ny*nwidth + nx] = data[y*width + x]; newdata[ny*nwidth + nx + 1] = (data[y*width + x] + data[y*width + x + 1]) / 2.0; newdata[(ny+1)*nwidth + nx] = (data[y*width + x] + data[(y+1)*width + x]) / 2.0; newdata[(ny+1)*nwidth + nx + 1] = (data[y*width + x] + data[y*width + x + 1] + data[(y+1)*width + x] + data[(y+1)*width + x + 1]) / 4.0; } }
# time nodejs source.js
1. běh | 2. běh | 3. běh |
---|---|---|
real 0m1.501s
|
real 0m1.438s
|
real 0m1.432s
|
width = 1600 height = 1200 nwidth = width * 2 nheight = height * 2 data = [] newdata = [None] * (nwidth * nheight) someval = [0, 0, 0, 0.4, 1, 1.1, 8] it = 0 for i in range(0, width*height): data.append(someval[it % 7]) it += 1 for y in range(0, height-1): for x in range(0, width - 1): nx = x*2 ny = y*2 newdata[ny*nwidth + nx] = data[y*width + x] newdata[ny*nwidth + nx + 1] = (data[y*width + x] + data[y*width + x + 1]) / 2. newdata[(ny+1)*nwidth + nx] = (data[y*width + x] + data[(y+1)*width + x]) / 2. newdata[(ny+1)*nwidth + nx + 1] = (data[y*width + x] + data[y*width + x + 1] + data[(y+1)*width + x] + data[(y+1)*width + x + 1]) / 4.
# time python2.7 source.py
1. běh | 2. běh | 3. běh |
---|---|---|
real 0m5.989s
|
real 0m5.899s
|
real 0m5.843s
|
# time python3.4 source.py
1. běh | 2. běh | 3. běh |
---|---|---|
real 0m7.910s
|
real 0m8.044s
|
real 0m8.294s
|
#define WIDTH 1600 #define HEIGHT 1200 const int width = WIDTH; const int height = HEIGHT; const int nwidth = 2 * WIDTH; const int nheight = 2 * HEIGHT; double data[WIDTH * HEIGHT]; double newdata[4 * WIDTH * HEIGHT]; double someval[] = {0, 0, 0, 0.4, 1, 1.1, 8}; int main(int argc, char *argv[]) { int it = 0; for (int i = 0; i < width*height; i++, it++) { data[i] = someval[it % 7]; } for (int y = 0; y < height-1; y++) { for (int x = 0; x < width - 1; x++) { int nx = x*2; int ny = y*2; newdata[ny*nwidth + nx] = data[y*width + x]; newdata[ny*nwidth + nx + 1] = (data[y*width + x] + data[y*width + x + 1]) / 2.; newdata[(ny+1)*nwidth + nx] = (data[y*width + x] + data[(y+1)*width + x]) / 2.; newdata[(ny+1)*nwidth + nx + 1] = (data[y*width + x] + data[y*width + x + 1] + data[(y+1)*width + x] + data[(y+1)*width + x + 1]) / 4.; } } return 0; }
# gcc -O0 -std=c99 -o xxx0 xxx.c; time ./xxx0
1. běh | 2. běh | 3. běh |
---|---|---|
real 0m0.107s
|
real 0m0.110s
|
real 0m0.109s
|
# gcc -O2 -std=c99 -o xxx2 xxx.c; time ./xxx2
1. běh | 2. běh | 3. běh |
---|---|---|
real 0m0.058s
|
real 0m0.054s
|
real 0m0.061s
|
Co se tohoto testu týká, probíhal na nezatíženém desktopu (X86_64; AMD Phenom(tm) II X4 965 Processor; 8GB Ram). Rychlost Python kódu je celkem žalostná, obvzlášť trojková řada vyhořela. Nodejs si vlastně nevedl zase tak špatně (v porovnání s C tedy ano ).
Ač mi psaní server-side skriptů v javascriptu přijde pořád trochu úchylný, jde to. Malá rychlost není asi největší problém. Jednoduché tvoření async friendly kódu (když si dá člověk pozor a nevytvoří callback-hell), pro některé možnost sdílet kód mezi frontendem a backendem, mohou být výhody. Co mi tedy neuvěřitelně vadí je, že při chybě typu volám neexistující funkci nevyhodí nodejs traceback, ačkoliv to tracebacky umí. S function scope životností proměnných by se člověk smířil, s přístupem vše je objekt a čísla jsou jen floating point, nakonec asi taky. Psaní C++ modulu, případně volání JS kódu z C++, je zdá se mnohem hezčí, než v případě Pythonu. Ale starýho psa novým kouskům nenaučíš...
Tiskni
Sdílej:
someval = [0, 0, 0, 0.4, 1, 1.1, 8] it = 0 for i in range(0, width*height): data.append(someval[it % 7]) it += 1na:
someval = (0, 0, 0, 0.4, 1, 1.1, 8) data = tuple( someval[it % 7] for it, i in enumerate(range(width*height)) )
Ještě je možné úvodní inicializaci v Pythonu zrychlit takto:
size = width*height someval = [0, 0, 0, 0.4, 1, 1.1, 8] data = someval*(size/len(someval) + 1) while len(data) > size: data.pop()
it
se dá použít rovnou i
.
real 0m6.251s user 0m5.835s sys 0m0.236sS přepsáním na tuple a pypy:
real 0m1.753s user 0m1.243s sys 0m0.270s
Pak je ten jazyk jen v roli lepidla a skutečnou práci dělá někdo jiný – to můžeš udělat prakticky s každým jazykem.
Mně osobně kolikrát více zajímá výkon aplikace v daném jazyce napsané než výkon samotného runtime, zvlášťě v případě jazyků jako je Python, kde bývá dobrým zvykem náročné věci delegovat nepythonímu kódu.Ono to ale koreluje, delegace nedelegace. Asi to není úplně relevantní porovnání třeba pro server, nicméně porovnával jsem onehdá torrent klienty, nejvíc se mi líbily Deluge a qBittorrent. Volba padla na qBittorrent, protože Deluge je v pythonu a přestože torrent implementace je deledogávna do C++ (libtorrent-rasterbar) a GUI je delegováno do C (GTK), tak oproti qBittorrentu žere víc paměti a je znatelně pomalejší.
Jenže to znamená, že kromě Pythonu musíš používat i další jazyk (C), ve kterém svoje algoritmy implementuješ.
Zda je potřeba psát kód v C nebo použít kód již napsaný, záleží na tom, co děláš.
Pokud to jsou typické úlohy jako šifrování, komprese atd. tak tam jistě už napsaný bude. Ale pokud potřebuješ zpracovat nějaká svoje data pomocí svého algoritmu, tak tam si to musíš napsat sám. Případně to můžeš zkonvertovat na struktury, kterým rozumí nějaká knihovna, ale tam zase zdržuje ten překlad a ten algoritmus bude nějaký obecný, něco mu chybí nebo naopak přebývá oproti tomu, co potřebuješ.
Zda ten kód bude psát stejný člověk, záleží na projektu.
I v dobře fungujícím týmu má komunikace mezi lidmi nezanedbatelnou režii. Někdy ty výhody rozdělení na různé technologie převáží, ale ty náklady tam jsou vždycky.
Nepoužíváš jen tak nějaký další jazyk, ale jazyk C, což by pro Pythonistu nemělo být nic neočkávaného.
Měl jsem za to, že vysokoúrovňové jazyky používáme proto, abychom nemuseli řešit ty nízkoúrovňové věci a ztrácet s nimi čas (peníze). V Javě taky můžeš zabudovat do programu kód v C/C++, ale nikdy by mě nenapadlo tvrdit, že by javista měl umět C nebo C++.
A to se tam céčkové funkce volají fakt jednoduše – příklad volání libc
:
POSIX posix = (POSIX) Native.loadLibrary("c", POSIX.class);
Kde POSIX
je javovské rozhraní, které si napíšeš a ve kterém si deklaruješ funkce z té nativní knihovny, se kterými chceš pracovat:
public int chdir(String filename); public int chmod(String filename, int mode); public int chown(String filename, int user, int group); public int rename(String oldpath, String newpath); public int kill(int pid, int signal); public int link(String oldpath, String newpath); public int mkdir(String path, int mode); public int rmdir(String path);
typedef void* Foo;
int foo_create(Foo* result);
int foo_bar(Foo f, int param);
void foo_destroy(Foo f);
?
V Javě taky můžeš zabudovat do programu kód v C/C++, ale nikdy by mě nenapadlo tvrdit, že by javista měl umět C nebo C++.Vztah Pythonu a C je mnohem bližší, než je tomu v Javě. U Pythonu je C v podstatě součást ekosystému.
Nevím, jestli je to zrovna výhoda. U Javy by to tak klidně taky mohlo být, technicky tomu nic nebrání.
Blbá metóda merania. U mňa je rýchlejší aj bez optimalizácie (po zahriatí JIT). PyPy je určený pre dlho bežiace služby podobne ako java -server, porovnávať cez time je blbosť. V8 je naopak optimalizovaný pre klienta (tj. čo najrýchlejšie preložiť aj keď s dlhodobo horším výsledkom). S drobnou optimalizáciou (čísla prepísané na float point čo je v js štandard) má u mňa po zahriatí 4.5x vyšší výkon než V8 a s vynechaním zbytočnej premennej 9.5x vyšší vykon.
real 0m1.279s
user 0m1.130s
sys 0m0.151s
#!/usr/bin/perl my $width = 1600; my $height = 1200; my $nwidth = $width * 2; my $nheight = $height * 2; my @data; my @newdata; my @someval = (0, 0, 0, 0.4, 1, 1.1, 8); my $it = 0; my $size = width * height; for (my $i = 0; $i < $size; $i++) { push @data, $someval[($it++ % 7)]; } for (my $y = 0; $y < $height-1; $y++) { for (my $x = 0; $x < $width - 1; $x++) { my $nx = $x*2; my $ny = $y*2; $newdata[$ny*$nwidth + $nx] = $data[$y*$width + $x]; $newdata[$ny*$nwidth + $nx + 1] = ($data[$y*$width + $x] + $data[$y*$width + $x + 1]) / 2.0; $newdata[($ny+1)*$nwidth + $nx] = ($data[$y*$width + $x] + $data[($y+1)*$width + $x]) / 2.0; $newdata[($ny+1)*$nwidth + $nx + 1] = ($data[$y*$width + $x] + $data[$y*$width + $x + 1] + $data[($y+1)*$width + $x] + $data[($y+1)*$width + $x + 1]) / 4.0; } }
real 0m5.341s
user 0m5.104s
sys 0m0.236s
#push @data, $someval[($it++ % 7)]; $data[$i]=$someval[($it++ % 7)];
pro praci s polem v py bych pouzil Numpy/SciPy
Porovnávať implementáciu s JIT s implementáciou bez JIT je trochu nefér. Pre porovnanie python 2.7 mal u mňa 10.2166445971s a PyPy s JIT 2.02654728889s teda beží približne 5x rýchlejšie. Ak to prerátam na tento benchmark vychádza mi výkon pypy a node.js zhruba rovnaký. (pri benchmarkoch pozor, pypy má pomerne dlhú dobu zahrievania, čas som meral cez timeit po 5 iteráciách na prázdno, reálne pri webových aplikáciách čas zahrievania nie je rozhodujúci, služba beží pomerne dlho).
Mimochodom čas sa u mňa zníži na 0.554312205315 ak zmením riadok
newdata = [None] * (nwidth * nheight)
na
newdata = [0.0] * (nwidth * nheight)
Celý kód optimalizovaný pre pypy vyzerá takto:
# -*- coding: utf-8 -*- from __future__ import unicode_literals import timeit def fun(): width = 1600 height = 1200 nwidth = width * 2 nheight = height * 2 newdata = [0.0] * (nwidth * nheight) someval = (0.0, 0.0, 0.0, 0.4, 1.0, 1.1, 8.0) data = [ someval[it % 7] for it, i in enumerate(range(width*height)) ] for y in range(0, height-1): for x in range(0, width - 1): nx = x*2 ny = y*2 newdata[ny*nwidth + nx] = data[y*width + x] newdata[ny*nwidth + nx + 1] = (data[y*width + x] + data[y*width + x + 1]) / 2. newdata[(ny+1)*nwidth + nx] = (data[y*width + x] + data[(y+1)*width + x]) / 2. newdata[(ny+1)*nwidth + nx + 1] = (data[y*width + x] + data[y*width + x + 1] + data[(y+1)*width + x] + data[(y+1)*width + x + 1]) / 4. def main(): for i in range(5): fun() print(timeit.timeit('fun()', setup='from __main__ import fun', number=10)) if __name__ == '__main__': main()
Ešte doplním prepočet (za predpokladu že pomer výkonu bude približne rovnaký oproti mojej vykopávke): 5.899 / (10.216645 / 0.55431221) = approx. 0.32005496s.
Vynechaním zbytočnej nepoužitej premennej i dokonca vychádza čas 0.26289129s čo je v prepočte 5.899 / (10.216645 / 0.26289129) = approx. 0.1517911s teda čas veľmi blízky neoptimalizovanému C čo je na dynamický interpretovaný jazyk veľmi dobrý čas.
Ešte pre vysvetlenie mojich úprav:
tuple / list majú rovnakú zložitosť, výmena listov za tuple neznamená žiadne zrýchlenieZajímavé. Já osobně ve vlastním kódu používám všude listy, protože mi prakticky nikdy nezáleží na rychlosti. Všichni mi ale vždy tvrdili, že bych měl používat tuple, protože je to rychlejší. V tomhle konkrétním kódu jsem to zkoušel měřit a dělalo to cca 100ms. Ale je možné, že za to mohl ten generátor.
Ja som skúšal zmeniť someval na tuple a výsledky boli rovnaké v rámci intervalu spoľahlivosti. Pri zmene data na tuple sa čas mierne zvýšil (čo je zrejme dôsledok použitia generátora, s list comprehension od začiatku pozná veľkosť výsledného listu, takže sa môže alokovať naraz).
Ja som čas vždy delil desiatimi, vo výsledkoch som uvádzal čas jednej iterácie. Asi som to mal spomenúť ;)
real 0m0.010s
user 0m0.008s
sys 0m0.000s
real 0m1.319s user 0m1.248s sys 0m0.073sna:
real 0m0.526s user 0m0.479s sys 0m0.046s
#define WIDTH 16000
#define HEIGHT 1200
#define NWIDTH (2 * WIDTH)
#define NHEIGHT (2 * HEIGHT)
double data[WIDTH * HEIGHT];
double newdata[NWIDTH * NHEIGHT];
double someval[] = {0, 0, 0, 0.4, 1, 1.1, 8};
int main(int argc, char *argv[]) {
int it = 0;
for (int i = 0; i < WIDTH*HEIGHT; i++, it++) {
data[i] = someval[it % 7];
}
double* col = data;
double* col_next = col + WIDTH;
double* ncol = newdata;
double* ncol_next = newdata + NWIDTH;
for (int y = 0; y < HEIGHT-1; y++) {
for (int x = 0, nx = 0; x < WIDTH - 1; x++, nx += 2) {
ncol[nx] = col[x];
ncol[nx + 1] = (col[x] + col[x]) / 2.;
ncol_next[nx] = (col[x] + col_next[x]) / 2.;
ncol_next[nx + 1] = (col[x] + col[x + 1] + col_next[x] + col_next[x + 1]) / 4.;
}
col = col_next;
col_next += WIDTH;
ncol = ncol_next;
ncol_next += NWIDTH;
}
return 0;
}
U mě se doba snížila z ~0.9s na ~0.67s (zvětšil jsem rozměl o řád, aby se to vůbec dalo měřit). Doufám, že to mám dobře col
, když je to řádka. A je tam špatně jeden ten index. Takže oprava:
#define WIDTH 16000
#define HEIGHT 1200
#define NWIDTH (2 * WIDTH)
#define NHEIGHT (2 * HEIGHT)
double data[WIDTH * HEIGHT];
double newdata[NWIDTH * NHEIGHT];
double someval[] = {0, 0, 0, 0.4, 1, 1.1, 8};
int main(int argc, char *argv[]) {
int it = 0;
for (int i = 0; i < WIDTH*HEIGHT; i++, it++) {
data[i] = someval[it % 7];
}
double* line = data;
double* line_next = line + WIDTH;
double* nline = newdata;
double* nline_next = newdata + NWIDTH;
for (int y = 0; y < HEIGHT-1; y++) {
for (int x = 0, nx = 0; x < WIDTH - 1; x++, nx += 2) {
nline[nx] = line[x];
nline[nx + 1] = (line[x] + line[x + 1]) / 2.;
nline_next[nx] = (line[x] + line_next[x]) / 2.;
nline_next[nx + 1] = (line[x] + line[x + 1] + line_next[x] + line_next[x + 1]) / 4.;
}
line = line_next;
line_next += WIDTH;
nline = nline_next;
nline_next += NWIDTH;
}
return 0;
}
Zkusil jsem to přepsat do Rustu, a běží to ještě o asi 30% rychlejc:
const WIDTH: usize = 16000;
const HEIGHT: usize = 1200;
const SIZE: usize = WIDTH * HEIGHT;
const NWIDTH: usize = 2 * WIDTH;
const NHEIGHT: usize = 2 * HEIGHT;
const NSIZE: usize = NWIDTH * NHEIGHT;
fn main() {
let someval = [0.0f64, 0.0, 0.0, 0.4, 1.0, 1.1, 8.0];
let data = someval.iter().cycle().take(SIZE).map(|&x|{x}).collect::<Vec<f64>>();
let mut newdata = Vec::<f64>::with_capacity(NSIZE);
unsafe { newdata.set_len(NSIZE); }
{
let mut lines = data.chunks(WIDTH);
let mut lines_next = data.chunks(WIDTH).skip(1);
let mut nlines = newdata.chunks_mut(NWIDTH);
for (line, line_next) in lines.zip(lines_next) {
let mut nline = nlines.next().unwrap();
let mut nline_next = nlines.next().unwrap();
let mut nx = 0;
for x in 0..WIDTH-1 {
nline[x] = line[x];
nline[x + 1] = (line[x] + line[x + 1]) / 2.0;
nline_next[nx] = (line[x] + line_next[x]) / 2.0;
nline_next[nx + 1] = (line[x] + line[x + 1] + line_next[x] + line_next[x + 1]) / 4.0;
nx += 2;
}
}
}
}
Buď jsem někde udělal chybu, nebo je Rust fakt hustej, vzhledem k tomu, že tam jsou high-level fíčury jako iterátory apod.
Buď jsem někde udělal chybu, nebo je Rust fakt hustej, vzhledem k tomu, že tam jsou high-level fíčury jako iterátory apod.Hm, tak A) je správně
V C kódu musíš ukazatele nline
a nline_next
v každém y
cyklu posunout o 2*NWIDTH
, jinak si přepisuješ data a plníš jen polovinu výstupního bloku.
Pokud není modulo mocninou dvou, tak se jedná o dělení a to i na současných super-superscalárních CPU (6 dokončených instrukcí za hodinový cyklus) mívá buď jen jednu nebo dvě funkční jednotky a latenci okolo 6 až 12 hodinových taktů - za tu dobu lze zvládnout i 50 jiných instrukcí. Na to, jak je to s propustností dělení bych se musel pro konkrétní CPU podívat do třetího a čtvrtého manuálu z
http://www.agner.org/optimize/nebo originální dokumentace od Intelu.
Pokud se bavíme o modulo v "it % 7" tak 8 býval právě limit pro globální pattern based predikci skoků na x86. Takže pro 2 bit prediktor v BHT sice bude 1 chybná predikce každých sedm cyklů (to je cena tak 50 až 100 instrukcí navíc), ale rychle se na to přijde a pro skok na dan0 adrese se přejde na ten pattern a ten již bude predikovat vše správně.
Pokud by to mělo být v C rychlé, tak bych dal zvlášť iterátor od 0 do 6, nazvaný třeba "it7" a zkusil kód přepsat jako
it7 = it7 >= 7? 0 : it7 + 1;
Pokud bude kompilátor chytrý tak jak pro tento zápis tak pro řešení s if dokáže na x86 šikovně použít instrukci SETcc a AND. Na ARM pak podmínění instrukce nebo ITE pro Thumb. I pokud to kompilátor vymyslet nezvládne, tak mu lze pomoci obecným kódem
it7 = (it7 + 1) & ((int32_t)(it7 - 7) >> 31);
Na druhou stranu, pokud dáte vyšší stupeň optimalizace, tak to může být kontraproduktivní, protože pro pro "if" dokáže GCC vnitřních sedm opakování rozložit (loop unroling a GCC Graphite+GIMPLE) na sedm přiřazení za sebou a pokud to nevychází přesně nebo je limit proměnný, tak poslední průchod nechá zvlášť.
Pokud by se GIMPLE nedokázal na if nebo ternární operátor chytit, tak mu lze pomoc tím, že se rovnou délka vnějšího cyklu vydělí sedmi a napíše se vnitřní cyklus nebo se tam dá těch sedm přiřazení rozbalených ručně. Pokud na konci něco zbývá, tak tu část posledního cyklu pak projet v kopii kódu postupně sekvenčně a to již musí správně vzít i nejhloupější C kompilátor.
Obětovat čas na zkoušení konkrétního příkladu se mi věnovat nechce a stejně by mi vyšla čísla řádově jinde. Ale pokud někdo zkusí moje návrhy, tak mě porovnání potěší.
I pokud to kompilátor vymyslet nezvládne, tak mu lze pomoci obecným kódem
it7 = (it7 + 1) & ((int32_t)(it7 - 7) >> 31);
To je chytrý trik. IMHO tam ale mělo být -6. Trochu se mi na tom nelíbí ten signed right shift. Jinak tohle je u mě s GCC -O2 rychlejší než ten if. Při pohledu do -S se v té verzi s if žádný velký loop unrolling neděje, používá CMOV. Graphite nevím jak se aktivuje.
To, že kompilátor může na x86 použít CMOV mě nenapadlo - instrukční sadě jsem si jí do teď nevšiml. Počítal jsem, že se stejného efektu docílí SETcc a AND.
V každém případě díky za vyzkoušení, že CMOV vyjde pomalejší bych nečekal. Procesor asi neumí použít ekvivalent kompletního register renaming pro příznakový registr. Pro čisté operace nad daty dokáže nejspíš přednačítat a rozpracovat několik smyček paralelně (s překrýváním v pipeline). S CMOV musí před dalším přístupem do paměti podle it7 asi čekat na pozdější pipeline stage pro dokončení CMOV. Ale to jen vařím z vody.
Co se týče operací nad signed čísly, tak shift doprava by měl být i podle striktní normy v pořádku. Jako vždy ovšem platí, že musí být o méně bitů než reprezentace. Problém může být ten následující AND, to je implementation specific, protože signed aritmetika nemusí pracovat v dvojkovém doplňku. Na druhou stranu, pokud se signed přetypuje na uint32_t tak tam by i podle striktní normy mělo platit, že se to provede do dvojkového doplňku. Pravda je, že i ten "it7" by pak měl být unsigned, ideálně uint_fast32_t. Ale zatím si nejsem vědom architektury (kromě nějakého Ruského trojkově založeného CPU), kde by to neplatilo.
Jinak obvykle větší problém než vlastní, takto jednoduchý, algoritmus bývají přístupy do paměti, výpadky cache a jejich zarovnání/nezarovnání. Při analýze průchodnosti paměti pak může velmi pomoc Valgrid/Cachegrind. Viz naše úloha pro studenty v rámci předmětu A0B36APO
https://cw.fel.cvut.cz/wiki/courses/a0b36apo/homeworks/02/startRozdíly v čase výkonu jsou podle implementace o jeden až dva řády. Na počet výpadků pak lze s promyšleným využitím SSE/SSE2/AVX jít ještě minimálně dvakrát lépe. Doladit na instrukce to lze bez Valgrindu v reálném běhu pomocí Linuxového perf, který přesně řekne, na které instrukci se kolik času čeká. Své nejlepší řešení úlohy zatím nepublikuji, aby jsme ji mohli použít i v příštím roce.
Co se týče operací nad signed čísly, tak shift doprava by měl být i podle striktní normy v pořádku. Jako vždy ovšem platí, že musí být o méně bitů než reprezentace.V případě signed integeru tam je ještě podmínka, že musí být >= 0. Right shift signed integeru < 0 je implementation-defined. Nicméně realisticky bych nečekal, že by nějaká implementace použila v tomhle případě unsigned shift. Zkoušel jsem ještě
it = (it + 1) & ((it >= 6) - 1);
Ale je to pomalejší, asi kvůli CMP.
Viz naše úloha pro studenty v rámci předmětu A0B36APO https://cw.fel.cvut.cz/wiki/courses/a0b36apo/homeworks/02/startTo vypadá zajímavě, to si možná ze srandy zkusím (a ten 4. úkol taky). Pro studenta mi to přijde dost obtížné (také vzhledem k času v semestru), ale podobný pocit jsem měl z většiny předmětů na FELu, takže to je možná můj problém...
Situace je tak špatnáTo je ale dobrá zpráva, jde-li o spolehlivost a bezpečnost – zvlášť, pokud to platí i pro C++.
Studenti programování (kterým se při lákání na školu říká, že jsou/budou nejlepšími 150 v republice (možná ve světě)) by opravdu nemělo dělat problém ve čtyřech až šesti předmětech napsat za semestr 5x prográmek do 300 řádek kódu.Je pravda, že tahle úloha asi není až tak obtížná. Já měl na mysli spíš ten závěrečnej test s dekódováním algoritmu z asm. Ale jestliže jsou studenti s asm seznámeni v semestru, tak by to asi taky nemuselo bejt tak hrozný. No ale stejně je potřeba počítat, že 6 předmětů × 5 prográmků = 30 a semestr má teoreticky 15, reálně spíš 13 týdnů. U mě to byl předmět PAL, který mě víceméně odradil od dalšího mgr studia na FEL. Přijde mi, že u tohohle předmětu se vyučující rozhodli přeskočit část "Nejdřív vás něco naučíme" a od začátku semestru přistoupili rovnou k fázi ".. a pak vás z toho vyzkoušíme"
Jinak zrovna v současné době nám přichází množství nářků z firem, že lidi schopné řešit HW, SW pro řízení a embedded/mobilní aplikace alespoň s minimální znalostí C chybí. Situace je tak špatná, že Siemens již má náborové reklamy na C vývojáře přímo ve vozech Pražského metra a od druhých vím, že již nábory raději mimo osobní doporučení nedělají, protože úroveň uchazečů je tristní. Přitom většina studentů se chce přímo slovy jednoho z nich "vznášet na vlnách vysokoúrovňových frameworků" a požadavky na porozumění podstatě programů považují za sprosté urážky.Tak to je dost děsnej výrok. Ale všiml jsem si toho trendu taky. Podle mě prostě nemají o low-level zájem / nemají motivaci se tím zabývat, asi většinou ani neví, proč by měli. Tohle je čistě moje spekulace: Minimálně z části to podle mě je vysokou vstupní investicí. Zvládnout cool frejmwork X ve snadnoZvládnutelnémJazyce Y (tj. např. Java) je snadné, protože adept si najde na internetu pár tutoriálů (dost možná i v češtině), stáhne si IDE a všechny nástroje podle instrukcí, IDE mu napoví první poslední, od pojmenování a strukturování souborů až po to, jak si poposednout na židli,... prostě všechno to dostane na stříbrném podnosu hotové k použití. Oproti tomu dostat se do C/C++ a příbuzných technologií je náročnější, protože existuje několik velmi různých kopmilátorů, všelijaká IDE, spousta různých knihoven, build systémy (nováček obvykle neví, co to je, že...) atd. Tj. zatímco u nějakého cool Java, C# nebo JavaScript frameworku ten první dojem je "Wow, tohle je kůl", u C/C++ je prvním dojmem naprostý zmatek. Což hodně lidí odradí. Moje první pokusy v C++ jsem, jestli se dobře vzpomínám, realizoval v Borland C++ Builder, protože do té doby jsem dělal jen v Delphi ten Borland se jevil jako taková snadno uchopitelná technologie v celém tom zmatku.
Tohle je čistě moje spekulace: Minimálně z části to podle mě je vysokou vstupní investicí. Zvládnout cool frejmwork X ve snadnoZvládnutelnémJazyce Y (tj. např. Java) je snadné, protože adept si najde na internetu pár tutoriálů (dost možná i v češtině), stáhne si IDE a všechny nástroje podle instrukcí, IDE mu napoví první poslední, od pojmenování a strukturování souborů až po to, jak si poposednout na židli,... prostě všechno to dostane na stříbrném podnosu hotové k použití.
Na tom je zajímavé, že dobrých javistů je stále nedostatek. Přitom podle podobných komentářů by to mohl dělat i nedostudovaný popelář. Takže buď je extrémní nerovnováha mezi nabídkou a poptávkou1 nebo to zase tak jednoduché není.
nebo JavaScript frameworku ten první dojem je "Wow, tohle je kůl"
Typické pro frikulínské jazyky jako JavaScript, Python, Ruby atd. Program si při kompilaci postahuje půlku Internetu2 a demo stažené ze stránek projektu vypadá fakt hustě a zdá se, že vyřeší všechny tvoje problémy a budeš s tím jazykem/frameworkem sekat aplikace jako Baťa cvičky. Konfrontace s realitou bývá horší, natož pak nějaké dlouhodobé udržování aplikace.
Oproti tomu je C/C++ ještě ráj – je to poměrně konservativní prostředí, roky ověřené postupy. Závislosti na cizích knihovnách se jakž takž daří držet na uzdě. Akorát ten jazyk jako takový je dost komplikovaný a trochu procházka minovým polem (i když to je u dynamických jazyků taky, akorát z jiného důvodu).
u C/C++ je prvním dojmem naprostý zmatek
U C++ jsem byl asi nejvíc zklamaný, že si prakticky nejde vystačit jen se standardní knihovnou (ani pro jednoduché aplikace). Jinak celkem dobré a v něčem i lepší než ta Java.
[1] což by se ale mělo už dávno vyrovnat, protože Javu se dneska na škole učí prakticky každý
[2] desítky až stovky knihovnen neznámého původu, které zrovna frčí nebo na ně autor náhodou narazil na GitHubu, ale za půl roku už možná nebudou existovat nebo se jejich autoři budou věnovat něčemu jinému
Na tom je zajímavé, že dobrých javistů je stále nedostatek.To mě vůbec nepřekvapuje. Sjet pár tutoriálů je jedna věc, psát dobré aplikace o dost jiná. Výuka ve škole taky nic nezaručuje...
Typické pro frikulínské jazyky jako JavaScript, Python, Ruby atd. Program si při kompilaci postahuje půlku Internetu2Tohle mě vysíralo např. u Jekyllu, což je jinak výborná aplikace, ale ty dependence... Nicméně jsem se s tim ale setkal i v Javě a koneckonců i jazyky jako Rust a Go mají stahovače dependencí. Ono to IMHO v zásadě není špatná věc, akorát lidi to zneužívaj...
Osobne si myslím, že vždy lepšie dotiahnuť zopár závislosti než mať na krku takú hrúzu ako je napr. abclinuxu.
Je to nešťastně navržené (např. ta hromada konstant a následných IFů), ale to moc nesouvisí s tím, jestli si do programu nataháš nesmyslné množství externích knihoven.
Prečo je to nešťastne navrhnuté?
Ja programujem prevažne v Djangu. V kóde abclinuxu som videl v každom súbore test na maintenance. V djangu by som napísal pip install django-maintenance, pridal 1 riadok do middleware a je to. Keby som používal framework bez podpory validácie formulárov doinštaloval by som podporu zase jediným príkazom. Keď potrebujem stránkovanie doinštalujem ho, keď potrebujem markdown doinštalujem, keď potrebujem farebné výpisy v termináli doinštalujem balík sk s podporou rozpoznania blbého terminálu / presmerovania. PIP je na to aby som nerobil copy & paste kódu ako v abclinuxu.
Balíčky sú v centralizovanom repozitári, napr. MySQL-python je jeden zo starších balíčkov (prvý krát pridaný v roku 2003, stále dostupný po vyše 10 rokoch). Staré verzie sa neodstraňujú, takže nemal by byť problém o 10 rokov nainštalovať dokonca tie isté verzie.
(Tím naznačuji, že může být rozumné držet závislosti v repozitáři společně se zdrojovým kódem.)
Priamo určite nie. Ako submoduly hostované na vlastnom servery beriem, ale kopírovať kód ktorý nemôžem centralizovane upgradnúť ak sa nájde bezpečnostná diera alebo nejaká iná nepríjemná chyba sa mi zdá blbé.
ale kopírovať kód ktorý nemôžem centralizovane upgradnúť ak sa nájde bezpečnostná diera alebo nejaká iná nepríjemná chyba sa mi zdá blbé.Co když váš kód (nebo kód vašich zákazníků) závisí na té chybě a nebude bez ní fungovat? IMO nový kód by měl projít revizí, abyste si do vašich programů nezanesl chyby.
Ako submoduly hostované na vlastnom servery beriemOsobně nevidím důvod, proč používat submoduly, pokud mohu mít vše v jednom repozitáři (tj. pokud všichni programátoři mají přístup ke všem částem kódu). Přijde mi, že se submoduly je akorát víc práce. Jsou firmy, které mají vše v jednom repozitáři (to samozřejmě nedokazuje, že je to dobrý nápad). Například Facebook.
Nikdy sa mi nestalo, že by som záplatou rozbil projekt, ale často krát sa mi stáva, že potrebujem ten istý kód updatnúť na 10 projektoch. Nehovorím, že moje riešenie je jediné správne, ale ak vychádzam zo svojich skúseností tak by som to kopírovaním niekoľko desiatok MB do každého projektu neriešil.
Nikdy sa mi nestalo, že by som záplatou rozbil projekt, ale často krát sa mi stáva, že potrebujem ten istý kód updatnúť na 10 projektoch.
K tomu je dobrý Maven:
hg pull -u
)mvn clean install
) a výsledný artefakt se uloží do tvého ~/.m2
Ještě lepší samozřejmě je používat balíčky distribuce, než takhle prasit pomocí jakýchsi dalších „balíčkovacích“ systémů (ty jsou určeny pro vývojáře, ne pro provoz).
$HOME
plný nějakých magickýchj adresářů s kupou bordelu. Kdyby to aspoň dávali do .cache
.
Osobně nevidím důvod, proč používat submoduly, pokud mohu mít vše v jednom repozitáři (tj. pokud všichni programátoři mají přístup ke všem částem kódu). Přijde mi, že se submoduly je akorát víc práce.
Souhlasím, že je nutné mít všechny závislosti stažené u sebe. Ale přijde mi vhodnější je mít v samostatných úložištích – nejlepší je si udělat klon od autorů knihovny (a ti mohou používat jiný verzovací systém, než ty). A měl bys být schopný si je sám zkompilovat. Dobře řešitelné je to v Mavenu, kde se můžeš přepnout do offline režimu, závislosti si zkompilovat z vlastních klonů a pak si je nainstalovat do svého ~/.m2
, kde si je najde tvůj program.
Není moc rozumné to celé commitnout do jednoho repositáře, neboť v tom pak je docela bordelProč myslíte, že v tom je bordel? Právě mi přijde, že je to naopak. Když to mám v jednom repozitáři a ten pullnu, tak mám všechno aktualizované ve správné verzi. Navíc může platit jedna featura = jedna větev a při jejím zmergování třeba i jeden commit, což IMO usnadňuje revize.
Bordel v tom je keď napr. projekt portujem na vyššiu verziu knižnice. Vtedy sa tam musí skopírovať novšia verzia (veľký commit) a občas aj upraviť nejaký ten kód, ktorý s knižnicou pracuje. Hrabať sa potom v diffoch keď potrebujem nájsť nejakú konkrétnu zmenu je hnusné. So submodulmi by sa mi zmenil jeden hash a zvyšok by boli zmeny súvisiace s projektom.
Mít jeden velký diff mi přijde výhodné pro revidování
He?
z knihoven snažím odstranit nepoužité soubory
He?
Mít jeden velký diff mi přijde výhodné pro revidováníPro projekt to je jedna změna, tudíž je vhodné ji revidovat vcelku.
z knihoven snažím odstranit nepoužité souboryMám na mysli odstranění souborů, jenž obsahují kód, který nepoužívám. Snižuje to práci při revizi. Pokud ten kód navíc předáváte zákazníkům a ti ho také revidují, tak se ušetří několikrát.
Pro projekt to je jedna změna, tudíž je vhodné ji revidovat vcelku.
Revidovať celú cudziu knižnicu? Často aj niekoľko MB kódu? Neviem, osobne mám radšej v diffe niečo také:
-Subproject commit 035b6ca862da3bba0ab8aad388a485758311a464 +Subproject commit b4627a810604d8c082374ee88a6494b928491ba6
Zvyšné zmeny ktoré vidím v diffe súvisia už len so samotným produktom, diffy externých knižníc si môžem pozrieť v submoduloch, ale pri hľadaní toho kde som mohol niečo rozbiť ma externá knižnica zaujíma ako monolit.
Mám na mysli odstranění souborů, jenž obsahují kód, který nepoužívám.
To som robil raz v živote keď som staval embedded systém. Vlastne som ani nič neodstraňoval, projekt bol ako submodul a pri builde obrazu systému sa v dočasnom adresári vyhodili nepoužívané súbory / adresáre. V repozitári bol len skript na vyčistenie od nepoužívaných vecí. Neviem si predstaviť z veľkej knižnice vyhadzovať znovu kód pri každom update knižnice.
Revidovať celú cudziu knižnicu?Je to ve vlastním zájmu. Minimálně z bezpečnostního hlediska a z právního hlediska (co když knihovna obsahuje například kradený kód nebo nějaký soubor obsahuje nevhodnou licenci). Pak tu je ještě hledisko funkčnosti.
Často aj niekoľko MB kódu?Některé firmy si pro to vytvořili speciální nástroje – třeba fe (viz Iron out your release process).
Je to ve vlastním zájmu. Minimálně z bezpečnostního hlediska a z právního hlediska
Tak na toto resource vážne nemáme. Vlastne neviem si predstaviť žiadnu tunajšiu firmu, ktorá by si mohla dovoliť revidovať glibc, openssl, gnutls ... Existuje nejaká česká / slovenská firma ktorá si to môže dovoliť?
Existuje nejaká česká / slovenská firma ktorá si to môže dovoliť?U standardních a široce používaných součástí OS se můžete spolehnout na komunitu. U ostatních knihoven/nástrojů potřebujete dělat revize.
+1
Tady platí nepřímá úměra: čím je knihovna méně známá/používaná/zralá, tím víc je nutná revize jejího kódu.
Otázkou je, jak takovou aplikaci spustíte třeba za 10 let, kdy už ty balíčky nemusí být dostupné?To je otázka u v podstatě jakékoli aplikace. 10 let je dlouhá doba.
To je otázka u v podstatě jakékoli aplikace.Když je aplikace dobře napsaná, její závislosti byly šikovně vybrány a jazyk + standardní knihovny stále existují a drží zpětnou kompatibilitu, tak by to neměl být problém.
V praxi bych to "by to neměl být problém" očekával jen u aplikací, které jsou vyloženě napsány se záměrem takhle dlouhé funkčnosti.10 let není zas tak dlouhá doba. Například v Javě napíšete hladce aplikace, jenž budou fungovat déle. Samozřejmě, když si vyberete ekosystém, kde se na zpětnou kompatibilitu tolik nehraje (třeba Python), tak můžete mít problémy. Když tedy vybíráte programovací jazyk nebo knihovnu, tak musíte zohledit očekávanou délku a kvalitu podpory, případně to, zda ji dokážete podporovat sám. (Například u F# některé firmy přecenily kvalitu podpory, podobné problémy však mohou nastat i se Scalou nebo s jiným méně rozšířeným jazykem. Jiné firmy naopak mají lidi, co rozumí kompilátoru a tyto chyby umí opravit.)
V kóde abclinuxu som videl v každom súbore test na maintenance.
To je právě ten špatný návrh – v Javě ti stačí jeden servletový filtr a máš to na jednom místě. Nebo ještě lépe: řešit to na úrovni HTTP serveru (ten na to v pohodě stačí – pak můžeš odstavit/restartovat celý aplikační server, protože ta „maintenance“ stránka se nevykresluje pomocí něj).
Problémy knihoven/závislostí zde vidím hlavně v tomhle:
Doporučuji jednoduchý test:
Výsledek je jedním ze znaků vyspělosti firmy/týmu/autora.
Výsledek je jedním ze znaků vyspělosti firmy/týmu/autora.
Len pre zaujímavosť bez internetu človek nenainštaluje ani len javu.
Neviem ako iné firmy, my máme dáta inde než pracujeme, takže nie bez internetu projekt nenainštalujem už len preto, že väčšina projektov závisí od interných balíkov. Externé závislosti .. no ak mám tar.gz tak nie je problém nainštalovať. V podstate videl som medzi balíkmi systému aj samotné django, väčšina dependency, ktoré používam sú kvôli ladeniu a na produkčnom sa nepoužívajú (werkzeug, django-extensions, django-debug-toolbar).
Interné balíky majú väčšinou pár riadkov. Máme tu napr. stránkovanie (zverejnené na githube), generovanie slugov, maintenance s voliteľným progressbarom (robil som aj na databázach kde trvalo prečistenie pár hodín, vtedy radšej dám návštevníkom progressbar s približným časom dokončenia) ...
Spoločné znovupoužiteľné moduly mám v samostatných repozitároch. Keď mám pocit, že API je stabilné zverejňujem kód aj na github.
Je pravda, že nerobíme veľké projekty. S dokopy 2 ľuďmi z čoho jeden má na starosti komunikáciu so zákazníkmi a tend druhý som ja to nie je také jekdonduché. Rozsah projektov zatiaľ jeden deň až mesiac (aj s nastrihaním, to som musel donedávna roibť ja, momentálne sa to už rieši externe). Zatiaľ asi najväčší dokončený projekt je toto. Časovo to bol asi mesiac roboty (upozorňujem, že v tom čase som musel aj strihať, aj písať javascripty na čom som zabil väčšinu času).
Len pre zaujímavosť bez internetu človek nenainštaluje ani len javu.
Proč ne?
apt install openjdk-8-jdk
z lokálního obrazu nebo zrcadla. Případně si postahuješ a uložíš jednotlivé balíčky.
A platí tu podobná nepřímá úměra jako u těch revizí. Dá se předpokládat, že zdroje hodně používaných distribucí (Debian, Ubuntu, Fedora, OpenSUSE atd.) se jen tak neztratí a člověk vždycky to .iso nebo on-line zrcadlo sežene, takže není až tak nutné je syslit u sebe. Zatímco u méně známých/používaných programů a knihoven člověk takovou jistotu nemá a ty můžou zmizet prakticky ze dne na den – proto je potřeba je mít zálohované u sebe.
Ideálně bys měl mít u sebe i zrcadlo distribuce nebo aspoň proxy s pamětí, přes kterou stahuješ balíčky. Chápu, že firma o dvou lidech tohle asi dělat nebude, ale takovou nezdravou závislostí na okolí trpí často i mnohem větší firmy.
ale takovou nezdravou závislostí na okolí trpí často i mnohem větší firmy
Lenže čo je to nezdravá závislosť? Ja dokážem všetky balíky nainštalovať offline, stačí skopírovať do lokálneho repozitára alebo nastaviť zdroj niekde v lokálnej sieti.
Pre zaujímavosť v poslednom projekte mám tieto závislosti:
Django # webový framework Pillow # najpoužívanejšia python knižnica na prácu s obrázkami django-allauth # autentifikácia na sociálne kraviny django-braces # LoginRequiredMixin, PermissionRequiredMixin ... proste drobné utilitky django-compressor # spolu s django-libsas na kompiláciu scss (áno rád si na jednom mieste definujem farby) django-libsass pytz # časové zóny requests # spolu s requests-oauthlib prístup na oauth api requests-oauthlib sockjs-tornado # websocket protokol tornado # webserver
Všetky projekty sú hostované priamo cez repozitár pypi (nie nebojím sa, že by po 10 rokoch nefungoval, dostupné sú všetky verzie).
Ideálně bys měl mít u sebe i zrcadlo distribuce nebo aspoň proxy s pamětí, přes kterou stahuješ balíčkyTo je docela dobrá připomínka. Navíc takovou proxy není nijak těžké nastavit. Viz balíčky apt-cacher a apt-mirror. Jakmile někdo balíček použije, zůstane v cache a bude snadné se k němu kdykoliv vrátit. A protože s takovým balíčkem nainstaluje i jeho závisloti, je cache úplná a závislost se omezuje na architekturu procesoru, pro kterou to bylo zkompilované, v horším případě na její emulátor.
No ale stejně je potřeba počítat, že 6 předmětů × 5 prográmků = 30 a semestr má teoreticky 15, reálně spíš 13 týdnů.A z toho je první týden organizační, poslední zápočtový, občas je cviko před přednáškou, takže druhý týden neví co by. Takže z 13 je 10, pokud se tam nepřiplete nějaký svátek. A než se probere aspoň nějaká užitečná část teorie, půl semestru je pryč.
Ale všiml jsem si toho trendu taky. Podle mě prostě nemají o low-level zájema nebo nemaji zajem myslet? A je to trend nebo je to tim, ze IT dneska studuje nasobne vic lidi nez pred par 10-15 lety (pricemz procento lidi v populacnim rocniku ktere je ochono myslet je IMHO porad +/- stejne)? A meli vubec studenti pred temi 10-15 lety sanci se vznaset na vlnach vysokourovnovych frameworku nebo jim casto nic jineho nez ten low-level nezbyvalo?
Myslím, že v tomto se doba až tak úplně nezměnila.IMHO se velmi zasadne zmenila a to v to zejmena pomer tech lidi, co programovat umi a premysli nad tim a tech, kdo programovat neumi nebo nad tim nepremysli (a casto oboje zaroven). (nebo jen jen otupel - driv jsem se za svuj kod stydel, ted smele posilam pull requesty, protoze vim, ze 90% lidi pise stejny sracky jako ja
v C stravite pul dne hledanim +1 chybyChápu to tedy správně, že C kultivuje programátora a vede k přemýšlení nad kódem a menší chybovostí? Jako začátečník jsem měl off by one každou chvíli, ale teď si jaksi nemůžu vzpomenout, kdy přesně naposledy.
Lol, tahle diskuse ukazuje ten zasadni rozdil mezi psanim v C a prakticky cimkoliv ostatnim. Zatimco v pythonu, javascriptu nebo v C++ STL si sednete a ten kod stvorite behem nekolika minut, v C stravite pul dne hledanim +1 chyby -- ale bude to ve vysledku pocitat straaasne rychle.Obávám se, že jsi diskusi těžce nepochopil. To zrychlení jsem tam hledal víceméně jen ze srandy a ze zvědavosti, co je v dané situaci nejrychlejší. Nepotřebuju to ale v praxi vůbec na nic a při psaní programu bych to na 99% nedělal (leda by to bylo potřeba v nějaké specifické situaci). V podstatě si tady jen tak hrajem. A s jazykem C to už vůbec nemá nic společného, 1) jazyk C tě k hledání těhlech optimalizací nijak nenutí a 2) podobné mikrooptimalizace se dají hledat v prakticky jakémkoli jazyce včetně skriptovacích, stačí si do StackOverflow zadat "how to [something] fast in [language]", viz např. diskusi tady a nespočet podobných...
Takze trvam na svem: ta diskuse (i kdyz je to hrani) je klasickym pripadem toho, proc nepouzivat C u vetsiny projektu.Nechápu, jak jsi k tomu závěru došel, ale dobře, příště teda radši napíšu modul do kernelu v JavaScriptu, když vidím, jaké zlo to Céčko způsobuje...
"Chyba" Pythonu (i kdyz mozna min, nez Perlu) je, ze te nenuti programovat, ale klidne si vystacis se skriptovanim.Obávám se, že tyhle hlášky poněkud vyšly z módy a i tehdy sloužily výhradně ke kompenzaci nízkého sebevědomí.
Faktem je, ze uz je v C napsany a nikdo ho prepisovat nebude. I kdyz verim, ze to urcite nekolik Java ci C++ programatoru laka.Láká to docela hodně programátorů. Viděl jsem například nějaké snahy vytvořit v lispu transpiler C do lispu. Chtěli to tuším kvůli ovladačům, když chtěli psát vlastní OS.
Ten céčkovej kód se dá ještě zoptimalizovat tím, že se drží řádkový pointery ...Tenhle trik znám, využívá se toho, že procesory (minimálně ty x86) umí bez problémů přístup do pole přes index včetně offsetu v jediné instrukci, ale dva různé indexy se už musí počítat bokem. Tudíž pokud se adresuje dvourozměrné pole (i pokud je dělan jako jednorozměrné, ale s dvěma indexy), je lepší ho ve vnitřní smyčce převést na jednorozměrné, aby ho mohl překladač přímo adresovat. PS: Zkusil jsem jen tak pro zábavu udělat ještě další optimalizace (převážně použitím pointerové aritmetiky), ale nepovedlo se mi trumfnout optimalizace překladače - zatímco bez nich byla moje verze cca. o 10-15% rychlejší, s volbou
-O3
byl výsledek zcela srovnatelný (v rámci statistické chyby), což za na první pohled zcela nepochopitelnáý kód nestojí Některým pak vychází ukrutné hovadiny, jako superrychlost Javy apod.
Souhlasím, že ten test nejde na maximum možností daného jazyka/kompilátoru. Ale nějakou vypovídací hodnotu má – porovnává totiž výkon za jinak stejných okolností – tzn. stejně schopný programátor1 a stejná úroveň optimalizace, výchozí nastavení. A tam se ukázalo, že Java dosahuje lepších výsledků než JavaScript, Python, Perl. A předběhlo ji jen C.
Java má ještě nevýhodu v tom, že pomaleji startuje2, takže jak to bude škálovat a program poběží delší dobu, ten náskok Javy se ještě prohloubí, protože ten start jsou jen fixní náklady, ne variabilní.
A i s „optimalizacemi“3 je ten JavaScript pomalejší než naivní implementace v Javě.
[1] ty algoritmy jsou 1:1, okopírované, jen s upravenou syntaxí, aby to šlo přeložit
[2] Hello world v Javě trvá o něco déle než Hello world třeba v Pythonu
[3] JavaScriptař musí vědět, že má místo Array
použít Float32Array
A tam se ukázalo, že Java dosahuje lepších výsledků než JavaScript, Python, Perl. A předběhlo ji jen C.Na mém stroji to je C < Rust < Java ≅ JavaScript(s FloatArray). U Javy i JavaScrpitu počítám jen dobu běhu, tj. bez startu JVM/Node. Nastavil jsem 10× větší pole (tj. 16000×1200), protože jinak je běh IMHO příliš rychlý na měření. U Javy jsem musel nastavit větší heapsize, protože jinak odmítal alokovat. Java byla rychlejší o asi 1~2%.
To neodpovídá mým měřením. Když nastavím rozměr vstupních dat na 1×1 a měřím tedy jen start VM a zpracování minimálních dat, tak dostanu:
$ time nodejs test-Float32Array.js real 0m0.043s user 0m0.032s sys 0m0.012s
$ time java Test Čas: 0 ms real 0m0.129s user 0m0.165s sys 0m0.032s
Tzn. fixní náklady jsou u Javy vyšší. A variabilní naopak. Což se příznivě projeví při zpracování většího množství dat. Pro 16000×1200 pak máme časy:
$ time nodejs test-Float32Array.js real 0m2.523s user 0m2.072s sys 0m0.506s
$ time java Test Čas: 596 ms real 0m0.757s user 0m0.431s sys 0m0.406s
JavaScrip pak není jen 1,5× ale víc než 3,3× pomalejší než Java.
$ time java -Xmx1500m Ndata
Čas: 554 ms
java -Xmx1500m Ndata 0,38s user 0,33s system 102% cpu 0,690 total
$ time node ndata.js
TIME: 0.556 [s]
node ndata.js 0,32s user 0,34s system 99% cpu 0,666 total
Asi bude něco špatně s tvým Node.js, ale nevim co, Node.js až tak nerozumim...
Když byste nasadili Intel kompilátor, který by přišel na prasečiny a opakované výrazy a zoptimalizoval by je, dostanete jiné výsledky, než když to předhodíte gcc, které na ně nepřijde.Můžeš sem tedy hodit měření? Zajímalo by mě to.
Prostě test na zpracování rychlosti polí, kde záměrně autor napíše cyklus tak, aby to bylo neefektivní, to je stejné, jako testovat schopnosti atletů se svázanýma rukama za zády.To není dobré přirovnání, atlet se svázanýma rukama je neexistijující případ, zatímco ty úryvky kódu v blogu a diskusi představují IMHO celkem běžně používaný styl programování.
Kdo by to řekl! To bylo opravdu tak těžké odhadnout, že kompilátory do storjového kódu budou nejrychlejší, za nimi JIT a na konci čisté interpretry. Jestli jste tohle neodhadli sami, tak bych se divil.S tímhle souhlas.
Nemám k dispozici momentálně ani Intel, ani gcc kompilátor.V tom případě nemůžu než tu poznámku o Intel kopmilátoru brát vícméně jako prázdné řeči...
Kdyby se celé pole převedlo na jednorozměrné, urychlilo by se to samo i bez další nápovědy.To je jednorozměrné pole.
Kromě toho testovací příklady na rychlost, ve kterých se opakovaně vyhodnocují tytéž složité výrazy neberu vážně, stejně jako výsledky, které autor naměřil. Protože naměřil pouze schopnost kompilátoru hledat opakované výrazy (a gcc je hodně mizerný v optimalizaci kódu), ale nikoli rychlost toho kterého jazyka.Já osobně jsem z testu žádné důsledky nevyvozoval, takže to je trochu vlamování do otevřených dvěří...
+1
Ponkrácovi celkem věřím, že je schopný to dotáhnout do konce a nějakou práci udělat. Ale když jsou autoři programu zavaleni hromadou požadavků, dotazů a prázdných keců a nesplněných slibů, tak se jim nejde moc divit, že k tomu mají rezervovaný postoj a nechtějí s tím moc trávit čas, protože ze statistického hlediska je jen velmi malá šance, že z toho něco bude. V takové situaci je lepší hned na začátku poslat nějaký kus kódu – i kdyby byl úplně blbě a neodpovídal konvencím daného projektu. Je z toho zřejmé, že na druhém konci e-mailu sedí programátor, který je schopný něčím přispět a ne jen1 uživatel, který akorát vymýšlí, co všechno úžasného by se mohlo udělat (ale sám to neudělá). Kus kódu je něco, s čím se dá reálně pracovat, udělá se pár revizí, opraví se to a může se to začlenit.
Pak jsou i větší úkoly, které je potřeba nejdřív promyslet, zanalytovat, navrhnout nějakou architekturu a až pak se vrhnout na programování. Ale tam je zase nezvyklé, že by takovou práci dělal člověk, který do projektu dosud nijak nepřispěl – ani malou opravou nebo přidáním drobné funkcionality. Je to asi jako když člověk přijde do nové firmy – taky nejdřív bude opravovat chyby a psát drobná vylepšení, než ho nechají dělat něco důležitého.
[1] uživatel může být taky přínosný, pokud např. diagnostikuje chyby a dokáže je slušně popsat – taková hlášení autoři taky vítají
"use strict"; var diff = process.hrtime(); var width = 1600; var height = 1200; var nwidth = width * 2; var nheight = height * 2; var data = []; var newdata = new Array(nwidth * nheight); var someval = [0, 0, 0, 0.4, 1, 1.1, 8]; var it = 0; var size = width * height; for (var i = 0; i < size; i++) { data.push(someval[it++ % 7]); } for (var y = 0; y < height-1; y++) { for (var x = 0; x < width - 1; x++) { var nx = x * 2; var ny = y * 2; newdata[ny*nwidth + nx] = data[y*width + x]; newdata[ny*nwidth + nx + 1] = (data[y*width + x] + data[y*width + x + 1]) / 2.0; newdata[(ny+1)*nwidth + nx] = (data[y*width + x] + data[(y+1)*width + x]) / 2.0; newdata[(ny+1)*nwidth + nx + 1] = (data[y*width + x] + data[y*width + x + 1] + data[(y+1)*width + x] + data[(y+1)*width + x + 1]) / 4.0; } } diff = process.hrtime(diff); console.log("TIME: " + (diff[0] + diff[1] / 1e9).toFixed(3) + " [s]");Můj pokus 1:
"use strict"; var diff = process.hrtime(); var aWidth = 1600; var aHeight = 1200; var bWidth = aWidth * 2; var bHeight = aHeight * 2; // Use typed array. var aData = new Float64Array(aWidth * aHeight); var bData = new Float64Array(bWidth * bHeight); var someval = [0, 0, 0, 0.4, 1, 1.1, 8]; var it = 0; var size = aWidth * aHeight; // Prevent using modulo. for (var i = 0; i < size; i++) { aData[i] = someval[it]; if (++it >= 7) it = 0; } for (var y = 0; y < aHeight - 1; y++) { for (var x = 0; x < aWidth - 1; x++) { var nx = x * 2; var ny = y * 2; bData[ny*bWidth + nx] = aData[y*aWidth + x]; bData[ny*bWidth + nx + 1] = (aData[y*aWidth + x] + aData[y*aWidth + x + 1]) / 2.0; bData[(ny+1)*bWidth + nx] = (aData[y*aWidth + x] + aData[(y+1)*aWidth + x]) / 2.0; bData[(ny+1)*bWidth + nx + 1] = (aData[y*aWidth + x] + aData[y*aWidth + x + 1] + aData[(y+1)*aWidth + x] + aData[(y+1)*aWidth + x + 1]) / 4.0; } } diff = process.hrtime(diff); console.log("TIME: " + (diff[0] + diff[1] / 1e9).toFixed(3) + " [s]");Můj pokus 2:
"use strict"; var diff = process.hrtime(); var aWidth = 1600; var aHeight = 1200; var bWidth = aWidth * 2; var bHeight = aHeight * 2; var aSize = aWidth * aHeight; var bSize = bWidth * bHeight; // Use typed array. var aData = new Float64Array(aSize); var bData = new Float64Array(bSize); var someval = [0, 0, 0, 0.4, 1, 1.1, 8]; var it = 0; // Prevent using modulo. for (var i = 0; i < aSize; i++) { aData[i] = someval[it]; if (++it >= 7) it = 0; } // Organize more the array access. for (var y = 0; y < aHeight - 1; y++) { var ny = y * 2; var nx = 0; var aIndex = y * aWidth; var bIndex = ny * bWidth; for (var x = 0; x < aWidth - 1; x++, nx += 2) { var a00 = aData[aIndex]; var a01 = aData[aIndex + 1]; var a10 = aData[aIndex + aWidth]; var a11 = aData[aIndex + aWidth + 1]; bData[bIndex ] = a00; bData[bIndex + 1 ] = 0.5 * (a00 + a01); bData[bIndex + bWidth ] = 0.5 * (a00 + a10); bData[bIndex + bWidth + 1] = 0.25 * (a00 + a01 + a10 + a11); aIndex += 1; bIndex += 2; } } diff = process.hrtime(diff); console.log("TIME: " + (diff[0] + diff[1] / 1e9).toFixed(3) + " [s]"); var sum = 0; for (var i = 0; i < bData.length; i++) sum += isFinite(bData[i]) ? bData[i] : 0.0; console.log(sum);Výsledky:
[ORIGINAL] 1.121 [s] [POKUS #1] 0.059 [s] [POKUS #2] 0.058 [s]
node test.js TIME: 0.203 [s] pypy test.py TIME: 0.195 [s]
Použitie array('d') (ekvivalent Float64Array) namiesto obyčajného listu. Celkom dobrý výsledok na interpret do ktorého nikto nenalieval milióny dolárov.
Relevantná upravená časť kódu:
newdata = array('d', [0.0]) * (nwidth * nheight) someval = array('d', [0.0, 0.0, 0.0, 0.4, 1.0, 1.1, 8.0])
$ time python test.py real 0m4.106s user 0m3.992s sys 0m0.104s
$ time python3 test.py real 0m5.827s user 0m5.740s sys 0m0.076s
$ time nodejs test.js real 0m1.021s user 0m0.972s sys 0m0.065s
$ time ./test real 0m0.036s user 0m0.009s sys 0m0.029s
$ time java Test Čas: 62 ms real 0m0.186s user 0m0.231s sys 0m0.060s
Z toho ještě víc než půlka je start JVM, který je irelevantní, pokud program běží delší dobu.
P.S. už je dost pozdě, tak doufám, že jsem při přepisu z JS do Javy neudělal nějakou chybu… ale takhle nějak by to mohlo být.
Java zkompilovaná do nativní binárky pomocí GCJ:
$ gcj --main=Test /tmp/Test.java -O2 -o test.gcj $ time ./test.gcj Čas: 47 ms real 0m0.081s user 0m0.044s sys 0m0.042s
A Perl:
$ time ./test.pl real 0m4.492s user 0m4.353s sys 0m0.129s
(tenhle výsledek mě zklamal, Perlu jsem celkem fandil)
Po opravě chyby:
$ time ./test.pl real 0m5.236s user 0m4.989s sys 0m0.236s
new Array(100)
, tak výchozí hodnoty v poli budou undefined
a to pole nebude asi nikdy optimalizované jako int[] nebo double[]. A toto se nevztahuje jen na V8.
Tahle optimalizace výrazně pomůže. Ale pořád je to víc než 1,5× pomalejší než Java.
$ time nodejs test.js real 0m1.027s user 0m0.958s sys 0m0.085s
$ time nodejs test-Float32Array.js real 0m0.283s user 0m0.230s sys 0m0.056s
$ time java Test Čas: 60 ms real 0m0.185s user 0m0.249s sys 0m0.043s
No proto ja pouzivam jazyk D, ten vypada pekne a je to skoro stejne rychle jak C:
import std.range; immutable width = 1600; immutable height = 1200; immutable nwidth = 2 * width; immutable nheight = 2 * height; void main(string[] argv) { auto newdata = new double[width * height * 4]; auto data = [0, 0, 0, 0.4, 1, 1.1, 8].cycle.take(width * height).array; for (int y = 0; y < height-1; y++) { for (int x = 0; x < width - 1; x++) { int nx = x*2; int ny = y*2; newdata[ny*nwidth + nx] = data[y*width + x]; newdata[ny*nwidth + nx + 1] = (data[y*width + x] + data[y*width + x + 1]) / 2.; newdata[(ny+1)*nwidth + nx] = (data[y*width + x] + data[(y+1)*width + x]) / 2.; newdata[(ny+1)*nwidth + nx + 1] = (data[y*width + x] + data[y*width + x + 1] + data[(y+1)*width + x] + data[(y+1)*width + x + 1]) / 4.; } } }
[kozak@dajinka ~]$ time ./test real 0m0.036s user 0m0.013s sys 0m0.020s
import numpy as np from numba import jit width = 1600 height = 1200 @jit def get_new_data(data, width, height): nwidth = 2 * width nheight = 2 * height new_data = np.zeros(nwidth * nheight) for y in range(0, height - 1): for x in range(0, width - 1): nx = 2 * x ny = 2 * y new_data[ny * nwidth + nx] = data[y * width + x] new_data[ny * nwidth + nx + 1] = (data[y * width + x] + data[y * width + x + 1]) / 2 new_data[(ny + 1) * nwidth + nx] = (data[y * width + x] + data[(y + 1) * width + x]) / 2 new_data[(ny + 1) * nwidth + nx + 1] = (data[y * width + x] + data[y * width + x + 1] + data[(y + 1) * width + x] + data[(y + 1) * width + x + 1]) / 4 return new_data initial_values = 0, 0, 0, 0.4, 1, 1.1, 8 data = np.zeros(width * height) for i, initial_value in enumerate(initial_values): data[i::len(initial_values)] += initial_value get_new_data(data, width, height)
Přidávám rychlosti naměřené u mě. Pro jednotlivé jazyky jsem se snažil vybrat nejrychlejší implementace, které se tu objevily. U jazyku, před kterými je uvedeno x
jsem ověřoval správnost generovaného výsledku vůči výsledku vygenerovanému originálním C kódem.
x Původní C (-O3): 0.048s
D (-O3): 0.076s
x uclang libjit: 0.084s (0.074854)
nodejs: 0.117s (0.074 [s])
Java: 0.175s (72ms)
x Python (pypy): 0.285s (2.32852292061)
x Python (python): 4.881s
Python (python3.4): 6.720s
perl: 7.832s
x uclang: 8.504s
Pro hrubé výpočty je možné v jazyce uclang použít modul jit
(založený na knihovně libjit), který akceptuje podmnožinu jazyka C. Při využití modulu jit
trvá výpočet 0.103 sekundy. Měřeno příkazem time
, včetně rychlosti startu uclang interpretru a JIT překladu. Po optimalizací JIT kódu se délka výpočtu zkrátí na 0.084 sekundy.
Jak už bylo napsáno v předcházejících komentářích: myslím, že skriptovací jazyky obecně nejsou určeny pro hrubé výpočty, a tyto by měly být prováděny v modulech naprogramovaných v C++ a importovaných skriptovacím jazykem. Jinak hledím na V8 a jeho JIT, stejně tak na pypy s otevřenou pusou.
jeste to zkus s py3 + numpy + numba
#!/bin/awk -f BEGIN { width = 1600; height = 1200; nwidth = width * 2; nheight = height * 2; #@data; #@newdata; split ("0, 0, 0, 0.4, 1, 1.1, 8", someval, ","); it = 0; size = width * height; for (i = 0; i < size; i++) { data[i]=someval[(it++ % 7)]; } for (y = 0; y < height-1; y++) { for (x = 0; x < width - 1; x++) { nx = x*2; ny = y*2; newdata[(ny*nwidth + nx)] = data[(y*width + x)]; newdata[(ny*nwidth + nx + 1)] = (data[(y*width + x)] + data[(y*width + x + 1)]) / 2.0; newdata[((ny+1)*nwidth + nx)] = (data[(y*width + x)] + data[((y+1)*width + x)]) / 2.0; newdata[((ny+1)*nwidth + nx + 1)] = (data[(y*width + x)] + data[(y*width + x + 1)] + data[((y+1)*width + x)] + data[((y+1)*width + x + 1)]) / 4.0; } } }
Časy doplněné o Ruby a AWK (u mě to zvládne bez problémů).
x Původní C (-O3): 0.048s
D (-O3): 0.076s
x Uclang libjit: 0.084s (0.074854)
NodeJS: 0.117s (0.074 [s])
Java: 0.175s (72ms)
x Python (pypy): 0.285s (2.32852292061)
x Python (python): 4.881s
Ruby: 5.952s
Python (python3.4): 6.720s
Perl: 7.832s
x Uclang: 8.504s
AWK: 11.528s
Python numba klade příliš velký odpor při instalaci.
node test.js 0,41s user 0,03s system 100% cpu 0,440 total
ruby test.rb 1,76s user 0,03s system 98% cpu 1,821 total
haskell/test 2,56s user 0,23s system 99% cpu 2,785 total
python test.py 4,80s user 0,07s system 99% cpu 4,868 totalPřekvapilo mě ruby, čekal jsem, že bude stejně rychlé jako python a ono je 3x rychlejší.
data
. Tím, že jsem se tam zbavil modula jsem ušetřil zhruba 0.9s (cca 10% z původního času).