Java DataInputStream - rychlost

Re:Java DataInputStream - rychlost
« Odpověď #15 kdy: 21. 05. 2023, 15:36:39 »
Kód: [Vybrat]
byte[] bytes = new byte[4];
ByteBuffer buf = ByteBuffer.wrap(bytes);

for (int i = 0; i < size; i++) {
   dis.read(bytes);
   buf.rewind();
   pole[i] = buf.getFloat();
}

Jen pozor, že InputStream.read nemusí vždycky naplnit celý buffer, pokud se mu nechce (a vrací počet bytů, který doopravdy načetl). Bezpečnější je použít něco jako třeba IOUtils.read z commons-io, ale je otázka, co to v tvém případě udělá s výkonem...


Re:Java DataInputStream - rychlost
« Odpověď #16 kdy: 22. 05. 2023, 07:19:29 »
Od stolu bych tipoval: Volání BufferInputStream.read(bytes) na každý bytě je zpočátku neefektivní, ale JIT ho potom zoptimalizuje, proto při opakovaném použití to není problém. JIT typicky nekomplikuje kód hned, ale až po nějakém množství použití. Tím si jednak zajišťuje, že nekomplikuje kdeco, a jednak díky tomu má nějaké statistiky, kterých může využít. Takže část cyklu poprvé proběhne neoptimálně, proběhne kompilace a dál je to OK.

To, že záleží, ve kterém vlákně se to spustí, je na první pohled divné, ale v zásadě to není v rozporu s hypotézou. Mj. je možné, že i další vlákna budou mít nějaký vliv na JIT.

Jak to ověřit?

a) Napadá mě použití -Xint, tím JIT vypadne ze hry. Ano, bude to pomalejší, ale mělo by to být konzistentně pomalejší. Problém ale je, že to může být tak pomalé, že ten rozdíl v tom nebude patrný.
b) GraalVM native-image – vše se zkompiluje ještě před startem, JIT do toho nebude házet vidle. U dlouho běžících aplikací může JIT udělat lepší práci než native-image, ale native-image dává predikovatelnější výkon. Nevýhodou je, že nelze použít na každou aplikaci, protože to klade nějaké dodatečné požadavky – zejména musí vědět, co všechno má zkompilovat, do čehož mu může do nějaké míry házet vidle třeba reflexe provedená později než při statické inicializaci. Spoustu věcí lze řešit, jen jsem chtěl upozornit, že ne všechny aplikace lze takto přeložit na první dobrou.
c) Experimentovat s -Xcomp (zkompiluje vše při prvním použití; bude toho kompilovat více, bude častěji rekompilovat (protože častěji nevyjde spekulativní předpoklad), asi z toho nebudou padat tak dobré výsledky), případně -XX:CompileThreshold. Neříkám, že se to hodí do produkce, spíš to může přinést predikovatelnější chování.

BTW, Executor obecně nemusí kód spustit hned (a dokonce ani v jiném vlákně). Zřejmě s tím není problém (jinak by problém nevyřešil onen workaround), ale je dobré s tím počítat a nebrat volání Executor.execute(Runnable) za začátek práce.

luvar

  • ***
  • 238
    • Zobrazit profil
    • E-mail
Re:Java DataInputStream - rychlost
« Odpověď #17 kdy: 22. 05. 2023, 23:10:26 »
Kód: [Vybrat]
byte[] bytes = new byte[4];
ByteBuffer buf = ByteBuffer.wrap(bytes);

for (int i = 0; i < size; i++) {
   dis.read(bytes);
   buf.rewind();
   pole[i] = buf.getFloat();
}

Jen pozor, že InputStream.read nemusí vždycky naplnit celý buffer, pokud se mu nechce (a vrací počet bytů, který doopravdy načetl). Bezpečnější je použít něco jako třeba IOUtils.read z commons-io, ale je otázka, co to v tvém případě udělá s výkonem...

Ja by som navrhoval nemotať knižnice, kým netreba a použil dis.readFully(buff).

V každom prípade profiling, krátky test v JMH (i ked toto je relativne velka uloha pre takyto typ testu a hlavne zavisla od externych veci. Vysledok je teda kus otazny), alebo mozno aj spustenie cez strace a pozeranie ocami do konzoly, dokaze nieco odhalit. Prípadne nejaký z toolov z obrázku tuná https://brendangregg.com/linuxperf.html by mohol nasmerovat (nielen pri jave).

V kazdom pripade by som veril, ze 4 volania read() mozu byt ten rezdiel, co mate namerany. Skusit si oddedit vlastny kus a pretazit jednu metodu, aby to robila rovnak by mohlo byt vcelku jednoduche.

Re:Java DataInputStream - rychlost
« Odpověď #18 kdy: 22. 05. 2023, 23:46:46 »
Načtení 128MB náhodných dat ze souboru jako float - hned na první průchod:

DataInputStream 2868 ms
MappedByteBuffer 632 ms

Arthur

  • ***
  • 171
    • Zobrazit profil
    • E-mail
Re:Java DataInputStream - rychlost
« Odpověď #19 kdy: 23. 05. 2023, 16:25:30 »
Díky za podněty, z uvedeného mi přijde, že hypotéza ohledně JIT nejvíce odpovídá reálnému chování.

Celkem uspokojivě jsem to vyřešil pomocí toho ByteBufferu. Čím větší tím je to rychlejší (a blíží se výkonu MappedByteBufferu), ale zase se potýkám s konzumací až 2-násobného množství RAM, což začíná být významné u souborů přes 500MB. Takže jsem zvolil nějaký přiměřeně velký buffer.