PHP: Výsledky srovnání funkcí file_(get)put_contents & f(read/write/flush)

Lin
dík za tvou přechozí pomoc. Psal, si ale že se už do diskuse nebudeš zapojovat, tak nechápu co tu furt rozebíráš - tvoje pocity mě nezajímají. Stálo mě to dost energie a nemám náladu číst, že je to k ničemu. Fakta a grafy jsem zveřejnil a algorytmy taky. Nezajímá tě to? Tak sem už nepiš. Děkuji.


Jenom bych upřesnil, že neustále píšete, že selhávají funkce flock() a file_put_contents() atd., ale ve skutečnosti selhává váš kód. Ty funkce fungují správně, chyby jsou ve vašem kódu.
Přesně tak. Já jsem myslel, že teď když si Exkalibr ten test doladil, že ho spustil celý znovu a všechny výsledky v grafu jsou SPOLEHLIVÉ zápisy dat, ale vypadá to, že se pořád plácáme v dolaďování testovacího scriptu. Jakýkoliv graf v tuhle chvíli ztrácí smysl. Jediná správná věc je všechno zahodit, napsat znovu test pečlivě (slovy pečlivě) a odprezentovat jediný graf s jasným sdělením. Takhle to vypadá, že cílem bylo pochlubit se uměním vytvářet grafy a testovací script posloužil jen jako generátor náhodných dat.

Blbost. Tady už není co dolaďovat napsal jsem snad jasně, že T10 je dokončený a bez chyb. Není co dolaďovat. K ostatním testům se vracet nebudu, nelze je opravit, když obsahují chyby. Jediné v čem se plácáme dokonala je tvá nechápavost. Psal jsem to už několikrát a nebaví mě to opakovat, že ten graf odráží rychlosti čtení a zápisu a chyby v tom nehrají roli. Když došlo k údajnému selhání fwrite, soubor byl přece načten a zkopírován pomocí copy(). Takže ty časy jsou spolehlivé a věruhodné. Nechápu co to tu furt plácáš.
To, že bych se v tom plácal já není nic překvapivého, tenhle test byl jeden velký chaos, jsi hodně zbrklý. Bohužel se v tom plácáš hlavně ty. Jestliže jsi napsal:
Ty testy jsem začal dělat proto, že jsem se chtěl bufferování vyhnout. Chtěl jsem najít způsob jak přečíst a zapsat data bezpečně do souboru, aniž bych se musel bát, že když je pozměním, změny nebudou uloženy. Proto mě ty funkce file_get(put)_contents nezajímají, jsou pro mě nepoužítelné.
tak to znamená, že jsi došel ke zcela mylnému závěru. Funkce file_get(put)_contents je spolehlivá a použitelná.

PS: psal jsem, že sem nebudu psát, protože jsem se bál, že rozjedeš diskuzi na téma toho nového zámku co jsi vymyslel.

Kit

  • *****
  • 704
    • Zobrazit profil
    • E-mail
Jenom bych upřesnil, že neustále píšete, že selhávají funkce flock() a file_put_contents() atd., ale ve skutečnosti selhává váš kód. Ty funkce fungují správně, chyby jsou ve vašem kódu.
Nic jiného netvrdím. Záleží přece na tom jakým způsobem ty funkce použijete a to jsem psal, že já hledám funkci, která umožňuje přístup k souboru více uživateli, načíst soubor, změnit soubor, zapsat soubor. V mých testech není ta změna.

Tak to jsem ti přece takový skript napsal. Fopen, flock, fread, fwrite, fclose. To funguje perfektně a bez chyby.

Samozřejmě je lepší používat file_get_contents a file_put_contents s tím, že mezi nimi ten soubor zamknutý není, což v jiných aplikacích vůbec nevadí. Obvykle se totiž používají pro zápis generovaného souboru, logu nebo čtení konfigurace.

Pro práci s daty, u kterých je riziko souběhu při R/W, je dobrá databáze SQLite. Sami autoři uvádí, že jejich cílem byla náhrada funkcí fopen, flock, fread a fwrite. Netuším, proč se tomu bráníš - na diskuzní fórum je to perfektní, rychlé, šetří to místem a má to vyřešeny všechny problémy, se kterými se potýkáš.

Těm grafům nerozumím a zřejmě nejsem jediný.

Nic jiného netvrdím. Záleží přece na tom jakým způsobem ty funkce použijete a to jsem psal, že já hledám funkci, která umožňuje přístup k souboru více uživateli, načíst soubor, změnit soubor, zapsat soubor. V mých testech není ta změna.
To ale umožňují všechny funkce, o kterých tady byla řeč. Akorát se (kterákoli z nich) musí správně používat. Navíc ty vaše testy mohou ale nemusí případné chyby odhalit. Konkurenční programování je založené na tom, že pochopíte problém a nastudujete si dokumentaci použitých funkcí. Nelze to založit na testech, protože variant je příliš mnoho, takže je nedokážete otestovat všechny. A do třetice, výsledkem testu, který ověřuje, zda by váš kód mohl být správně, je výstup ano (možná je správně) nebo ne (není správně) – ne žádné grafy. Pokud vám z testu vyjde, že ten kód je špatně, nemá smysl z toho malovat nějaký graf, ale opravit ten kód.

@Kit: Po jak velkých blocích je nejoptimálnější načítat data ze souboru? Dejmetomu, že mám texťák s emaily a id-éčkama uživatelů, na začátku je hlavička s rejstříkama cca 2KB. Je nějaký rozdíl v rychlosti či odezvě v tom jestli napoprvé načtu 2kB, 4kB nebo 8kB? Opakuji, že jde jen o první načtení hlavičky, z toho zjistím přesné umístění bloku, ve kterém hledat email po přihlášení; Takový blok by pak měl mít prakticky mezi 50-250 znaky. Takže i kdyby měl soubor 4MB, během přihlašování načtu jen hlavičku a pak ten hledaný blok.
« Poslední změna: 18. 10. 2019, 17:05:44 od exkalibr »



... nejoptimálnější...
?

Čemu nerozumíš ve slovu "nejoptimálnější"?

Kit

  • *****
  • 704
    • Zobrazit profil
    • E-mail
@Kit: Po jak velkých blocích je nejoptimálnější načítat data ze souboru? Dejmetomu, že mám texťák s emaily a id-éčkama uživatelů, na začátku je hlavička s rejstříkama cca 2KB. Je nějaký rozdíl v rychlosti či odezvě v tom jestli napoprvé načtu 2kB, 4kB nebo 8kB? Opakuji, že jde jen o první načtení hlavičky, z toho zjistím přesné umístění bloku, ve kterém hledat email po přihlášení; Takový blok by pak měl mít prakticky mezi 50-250 znaky. Takže i kdyby měl soubor 4MB, během přihlašování načtu jen hlavičku a pak ten hledaný blok.

Optimální by mělo být 8 KiB, protože to je obvyklá velikost bufferu.

BTW: Slovo "nejoptimálnější" je nesmyslné - slovo "optimální" je superlativem a stupňovat se nedá.

@Kit: Po jak velkých blocích je nejoptimálnější načítat data ze souboru? Dejmetomu, že mám texťák s emaily a id-éčkama uživatelů, na začátku je hlavička s rejstříkama cca 2KB. Je nějaký rozdíl v rychlosti či odezvě v tom jestli napoprvé načtu 2kB, 4kB nebo 8kB? Opakuji, že jde jen o první načtení hlavičky, z toho zjistím přesné umístění bloku, ve kterém hledat email po přihlášení; Takový blok by pak měl mít prakticky mezi 50-250 znaky. Takže i kdyby měl soubor 4MB, během přihlašování načtu jen hlavičku a pak ten hledaný blok.

Optimální by mělo být 8 KiB, protože to je obvyklá velikost bufferu.

To je totiž dobrá otázka :-(

Na úrovni blokové vrstvy a zřejmě i filesystému je dnes nejmenší optimální velikost přístupové transakce na x86 pravděpodoně 4 kB, což je obvyklá velikost stránky VM a obvyklá velikost sektoru na blokové vrstvě. A nejlíp ještě zarovnat na hranice 4 kB od začátku souboru. Filesystém sice alokuje ve větších "clusterech", ale ty IMO nemusí naráz číst a zapisovat. (Odhlédněme od faktu, že donedávna obvyklá velikost sektoru na disku bývala 512 B - ono to na věci možná mnoho neměnilo, vzhledem k tomu, jak funguje v UNIXu memory management v těsné spolupráci s blokovou vrstvou a souborovými systémy. A odhlédněme od existence "huge pages" na Linuxu a snad i jinde.)

A Kit má naopak pravdu v tom, že 8 kB/kiB je alokační jednotka Apache HTTPd "buckets", konkrétně:
Kód: [Vybrat]
/* default bucket buffer size - 8KB minus room for memory allocator headers */
#define APR_BUCKET_BUFF_SIZE   8000
Což osobně čtu tak, že user-space alokátor Apache si přidělenou virtuální paměť (přidělena kernelem po 4 kB) parceluje pro potřeby file IO po 8kB (8192 B), ale payloadu Vaší PHP aplikace z toho dává jenom 8 kiB (8000 B).

Kromě toho je v Linuxu na blokové vrstvě nějaký pevný read-ahead, by default to bývalo 128 kB. A nějaký svůj vlastní adaptivní read-ahead má souborová vrstva - a dá se tuším i ladit explicitním syscallem na otevřený file descriptor, což ale spousta softu nedělá. A při dlouhém sekvenčním zápisu jsem pozoroval, že Linux láme ten stream na ATA transakce dlouhé taky řádově pár set kB - a pokud moje transakce (předávané mým softwarem z user space) nebyly zarovnané na určité hranice mocniny dvou v LBA adresaci disku, tak mi je "lámal vejpůl", aby na blokové vrstvě na té hranici končil a začínal... už je to pár let, dneska to může být jinak.

Při čtení jednoho bloku (Vaše "hlavička") podle mého vůbec nemá smysl se zabývat velikostí čtecí transakce. Obecně důležité je, minimalizovat počet těch transakcí. Jestli načtete jenom 2 kB, které opravdu potřebujete, nebo budete spekulovat na celý bucket, to je myslím dost jedno. Tady bych doporučil: zbytečně to nedrobte. Pokud jde o jednu transakci, tak si prostě řekněte "svému prostředí" o ta data která potřebujete, ale netřeba to zbytečně nafukovat, nepomůžete si. Ony už si to "vrstvy mezi vámi a diskem" vhodně přifouknou a zarovnají (případně zlomí vejpůl, pokud se někde netrefíte na zarovnání).

Podle mého má smysl optimalizovat především *zápis*. Pokud máte data hezky za sebou = taková optimalizace je možná, zapisujte v co největších blocích (zas tak aby Vám to nezačlo dělat problémy s dostupným objemem RAM). Usnadníte tím práci fyzickým diskům - ať už se bude jednat o flash (erase bloky velké pár set kB až jednotky MB) nebo o točivý disk (pro transakce větší než 1 MB se průchodnost v random zátěži začíná blížit průchodnosti sekvenční). Nemá ale smysl, dělat kvůli tomu po svém nějaký read-ahead a write-back, protože potřebujete zapsat 1 B a chcete to systému usnadnit. To je blbost - pokud potřebujete drobné zápisy rozprostřené po ploše, tak se asi nedá nic dělat, a uděláte líp, když to necháte na operačním systému.

Obecně třeba ten bucketový systém v Apačovi je podle mého původně myšlen tak, aby bylo možno transparentně optimalizovat práci s daty na souborovém systému / na disku. A dost jsem se svého času vztekal, že konkrétně pro problém "mnoho paralelních čtoucích sekvenčních klientů nad točivým diskem" ty buckety rozhodně nejsou optimalizované a není je jak ladit. (Jednotná velikost bucketu, nulová možnost práce s maličkými transakcemi vs. s read-aheadem pro zjevně sekvenční streamy. Leda by člověk celou tu bucketovou vrstvu zásadně přepsal.) Asi to docela dobře sedí na VM vrstvu s její 4kB velikostí stránky.

Po jak velkých blocích je nejoptimálnější načítat data ze souboru?
Optimální je načítat data po stejných blocích, po jakých je načítá knihovní funkce do bufferu, po jakých s nimi pracuje souborový systém a po jakých s nimi pracuje disk. Přičemž v každé té vrstvě může být blok jinak velký, v lepším případě jsou to alespoň násobky. A v každé té vrstvě to může být v různých případech jinak. Takže nemá smysl to experimentálně zjišťovat, protože tím zjistíte pouze parametry pro ten případ, kde to testujete – a tam je lepší se podívat do konfigurace, jak máte nastavený souborový systém a jaký tam máte disk.

ByCzech

  • *****
  • 1 848
    • Zobrazit profil
    • E-mail
Což osobně čtu tak, že user-space alokátor Apache si přidělenou virtuální paměť (přidělena kernelem po 4 kB) parceluje pro potřeby file IO po 8kB (8192 B), ale payloadu Vaší PHP aplikace z toho dává jenom 8 kiB (8000 B).

Jen pro upřesnění. Správě to je:

8kB = 8000 B
8kiB = 8192 B

Nestandardizovaně, když se to v počítačovém pravěku potřebovalo rozlišovat a data a paměťi se počítaly na tisíce bajtů:

8KB = 8192 B

Problém nastal, když se začaly data a paměti počítat v řádech miliónů bajtů a předpona mega (10^3) už má velké písmeno. Později došlo ke standardizaci jako norma EC 60027-2, kde jsou předpony odvozené od těch v tabulce SI, ale stejně se v tom pořád běžně dělá binec a to co je tím myšleno se musí často odvozovat z kontextu, viz např. výrobci disků vs výrobci pamětí, kde GB u disku je 10^6 (SI), ale u pamětí to je 2^20 (takže vlastně GiB).

https://cs.wikipedia.org/wiki/Bin%C3%A1rn%C3%AD_p%C5%99edpona

@Kit:
Pro mě není slovo optimální dostačující. Je optimální a ještě víc optimální. Optimální může být pro vysokoúrovňového programátora, tzn. dostačující, ale pro perfekcionistu nízkoúrovňového programátora je nutné použít superlativum nejoptimálnější, protože to znamená dokonalý stav, kterého každý pedant touží dosáhnout.

Slovo read-ahead se má číst [red ~hed] nebo [ríd ~hed]?

František Ryšánek:
Díky za rozsáhlý popis.

Mám pocit, že většinou budu provádět zápis do jednoho souboru (append) v rozsahu ~250, ~500, ~1000 bajtů, takže to zarovnávání řešit nemusím.

Ještě dotaz. Potřeboval bych do souboru zapsat rejstřík s idečkama (část první) a k tomu kompatibilní rejstřík s číslama řádků v jiném souboru (část druhá).

S tím se váže otázka na používání funkcí chr a ord. Když bych do souboru chtěl zapsat uživatelská idečka a je třeba to udělat binárně a bezpečně tj. zkopírovat dva bajty s daným id, jak to správně udělat? Já znám jen ty dvě funkce chr a ord mám obavy o to, že tyhle věci budou náročné na výkon. A neměl bych taky řešit endianness (česky endianita?) správnost přečtení z binárního souboru?
« Poslední změna: 19. 10. 2019, 19:42:37 od exkalibr »

Pro mě není slovo optimální dostačující. Je optimální a ještě víc optimální. Optimální může být pro vysokoúrovňového programátora, tzn. dostačující, ale pro perfekcionistu nízkoúrovňového programátora je nutné použít superlativum nejoptimálnější, protože to znamená dokonalý stav, kterého každý pedant touží dosáhnout.
Optimální ale neznamená dostačující, znamená to nejlepší možný, dokonalý. Jak může být něco nejlepší, když existuje něco ještě lepšího?