Paměťová a výpočetní náročnost JVM vs .NET

Re:Paměťová a výpočetní náročnost JVM vs .NET
« Odpověď #135 kdy: 20. 09. 2016, 21:47:36 »
Reagoval jsem jen na tvrzení, že pole referencí je stejně efektivní jako pole pole hodnot.
Což tady pokud vím nikdo netvrdil. Už jenom proto, že jsou různá hlediska efektivity, která jdou často proti sobě. A také různé způsoby použití.


Re:Paměťová a výpočetní náročnost JVM vs .NET
« Odpověď #136 kdy: 20. 09. 2016, 22:34:57 »
Už mě to moc nebaví, takže jenom krátce.

Vytvoření pole objektů není žádná optimalizace, je to přímočarý nativní přístup.

Vytvoření pole referencí na objekty je javovina, nic to nepřináší (kromě zjednodušení garbage collectoru), co se týká přístupu do paměti je to neoptimální. Většinou ta neoptimálnost nevadí, pokud ano, je to v Javě problém, protože to nejde normálně vyřešit, musí se to hackovat přes pole primitivních typů, což úplně rozbije datový model a kód v aplikaci.

Nazývat převedení pole referencí na pole objektů předčasnou optimalizací je úplně mimo, pole objektů by měl být nativní přístup, ale není, protože Java...
Tak toto je absolútna <|>vina. Referencie aj objekty sú v Random Access Memory, t.j. v pamäti s ľubovoľným prístupom. Moderné operačné systémy používajú virtuálnu adresáciu pamäte a nikto nezaručí, že časť poľa hodnôt bude v jednej časti fyzickej pamäte a druhá časť nebude celkom inde. Samozrejme platí, že pre procesor sú dáta najrýchlejšie prístupné v poradí register -> L1 -> L2 -> L3 -> RAM -> SWAP. Platí že je vyššia pravdepodobnosť, že pole hodnôt bude bližšie k procesoru (L1, L2,L3) ako keď pôjde o pole referencií, čím by výpočet teoreticky mohol prebehnúť rýchlejšie.
Keďže ale na serveri beží paralelne oveľa viac procesov ako je jadier procesorov, (na mojom serveri je to napríklad teraz 4 jadrá/211 procesov), procesy sa na jednom jadre prepínajú (o to sa stará OS), L1,L2 a L3 sa vyprázdňuje a načítava podľa potreby procesov (o to sa stará správca cache v procesore), pravdepodobnosť toho, že pole hodnôt zostane v cache počas celej práce s poľom je takmer nulová a celá snaha o takúto optimalizáciu nemá žiaden zmysel.
Oveľa väčší zmysel ako sa snažiť o ušetrenie pár nanosekúnd v prístupe k RAM je venovať sa problému, analyzovať ho, zjednodušiť a skrátiť a zrýchliť tým výpočet.
Z celej tejto diskusie mi vyplýva, že pri programovaní v C# sa musí hlavne všetko optimalizovať (keďže to nezvládne za neho OS) a celý jazyk je ako trošku lepší assembler, kdežto v Jave sa sústredím na problém a vyriešim ho tým oveľa rýchlejšie, priamočiarejšie a keďže pri tom nemusím používať všelijaké hacky, tak aj prehľadnejšie pre ostatných.

Radek Miček

Re:Paměťová a výpočetní náročnost JVM vs .NET
« Odpověď #137 kdy: 20. 09. 2016, 23:00:38 »
Už mě to moc nebaví, takže jenom krátce.

Vytvoření pole objektů není žádná optimalizace, je to přímočarý nativní přístup.

Vytvoření pole referencí na objekty je javovina, nic to nepřináší (kromě zjednodušení garbage collectoru), co se týká přístupu do paměti je to neoptimální. Většinou ta neoptimálnost nevadí, pokud ano, je to v Javě problém, protože to nejde normálně vyřešit, musí se to hackovat přes pole primitivních typů, což úplně rozbije datový model a kód v aplikaci.

Nazývat převedení pole referencí na pole objektů předčasnou optimalizací je úplně mimo, pole objektů by měl být nativní přístup, ale není, protože Java...
že časť poľa hodnôt bude v jednej časti fyzickej pamäte a druhá časť nebude celkom inde.

To je pravda, ale stále bude velké množství hodnot u sebe.

Zatímco, když použijete pole referencí, tak zvyšujete spotřebu paměti (v mém příkladu se zvýšila 6x) - což mj. znamená, že plýtváte cachí. Dále zvyšujete počet alokovaných objektů, jejichž dosažitelnost bude GC periodicky kontrolovat (je-li délka pole n, počet objektů se zvýší cca n krát) - tj. zbytečná zátěž pro CPU. A nakonec je tu větší riziko, že hodnoty nebudou u sebe.

Pro připomenutí můj příklad:

Příkladem je seznam čísel, když v Javě použijete generický ArrayList pro uložení 1000 intů. Předpokládejme, že aplikace běží nad HotSpotem a na 64 bitové architektuře. Pro takový seznam se alokuje minimálně 1002 objektů. Každý objekt obsahuje hlavičku (mark word 8 bajtů + klass pointer 8 bajtů; dohromady 16 bajtů). Instance ArrayList se bude skládat z hlavičky, pole, jenž obsahuje minimálně 1000 referencí, a nějakých dalších dat (minimálně 16 bajtů (hlavička) + 8 bajtů (reference na pole) + 8016 bajtů (pole referencí + hlavička) + velikost dalších dat). Pro každý int se vytvoří objekt, který AFAIK zabere min. 20 bajtů (nevím, zda JVM má nějakou minimální velikost objektů; pokud ano, může to být více). Tj. dohromady to zabere minimálně 8040 + 20 * 1000 = 28032 bajtů.

Analogická věc v .NET Core zabere zhruba 4040 bajtů + velikost dalších dat ve třídě List. Na rozdíl od Javy, kde se alokovalo 1002 objektů se zde alokují pouze 2 objekty (instance generické třídy List a pole).

kdežto v Jave sa sústredím na problém a vyriešim ho tým oveľa rýchlejšie, priamočiarejšie a keďže pri tom nemusím používať všelijaké hacky, tak aj prehľadnejšie pre ostatných.

A proč se to tedy plánuje přidat do Javy?

atarist

Re:Paměťová a výpočetní náročnost JVM vs .NET
« Odpověď #138 kdy: 20. 09. 2016, 23:00:49 »
Tak toto je absolútna <|>vina. Referencie aj objekty sú v Random Access Memory, t.j. v pamäti s ľubovoľným prístupom. Moderné operačné systémy používajú virtuálnu adresáciu pamäte a nikto nezaručí, že časť poľa hodnôt bude v jednej časti fyzickej pamäte a druhá časť nebude celkom inde. Samozrejme platí, že pre procesor sú dáta najrýchlejšie prístupné v poradí register -> L1 ->

Asi vím, jaks to myslel, ale to co jsi napsal není pravda. Paměť není "RAM" (v původním významu toho slova) asi tak 25 let, stejně jako SSD není žádný disk :-) Podívej, jak je organizovaná L1 a L2 cache, jak dlouhé jsou cache line a zejména kolik jich je. To je omezující faktor, který se každou další dereferencí zhoršuje a zhoršuje, což je snadno měřitelné. Ano, nikdo nezaručí, že přečteš 100 MB pole tak, že budeš mít vše dopředu v cache, ale co bude u pole struktur/primitivních hodnot zaručeno je, že jak se určitý prvek dostane do cache line, budou tam i ty následující prvky. U pole referencí máš jen jistotu, že čtení dalších N referencí (ne hodnot!) bude možná v cache, možná ale taky ne, protože se to přeplácne načítanými strukturami.

zboj

  • *****
  • 1 507
    • Zobrazit profil
    • E-mail
Re:Paměťová a výpočetní náročnost JVM vs .NET
« Odpověď #139 kdy: 20. 09. 2016, 23:01:08 »
Už mě to moc nebaví, takže jenom krátce.

Vytvoření pole objektů není žádná optimalizace, je to přímočarý nativní přístup.

Vytvoření pole referencí na objekty je javovina, nic to nepřináší (kromě zjednodušení garbage collectoru), co se týká přístupu do paměti je to neoptimální. Většinou ta neoptimálnost nevadí, pokud ano, je to v Javě problém, protože to nejde normálně vyřešit, musí se to hackovat přes pole primitivních typů, což úplně rozbije datový model a kód v aplikaci.

Nazývat převedení pole referencí na pole objektů předčasnou optimalizací je úplně mimo, pole objektů by měl být nativní přístup, ale není, protože Java...
Tak toto je absolútna <|>vina. Referencie aj objekty sú v Random Access Memory, t.j. v pamäti s ľubovoľným prístupom. Moderné operačné systémy používajú virtuálnu adresáciu pamäte a nikto nezaručí, že časť poľa hodnôt bude v jednej časti fyzickej pamäte a druhá časť nebude celkom inde. Samozrejme platí, že pre procesor sú dáta najrýchlejšie prístupné v poradí register -> L1 -> L2 -> L3 -> RAM -> SWAP. Platí že je vyššia pravdepodobnosť, že pole hodnôt bude bližšie k procesoru (L1, L2,L3) ako keď pôjde o pole referencií, čím by výpočet teoreticky mohol prebehnúť rýchlejšie.
Keďže ale na serveri beží paralelne oveľa viac procesov ako je jadier procesorov, (na mojom serveri je to napríklad teraz 4 jadrá/211 procesov), procesy sa na jednom jadre prepínajú (o to sa stará OS), L1,L2 a L3 sa vyprázdňuje a načítava podľa potreby procesov (o to sa stará správca cache v procesore), pravdepodobnosť toho, že pole hodnôt zostane v cache počas celej práce s poľom je takmer nulová a celá snaha o takúto optimalizáciu nemá žiaden zmysel.
Oveľa väčší zmysel ako sa snažiť o ušetrenie pár nanosekúnd v prístupe k RAM je venovať sa problému, analyzovať ho, zjednodušiť a skrátiť a zrýchliť tým výpočet.
Z celej tejto diskusie mi vyplýva, že pri programovaní v C# sa musí hlavne všetko optimalizovať (keďže to nezvládne za neho OS) a celý jazyk je ako trošku lepší assembler, kdežto v Jave sa sústredím na problém a vyriešim ho tým oveľa rýchlejšie, priamočiarejšie a keďže pri tom nemusím používať všelijaké hacky, tak aj prehľadnejšie pre ostatných.
Celá diskuse začala tím, že R. Miček tvrdil, že Java zbytečně zatíží GC, protože nemá hodnotové typy, na rozdíl od C#, ve kterém to tedy bude efektivnější paměťově (ušetří se práce GC). Což je bezezbytku pravda. Zbytek jsou jen žvásty, co se nabalily jako sněhová koule na zpočátku věcnou a užitečnou radu.


gl

Re:Paměťová a výpočetní náročnost JVM vs .NET
« Odpověď #140 kdy: 20. 09. 2016, 23:45:18 »
Už mě to moc nebaví, takže jenom krátce.

Vytvoření pole objektů není žádná optimalizace, je to přímočarý nativní přístup.

Vytvoření pole referencí na objekty je javovina, nic to nepřináší (kromě zjednodušení garbage collectoru), co se týká přístupu do paměti je to neoptimální. Většinou ta neoptimálnost nevadí, pokud ano, je to v Javě problém, protože to nejde normálně vyřešit, musí se to hackovat přes pole primitivních typů, což úplně rozbije datový model a kód v aplikaci.

Nazývat převedení pole referencí na pole objektů předčasnou optimalizací je úplně mimo, pole objektů by měl být nativní přístup, ale není, protože Java...
Tak toto je absolútna <|>vina. Referencie aj objekty sú v Random Access Memory, t.j. v pamäti s ľubovoľným prístupom. Moderné operačné systémy používajú virtuálnu adresáciu pamäte a nikto nezaručí, že časť poľa hodnôt bude v jednej časti fyzickej pamäte a druhá časť nebude celkom inde. Samozrejme platí, že pre procesor sú dáta najrýchlejšie prístupné v poradí register -> L1 -> L2 -> L3 -> RAM -> SWAP. Platí že je vyššia pravdepodobnosť, že pole hodnôt bude bližšie k procesoru (L1, L2,L3) ako keď pôjde o pole referencií, čím by výpočet teoreticky mohol prebehnúť rýchlejšie.
Keďže ale na serveri beží paralelne oveľa viac procesov ako je jadier procesorov, (na mojom serveri je to napríklad teraz 4 jadrá/211 procesov), procesy sa na jednom jadre prepínajú (o to sa stará OS), L1,L2 a L3 sa vyprázdňuje a načítava podľa potreby procesov (o to sa stará správca cache v procesore), pravdepodobnosť toho, že pole hodnôt zostane v cache počas celej práce s poľom je takmer nulová a celá snaha o takúto optimalizáciu nemá žiaden zmysel.
Oveľa väčší zmysel ako sa snažiť o ušetrenie pár nanosekúnd v prístupe k RAM je venovať sa problému, analyzovať ho, zjednodušiť a skrátiť a zrýchliť tým výpočet.
Z celej tejto diskusie mi vyplýva, že pri programovaní v C# sa musí hlavne všetko optimalizovať (keďže to nezvládne za neho OS) a celý jazyk je ako trošku lepší assembler, kdežto v Jave sa sústredím na problém a vyriešim ho tým oveľa rýchlejšie, priamočiarejšie a keďže pri tom nemusím používať všelijaké hacky, tak aj prehľadnejšie pre ostatných.

Naměřil jsem víc než dvojnásobný rozdíl v rychlosti průchodu polem pro různá pořadí alokace prvků. To chceš vysvětlit jak?

Re:Paměťová a výpočetní náročnost JVM vs .NET
« Odpověď #141 kdy: 21. 09. 2016, 06:58:54 »
To je pravda, ale stále bude velké množství hodnot u sebe.
To je zajímavé, že tohle nepovažujete za platný argument, ale jakmile se vám to hodí, použijete ho také.

Zatímco, když použijete pole referencí, tak zvyšujete spotřebu paměti (v mém příkladu se zvýšila 6x)
To je nesmyslný údaj, zvýšení spotřeby paměti záleží na ukládaných datech a může to být jakékoli kladné racionální číslo.

což mj. znamená, že plýtváte cachí
Neznamená. To je pouze vaše tvrzení plynoucí z toho, že pravděpodobně nevíte, jak moderní CPU pracují.

Dále zvyšujete počet alokovaných objektů, jejichž dosažitelnost bude GC periodicky kontrolovat (je-li délka pole n, počet objektů se zvýší cca n krát) - tj. zbytečná zátěž pro CPU.
Takže na ty vaše objekty v poli se nedá udělat reference? To je ale děsivé omezení a jakýkoli algoritmus, který s tím naprogramujete, je neefektivní…

A proč se to tedy plánuje přidat do Javy?
Protože to tam není.

Re:Paměťová a výpočetní náročnost JVM vs .NET
« Odpověď #142 kdy: 21. 09. 2016, 07:07:17 »
Asi vím, jaks to myslel, ale to co jsi napsal není pravda. Paměť není "RAM" (v původním významu toho slova) asi tak 25 let, stejně jako SSD není žádný disk :-) Podívej, jak je organizovaná L1 a L2 cache, jak dlouhé jsou cache line a zejména kolik jich je. To je omezující faktor, který se každou další dereferencí zhoršuje a zhoršuje, což je snadno měřitelné. Ano, nikdo nezaručí, že přečteš 100 MB pole tak, že budeš mít vše dopředu v cache, ale co bude u pole struktur/primitivních hodnot zaručeno je, že jak se určitý prvek dostane do cache line, budou tam i ty následující prvky. U pole referencí máš jen jistotu, že čtení dalších N referencí (ne hodnot!) bude možná v cache, možná ale taky ne, protože se to přeplácne načítanými strukturami.
Problém vašeho tvrzení je v tom, že ani CPU nevypadá stejně, jako před 25 lety, a neběží na něm jediná úloha, jako před 25 lety.

To měření tady někdo provedl, a zjistil zhoršení o 100 % a o 50 %. Což je něco, čím se normálně není potřeba zabývat.

Navíc většina těch příkladů, které se tady uvádí, se na moderních CPU dá mnohem více optimalizovat tím, že se budou provádět paralelně.

tisnik

Re:Paměťová a výpočetní náročnost JVM vs .NET
« Odpověď #143 kdy: 21. 09. 2016, 09:59:25 »
Asi vím, jaks to myslel, ale to co jsi napsal není pravda. Paměť není "RAM" (v původním významu toho slova) asi tak 25 let, stejně jako SSD není žádný disk :-) Podívej, jak je organizovaná L1 a L2 cache, jak dlouhé jsou cache line a zejména kolik jich je. To je omezující faktor, který se každou další dereferencí zhoršuje a zhoršuje, což je snadno měřitelné. Ano, nikdo nezaručí, že přečteš 100 MB pole tak, že budeš mít vše dopředu v cache, ale co bude u pole struktur/primitivních hodnot zaručeno je, že jak se určitý prvek dostane do cache line, budou tam i ty následující prvky. U pole referencí máš jen jistotu, že čtení dalších N referencí (ne hodnot!) bude možná v cache, možná ale taky ne, protože se to přeplácne načítanými strukturami.
Problém vašeho tvrzení je v tom, že ani CPU nevypadá stejně, jako před 25 lety, a neběží na něm jediná úloha, jako před 25 lety.

To měření tady někdo provedl, a zjistil zhoršení o 100 % a o 50 %. Což je něco, čím se normálně není potřeba zabývat.

Navíc většina těch příkladů, které se tady uvádí, se na moderních CPU dá mnohem více optimalizovat tím, že se budou provádět paralelně.

Souhlas s tim, ze pro vetsinu aplikaci je lepsi, rychlejsi a jaksi prijemnejsi zmena algoritmu nez resit tyto optimalizace (prvni a zasadni optimalizaci je vyber jazyka vhodneho pro danou ulohu a vime, ze to neni snadne z mnoha technologickych i politickych duvodu).

Ad CPU: to je skutecne pravda, ovsem to v tomto kontextu neni az tak relevantni. Dulezitejsi je organizace cache (tu zname) a take to, co se stane v tom horsim pripade, kdy se musi sahat do operacni pameti (RAM). Kdysi - a lidi asi maji nekdy pocit, ze to stale plati - byl cip s SRAM docela jednoduchy, proste se poslala adresa, nastavil se R/W bit a po par taktech se cetlo/zapisovalo. Ostatne CPU byly rychlosti srovnatelny s SRAM, takze dokonce bylo mozne, ze dokud CPU pocital, pristupovala k SRAM jina periferie.

Ale uz DRAM mely rozdeleni CAS/RAS, takze cteni 'radku' je mnohem rychlejsi nez neustale vystavovani jak RAS tak CAS. S SDRAM a burst rezimem je to jeste o obrovsky krok dale a nahodne cteni je strasne drahe (doporucuji se podivat na ten prenosovy protokol) kdezto cteni/zapis cele cache line (64 bajtu atd.) je mnohem rychlejsi. Tudiz nezavisle na CPU proste plati, ze kontinualni pristup do pameti je rychlejsi, klidne i radove.

A ze v CPU bezi vic vlaken? Skutecne ano, ale pokud dochazi k invalidaci vsech cache line po prepnuti vlakna, znamena to s velkou pravdepodobnosti, ze to dalsi vlakno pouzivalo nahodny pristup a za tu chvili behu rozdrbalo zbytek 'systemu' (tedy je to komplikovanejsi diky lokalite L1 a vetsinou globalite L2).

Opakuji - v naproste vetsine pripadu to clovek nemusi az tak moc brat v uvahu, ale reakce na nazor "RAM je random access" musela zaznit, protoze to neni a uz asi nikdy nebude pravda.

NooN

Re:Paměťová a výpočetní náročnost JVM vs .NET
« Odpověď #144 kdy: 21. 09. 2016, 10:39:22 »
Opakuji - v naproste vetsine pripadu to clovek nemusi az tak moc brat v uvahu, ale reakce na nazor "RAM je random access" musela zaznit, protoze to neni a uz asi nikdy nebude pravda.
Zle si vysvetlujes pojem RAM. A pravda to bude vzdy.

tisnik

Re:Paměťová a výpočetní náročnost JVM vs .NET
« Odpověď #145 kdy: 21. 09. 2016, 10:57:20 »
Opakuji - v naproste vetsine pripadu to clovek nemusi az tak moc brat v uvahu, ale reakce na nazor "RAM je random access" musela zaznit, protoze to neni a uz asi nikdy nebude pravda.
Zle si vysvetlujes pojem RAM. A pravda to bude vzdy.

V kontextu toho co resime (efektivita pristupu do pole primo nebo pres reference) to skutecne zadny random access neni. A brano do dusledku, na NUMA architekture muzes mit dokonce i locky pri pokusu o pristup do pameti.

dustin

Re:Paměťová a výpočetní náročnost JVM vs .NET
« Odpověď #146 kdy: 21. 09. 2016, 10:57:58 »
Zle si vysvetlujes pojem RAM. A pravda to bude vzdy.

Hm, tak by ji to asi chtělo přejmenovat.

PetrM

Re:Paměťová a výpočetní náročnost JVM vs .NET
« Odpověď #147 kdy: 21. 09. 2016, 14:17:21 »
Opakuji - v naproste vetsine pripadu to clovek nemusi az tak moc brat v uvahu, ale reakce na nazor "RAM je random access" musela zaznit, protoze to neni a uz asi nikdy nebude pravda.
Zle si vysvetlujes pojem RAM. A pravda to bude vzdy.

RAM je zkratka, která nikdy nedávala smysl. Copak ROM, která má adresovatelný každý slovo, není taky RAM? Na smysl těch zkratek bych se moc nekoukal. Logičtější je ROM a RWM.

A systémová RAM opravdu náhodný přístup nemá ráda. A cokoliv "malýho" je pekelně drahý. Řekněme, že máme DDR s latencí 5-5-5 a chceme vyčíst v 64b systému 8B data. Burst je nastavený na osm slov:
OperaceTaktů
Vystavení RAS1
RAS-CAS Latence5
Vystavení CAS1
CAS-CS latence5
Čtení bloku (8 slov)4
Total16

Takže na vyčtení 8B z DDR SDRAM potřebuju 16 taktů sběrnice s tím, že se ve skutečnosti vyčte 64B dat, ale 56B se zahodí. Když do takové RAM leze člověk postupně s pomocí referencí, tak se může stát, že data načítá furt dokola a zahazuje další prvky, kdežto data v jednom bloku prolítnou sběrnici jak namydlený blesk... Blok 1024B se dá vycucat v 256 cyklech s plným adresováním (RAS i CAS). A když se to pak podrží v cache s přístupem 8B na jeden takt pro zpracování, je to prostě pohodička.