Fórum Root.cz
Hlavní témata => Vývoj => Téma založeno: Zelenac 19. 03. 2016, 13:00:43
-
Jak uz zde nekteri mi priznivci vedi, mam rad eventy a Javu, a tak jsem si zkousel zmerit rychlost standardnich konstrukci s temi lamdovskymi a ve vsech pripadech mi vyslo, ze lambdy jsou pomalejsi, v nekterych pripadech az nekolikanasobne. A ja blahovy jsem si myslel, ze lambdy v pozadi konecne funguji jako primy vstup do metody/funkce, a ono zrejme ne. Dela se z nich az moc velke terno ale je to fakt slabota. Zejo?
-
package main;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BinaryOperator;
class Parameters {
public final int p1;
public final int p2;
public Parameters(int p1, int p2) {
this.p1 = p1;
this.p2 = p2;
}
}
class Main {
static final long SIZE = 50000000;
static long tsuma = 0;
public static void sum(long val) {
tsuma += val;
}
public static int vypocet(Parameters params) {
return params.p1 * params.p2;
}
public static void main(String[] args) {
List<Long> list = new ArrayList<>();
for(long i = 0; i < SIZE/3; i++ ) {
list.add(i);
}
long timer;
long supp;
/**
* Obycejny se statickou metodou
*/
timer = System.nanoTime();
for(int i = 0; i < list.size(); i++)
sum(list.get(i));
timer = System.nanoTime() - timer;
System.out.println(timer + " " + tsuma);
/*
* Lambda se statickou metodou
*/
tsuma = 0;
timer = System.nanoTime();
list.forEach( (value) -> sum(value) );
timer = System.nanoTime() - timer;
System.out.println(timer +" " + tsuma);
/**
* Lambda se streamem
*/
supp = 0;
timer = System.nanoTime();
supp = list.stream().mapToLong(value -> value).reduce(1, (a, b) -> a += b );
timer = System.nanoTime() - timer;
System.out.println(timer + " " + supp);
/**
* Volani pres new bez lambdy
*/
supp = 0;
timer = System.nanoTime();
for(int i = 0; i < SIZE; i++) {
supp += vypocet(new Parameters(i, i));
}
timer = System.nanoTime() - timer;
System.out.println(timer + " " + supp);
/**
* Lambda
*/
BinaryOperator<Integer> op = (a, b) -> { return a * b; };
supp = 0;
timer = System.nanoTime();
for(int i = 0; i < SIZE; i++) {
supp += op.apply(i, i);
}
timer = System.nanoTime() - timer;
System.out.println(timer + " " + supp);
}
}
výstup:
35440642 ns
77238271 ns
40472188 ns
37840783 ns
1874345240 ns //zatracene pomala lambda
-
- takhle se mikrobenchmarky nikdy nemohou psat
- pokud te prekvapuje, ze netere kusy tohohle kodu budou pomalejsi, nez jine, tak se zamysli nad tim, jak se to preklada. (aha "A ja blahovy jsem si myslel, ze lambdy v pozadi konecne funguji jako primy vstup do metody/funkce" rika vsechno)
-
Dle speciální matematicko-redukční metody vyplynulo, že s benchmarky napsanými takhle pod sebeou se vejdu do tolerance +-0.3569991% relativně k benchmarkům spouštěným zvlášť. [1]
[1] Jan Amos Komínský, Highest Computer Scientist Methodology in Java, 2016, CERN
-
Dle speciální matematicko-redukční metody vyplynulo, že s benchmarky napsanými takhle pod sebeou se vejdu do tolerance +-0.3569991% relativně k benchmarkům spouštěným zvlášť. [1]
[1] Jan Amos Komínský, Highest Computer Scientist Methodology in Java, 2016, CERN
Tak sis zatroloval a s tim bychom to mohli asi uzavrit...
-
Jak uz zde nekteri mi priznivci vedi, mam rad eventy a Javu, a tak jsem si zkousel zmerit rychlost standardnich konstrukci s temi lamdovskymi a ve vsech pripadech mi vyslo, ze lambdy jsou pomalejsi, v nekterych pripadech az nekolikanasobne. A ja blahovy jsem si myslel, ze lambdy v pozadi konecne funguji jako primy vstup do metody/funkce, a ono zrejme ne. Dela se z nich az moc velke terno ale je to fakt slabota. Zejo?
Změň jazyk ;)
-
Jak uz zde nekteri mi priznivci vedi, mam rad eventy a Javu, a tak jsem si zkousel zmerit rychlost standardnich konstrukci s temi lamdovskymi a ve vsech pripadech mi vyslo, ze lambdy jsou pomalejsi, v nekterych pripadech az nekolikanasobne. A ja blahovy jsem si myslel, ze lambdy v pozadi konecne funguji jako primy vstup do metody/funkce, a ono zrejme ne. Dela se z nich az moc velke terno ale je to fakt slabota. Zejo?
Změň jazyk ;)
Tak určitě, ať zahodí ten nejlepší a jde do nějakého horšího. Takhle se to nedělá. Nejdříve se naučím jeden dobře a až pak se dívám jinam. Hlavně měl štěstí, protože si vybral zrovna to nejlepší. Jen tomu musí věnovat pár let, aby se dostal na trochu lepší úroveň.
-
Zacni pouzivat C#, jediny jazyk, ktery ma vsechno smysluplne.
-
Jak uz zde nekteri mi priznivci vedi, mam rad eventy a Javu, a tak jsem si zkousel zmerit rychlost standardnich konstrukci s temi lamdovskymi a ve vsech pripadech mi vyslo, ze lambdy jsou pomalejsi, v nekterych pripadech az nekolikanasobne. A ja blahovy jsem si myslel, ze lambdy v pozadi konecne funguji jako primy vstup do metody/funkce, a ono zrejme ne. Dela se z nich az moc velke terno ale je to fakt slabota. Zejo?
Změň jazyk ;)
Tak určitě, ať zahodí ten nejlepší a jde do nějakého horšího. Takhle se to nedělá. Nejdříve se naučím jeden dobře a až pak se dívám jinam. Hlavně měl štěstí, protože si vybral zrovna to nejlepší. Jen tomu musí věnovat pár let, aby se dostal na trochu lepší úroveň.
Nikdo neříkal, ať zahodí nejlepší. Řeč byla o Javě ;)
-
To právě nechápu. Stačí ji umět a nic lepšího nenajdeš. Proto je Java taky všude, že jo.
-
To právě nechápu. Stačí ji umět a nic lepšího nenajdeš. Proto je Java taky všude, že jo.
No to bych nerekl. Java je vsude, protoze je to slusne navrzeny mainstream, co mel stesti. Ale ze by to bylo "nic lepsiho nenajdes", tak to urcite ne. Uz jenom, pokud zustaneme na JVM, je pro nektere veci (unittesty se Spockem jsou pekny priklad) lepsi Groovy, Kotlin zakulatil syntaxi, nektere veci lepe napises ve Scale...
-
Je tam nekolik problemu:
JIT optimalizuje az po nekolika (tisicich) pusteni dane funkcr. Proto je vhodne zvolit nejaky microbenchmark tool - JHM treba a korektne napsat benchmarky.
Zalezi jak se napise kod. Vytvaret lambdu pro kazdy prvek, kdyz se vlasyne nemeni, je zcela zbytecne, navic by se o to mozna prave postaral JIT. I tak normalne by to nevadilo, ale v takovem umelem benchmatk se to pochopitelne projevi.
Posledni priklad - letmo - vytvari pro kazde volani 3x Integer class. Zatimco u for verze kompolator nejspis zcela vyhodi ten temporary objekt ci ho da na stack, takze vysledkem bude zcela primy preklad pres par scitani.
Je treba rict, ze lambda je ve sve podstate virtualni callback, ktery negativne ovlivnuje moznosti optimalizace. Bezne to nevadi, bo moderni SW je na tom postaveny, ale v pripade jednoduchych matematickych smycek typu secti mi pole cisel to neni nejlepsi varianta.
Ale jeste jednou viz vyse - dost mozna cast vyresi JIT po vice pruchodech.
-
nezdrzuje to nahodou ten autoboxing/unboxing?
-
Zkus si ten kod vyhodit do dalsi metody a tu spoustet n-krat (kde n bude dostatecne velke cislo), pak si udelej tabulku s casy, jestli (a ktere) se zlepsi atd. Protoze JIT tady funguje mozna trosku jinak nez ocekavas.
Taky je zajimavy se mrknout na vygenerovany bajtkod, ono tam bude (jak pise ziktofel) dost operaci s auto(un)boxingem. To sice JIT taky pochopi a odstrani, ale musi se napred spustit, aby dostal sanci :)
-
Takže ne lambda, ale java jitr slow as fak. No a co lambdy a JavaFX eventy, tam mi to vylozene prijde ze eventy ve swingu jsou praktičtější, proste dam addListener(new Handler(){..} ) a prepisu si eventy na ktere chci reagovat v jedinem addListenerovi, navic je to i prenositelnejs na starsi jdk.
-
Pokud zůstaneme jenom u posledních dvou příkladů, tak drobnou změnou lze dostat výrazně jiné výsledky:
řádku
supp += vypocet(new Parameters(i, i));
nahradíme
supp += Integer.valueOf(i) * Integer.valueOf(i);
Výsledek měření:
1 106 232 471 ns
1 286 946 604 ns
Jde o to, že v případě ručního boxingu pomocí třídy Parameters se alokuje jeden objekt za iteraci, zatímco u autoboxingu se alokují objekty dva. Použití Parameters je díky tomu skoro 2x rychlejší:
707 933 084 ns
1 256 313 255 ns
V předcházejících třech případech se provádí jenom unboxing, takže jsou pochopitelně rychlejší.
-
Takže ne lambda, ale java jitr slow as fak. No a co lambdy a JavaFX eventy, tam mi to vylozene prijde ze eventy ve swingu jsou praktičtější, proste dam addListener(new Handler(){..} ) a prepisu si eventy na ktere chci reagovat v jedinem addListenerovi, navic je to i prenositelnejs na starsi jdk.
Nee, JIT ma urcite vlastnosti, kdy se spusti a jak funguje. Pokud se navic napise neekvivalentni kod (coz ta posledni cast je), tak se to proste nedava dobre porovnavat.
Spis se zeptam - je to problem v realne aplikaci, nebo Te zajima, proc je to skutecne pomale? V prvnim pripade - uz to neres, sam vidis, ze prvni reseni je rychlejsi, problem solved, muzes resit dalsi problemy. Pokud je to druhy priklad, tak je skutecne dobre se podivat na ten bajtkod.
-
A ještě dodatek: I použití lambdy jde výrazně zrychlit použitím neboxující lambdy:
Pokud místo
BinaryOperator<Integer> op = (a, b) -> a * b;
použijeme
IntBinaryOperator op = (a, b) -> a * b;
tak dostaneme časy:
718 975 989 ns
382 852 492 ns
Je ovšem otázka, jestli v reálném softwaru je takovýto rozdíl v performance skutečným problémem či ne, a zda znečištění javovského API explozí primitivních variant všech funkcionálních interfaců stálo za to.
-
Jak uz zde nekteri mi priznivci vedi, mam rad eventy a Javu, a tak jsem si zkousel zmerit rychlost standardnich konstrukci s temi lamdovskymi a ve vsech pripadech mi vyslo, ze lambdy jsou pomalejsi, v nekterych pripadech az nekolikanasobne. A ja blahovy jsem si myslel, ze lambdy v pozadi konecne funguji jako primy vstup do metody/funkce, a ono zrejme ne. Dela se z nich az moc velke terno ale je to fakt slabota. Zejo?
Změň jazyk ;)
Tak určitě, ať zahodí ten nejlepší a jde do nějakého horšího. Takhle se to nedělá. Nejdříve se naučím jeden dobře a až pak se dívám jinam. Hlavně měl štěstí, protože si vybral zrovna to nejlepší. Jen tomu musí věnovat pár let, aby se dostal na trochu lepší úroveň.
Nezapomeň na domácí úkoli a ten test z matematiky příští pondělí synu.
-
http://turnoff.us/geek/a-java-nightmare/