Na začátku není, za E ovšem je.
Stejně tak nejsou povoleny levostranné nuly (taky není řešeno - většina číselných parserů si s tím poradí).
Ony jsou třeba definované i přesně povolené bílé znaky. Taky to nedodržuju přesně, protože bílé znaky bych musel testovat několika ify, což je náročnější, než testovat to jednou podmínkou na menší než.
> bílé znaky bych musel testovat několika ify, což je náročnější, než testovat to jednou podmínkou na menší než.
Na tohle je ideální bitset - ve 32-bitovém int označím bity, které mě zajímají a pak jen if (c <= 32 && ((c-1)&WHITESPACE_BITS) != 0) ...
(s optimalizací pro 32-bit architektury - předpokládám, že c == '\0' je ošetřeno jinde)
Ten první příspěvek je docela validní - vzhledem k tomu, že benchmark je testovaný na datech, která jsou okamžitě přístupná, čekal bych, že cena za asynchronnost bude minimalizována. Tohle vypadá na 100 MB/s, což je o dost pomalejší než "obyčejný" Jackson v Java.
I když jsou všechny znaky k dispozici, ten kód tohle neví. Asi nejvíc je vidět v tom výpisu, kde musí testovat zda nenarazil na konec. Průměrně vychází 28 taktů na znak, což docela odpovídá tomu, jakou režii má co_await. Ale třeba se to dá ještě nějak zoptimalizovat, nevím. Nechci se pouštět do asm.
Není například možné načítat znaky po větších blocích, protože stream může skončit kdykoliv v prostřed bloku. Žádný "padding" tam nikdo nedá(jak to řeší třeba simdjson - v tom je třeba zásadní optimalizace).
Cílem příspěvku ale bylo ukázat možné řešení korutinou. Ono 100MB není zase málo, protože zkus si to představit. I když budeš mít server na 1Gb síti, pak tvá propustnost je velice optimisticky právě oněch 100MB/s. Optimisticky. Na levném VPS bude mnohem nižší. A klient třeba na internetu může mít nakonec ještě méně. Takže ten kód se stejně bude většinu času flákat, ale nebude zabírat víc paměti.
Jak krásný nepoměr je vidět právě v tom, že načtení všech dat trvá 4s, a parsování 3.5s. Nepoměr na síti může být mnohem větší. K čemu mít G/s, když několik desítek sekund trvám na upload?
> I když jsou všechny znaky k dispozici, ten kód tohle neví.
Může mít cache a vědět, zda má dost znaků pro následující operaci. Asm IMHO potřeba není, ale chápu, že optimalizovaný kód je komplikace.
> K čemu mít G/s, když několik desítek sekund trvám na upload?
To je zavádějící. Za prvé, server dělá spousu jiných věcí než jen parsuje vstup. Za druhé, tenhle typ parserů bude ideální na různé IoT, kde bude připojených miliony klientů k jednomu serveru. Ten hardware někdo musí zaplatit a pokud bude JSON parser brát 50% navíc, tak se to na účtu projeví dost drasticky.
Ok, hele jsem pro diskuzi, v zásadě to není alfou omegou tohoto příspěvku
Chtěl jsem to pojmout jako ukázku asynchronního zpracování dat, nějaký jednoduchý ale ne zrovna triviální algoritmus, nějaké challenge tam má být , jako třeba rekurze.
Cílem bylo vysvětlit jak pracovat s awaiterem, ukázat jak psát koncepty, (většina ultrarychlych parseru mi nutí i vlastní DOM)
Asi je prostor pro optimalizaci, mohu zkusit některé části přepsat na branchless, možná inlinovat čtení řetězců aby nebylo potřeba držet stav čtení. Co určitě by se blbě dělalo je zpracování vicero znaků současně, protože to nejde zaručit, že stream bude zarovnany na blok, nebude
Byl to obecný komentář nad tím, že nemá smysl takové věci optimalizovat, bo úzké hrdlo je někde jinde. Všechno je úzké hrdlo.
Jako proof of concept je to ok, a ten výkon v zásadě dobře ukazuje, že je tu daň za asynchronní zpracování. Jsou tam věci, které se můžou optimalizovat na základě optimistických předpokladů - Netty má třeba hezky vyřešené parsování na základě exception a když nedostane celý blok, tak se vrátí zpátky na začátek (což je drastická cena, ale prakticky nenastává). Jestli optimalizovat tady záleží na tom, jestli to bude skutečně někdo používat nebo to skončí u proof of concept ;-)
K tomu je potřeba dodat, že simdjson je trošičku FAKE. On je totiž schopen rychle zpracovat i nevalidní JSON a vůbec mu to nevadí. Totiž problém je, že simdjson ten JSON vlastně ani nečte, on si jen označí logické začátky a konce a chápu, tohle se dá dělat SIMD. Skutečný parsing probíhá až při čtení dat. Otázkou je, jak ten benchmark měří.
Jako já mohu měřit načtení JSONU i takto
auto zacatek = std::chrono::system_clock::now();
bool nacteno=true
auto konec = std::chrono::system_clock::now();
Jsem ultra rychlej. Protože jsem parsing deferroval mimo měření.
Jinak co si hraju s optimalizacemi, tak zatím nic moc nepomáhá, i třeba validace řetězce čtená přímo z cache celou věc nějak extrémě neurychlí. Samozřejmě, pokud přeskočím validaci, tak se to urychlí
Příklad:
https://github.com/simdjson/simdjson?tab=readme-ov-file#quick-start
$ tail -n 13 twitter.json
"search_metadata": {
"completed_in": 0.087,
"max_id": 505874924095815700,
"max_id_str": "505874924095815681",
"next_results": "?max_id=505874847260352512&q=%E4%B8%80&count=100&include_entities=1",
"query": "%E4%B8%80",
"refresh_url": "?since_id=505874924095815681&q=%E4%B8%80&include_entities=1",
"count": 1000,
"test":ahoj svete,
"since_id": 0,
"since_id_str": "0"
}
}
$ ./quickstart
1000 results.
Ani si nevšiml, že tam mám
"test":ahoj svete,
Pouze když to dám do "count":ahoj svete,
$ ./quickstart
terminate called after throwing an instance of 'simdjson::simdjson_error'
what(): INCORRECT_TYPE: The JSON element does not have the requested type.
Takže simdjson je spíš jedno velké divadlo, než parser
"""
Na tohle je ideální bitset - ve 32-bitovém int označím bity, které mě zajímají a pak jen if (c <= 32 && ((c-1)&WHITESPACE_BITS) != 0) ... (s optimalizací pro 32-bit architektury - předpokládám, že c == '\0' je ošetřeno jinde)
"""
Clang je chytrej
void eat_white() {
while (_pos < _str.length()) {
char c = _str[_pos];
if (c != '\r' && c != '\n' && c != '\t' && c != ' ') break;
++_pos;
}
}
přeloží jako
0x555555557040 :
...
mov 0x68(%rsp),%rax
movzbl (%rax,%r15,1),%eax
cmp $0x20,%rax
ja 0x5555555570a0
movabs $0x100002600,%rcx
bt %rax,%rcx
jae 0x5555555570a0
inc %r15
cmp %r15,%rbp
jne 0x555555557040
...
0x5555555570a0:
Intenzivně se zabývám programováním zejména v jazyce C++. Vyvíjím vlastní knihovny, vzory, techniky, používám šablony, to vše proto, aby se mi usnadnil život při návrhu aplikací. Pracoval jsem jako programátor ve společnosti Seznam.cz. Nyní jsem se usadil v jednom startupu, kde vyvíjím serverové komponenty a informační systémy v C++
Přečteno 50 233×
Přečteno 23 535×
Přečteno 22 525×
Přečteno 20 434×
Přečteno 17 495×