2015-03-12 4 views
10

Czy monadyczne programowanie w Javie 8 jest wolniejsze? Poniżej znajduje się mój test (z wyrównaniem do prawej strony) Jest używany, który tworzy nowe instancje dla każdego obliczenia). Wersja imperatywna jest 1000 razy szybsza. Jak programować monadycznie w Javie8, uzyskując porównywalną wydajność?Jak programować monadycznie w Javie8, uzyskując porównywalną wydajność?

Main.java

public class Main { 

    public static void main(String args[]){ 
     Main m = new Main(); 
     m.work(); 
     m.work2(); 
    } 


    public void work(){ 
     final long start = System.nanoTime(); 
     final Either<Throwable,Integer> result = 
       Try(this::getInput).flatMap((s) -> 
       Try(this::getInput).flatMap((s2) -> 
       parseInt(s).flatMap((i) -> 
       parseInt(s2).map((i2) -> 
       i + i2 
       )))); 
     final long end = System.nanoTime(); 
     result.map(this::println).leftMap(this::println); 
     System.out.println((end-start)/1000+"us to execute"); 
    } 

    public void work2(){ 
     Object result; 
     final long start = System.nanoTime(); 
     try { 
      final String s = getInput(); 
      final String s2 = getInput(); 

      final int i = parzeInt(s); 
      final int i2 = parzeInt(s2); 
      result = i + i2; 
     }catch(Throwable t){ 
      result=t; 
     } 
     final long end = System.nanoTime(); 
     println(result); 
     System.out.println((end-start)/1000+"us to execute"); 
    } 

    public <A> A println(final A a){ 
     System.out.println(a); 
     return a; 
    } 

    public String getInput(){ 
     final Integer value = new Random().nextInt(); 
     if(value % 2 == 0) return "Surprise!!!"; 
     return value+""; 
    } 

    public Either<Throwable,Integer> parseInt(final String s){ 
     try{ 
      return Either.right(Integer.parseInt(s)); 
     }catch(final Throwable t){ 
      return Either.left(t); 
     } 
    } 

    public Integer parzeInt(final String s){ 
     return Integer.parseInt(s); 
    } 
} 

Either.java

public abstract class Either<L,R> 
{ 
    public static <L,R> Either<L,R> left(final L l){ 
     return new Left(l); 
    } 

    public static <L,R> Either<L,R> right(final R r){ 
     return new Right(r); 
    } 

    public static<L,R> Either<L,R> toEither(final Optional<R> oR,final L l){ 
     return oR.isPresent() ? right(oR.get()) : left(l); 
    } 

    public static <R> Either<Throwable,R> Try(final Supplier<R> sr){ 
     try{ 
      return right(sr.get()); 
     }catch(Throwable t){ 
      return left(t); 
     } 
    } 

    public abstract <R2> Either<L,R2> flatMap(final Function<R,Either<L,R2>> f); 

    public abstract <R2> Either<L,R2> map(final Function<R,R2> f); 

    public abstract <L2> Either<L2,R> leftMap(final Function<L,L2> f); 

    public abstract Either<R,L> swap(); 

    public static class Left<L,R> extends Either<L,R> { 
     final L l; 

     private Left(final L l){ 
      this.l=l; 
     } 

     public <R2> Either<L,R2> flatMap(final Function<R,Either<L,R2>> f){ 
      return (Either<L,R2>)this; 
     } 

     public <R2> Either<L,R2> map(final Function<R,R2> f){ 
      return (Either<L,R2>)this; 
     } 

     public <L2> Either<L2,R> leftMap(final Function<L,L2> f){ 
      return new Left(f.apply(l)); 
     } 

     public Either<R,L> swap(){ 
      return new Right(l); 
     } 
    } 

    public static class Right<L,R> extends Either<L,R> { 
     final R r; 

     private Right(final R r){ 
      this.r=r; 
     } 

     public <R2> Either<L,R2> flatMap(final Function<R,Either<L,R2>> f){ 
      return f.apply(r); 
     } 

     public <R2> Either<L,R2> map(final Function<R,R2> f){ 
      return new Right(f.apply(r)); 
     } 

     public <L2> Either<L2,R> leftMap(final Function<L,L2> f){ 
      return (Either<L2,R>)this; 
     } 

     public Either<R,L> swap(){ 
      return new Left(r); 
     } 
    } 
} 
+5

Po prostu uruchom 'work' jeszcze kilka razy. Bootstrapping a lambda jest kosztowny. –

+1

Przy 1.000.000 przebiegach różnica jest bardziej 10 razy wolniejsza. Z 80% czasu spędzonego w Main.parseInt (String), z jakiegoś powodu ... –

+0

zmieniono pracę, aby przywrócić deltę czasu. Przebiegłem na 1.000.000 przejazdów i 10.000.000 przejazdów. Nadal stwierdziłem, że różnica wynosi około 1000. Chwyciłem tylko ostatnią ocenę (kiedy powinno być dość ciepło). Możesz zobaczyć wyniki w mikrosekundach poniżej Funk: 57411000 \t Imper: 83000 – jordan3

Odpowiedz

9

A ja nie bardzo rozumiem twój wysiłek – widocznie używasz map na skutki uboczne i tak naprawdę nie ma żadnych alternatywę, aby uzyskać wynik unboxed z dowolnego typu – Zmierzyłem twoją pracę nad JMH takim, jakie jest. Twoje użycie Random było złe, poprawiłem to. Jest to kod użyłem:

@BenchmarkMode(Mode.AverageTime) 
@OutputTimeUnit(TimeUnit.NANOSECONDS) 
@OperationsPerInvocation(Measure.SIZE) 
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) 
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) 
@State(Scope.Thread) 
@Fork(1) 
public class Measure 
{ 
    static final int SIZE = 1; 

    @Benchmark public Either<Throwable, Integer> workMonadically() { 
    final Either<Throwable,Integer> result = 
     Try(this::getInput).flatMap((s) -> 
      Try(this::getInput).flatMap((s2) -> 
       parseInt(s).flatMap((i) -> 
        parseInt(s2).map((i2) -> 
          i + i2 
        )))); 
    return result; 
    } 

    @Benchmark public Object workImperatively() { 
    Object result; 
    try { 
     final String s = getInput(); 
     final String s2 = getInput(); 

     final int i = parzeInt(s); 
     final int i2 = parzeInt(s2); 
     result = i + i2; 
    }catch(Throwable t){ 
     result=t; 
    } 
    return result; 
    } 

    public String getInput() { 
    final Integer value = ThreadLocalRandom.current().nextInt(); 
    if (value % 2 == 0) return "Surprise!!!"; 
    return String.valueOf(value); 
    } 

    public Either<Throwable,Integer> parseInt(final String s){ 
    try{ 
     return Either.right(Integer.parseInt(s)); 
    }catch(final Throwable t){ 
     return Either.left(t); 
    } 
    } 

    public Integer parzeInt(final String s){ 
    return Integer.parseInt(s); 
    } 

    public static abstract class Either<L,R> 
    { 
    public static <L,R> Either<L,R> left(final L l){ 
     return new Left<>(l); 
    } 

    public static <L,R> Either<L,R> right(final R r){ 
     return new Right<>(r); 
    } 

    public static<L,R> Either<L,R> toEither(final Optional<R> oR,final L l){ 
     return oR.isPresent() ? right(oR.get()) : left(l); 
    } 

    public static <R> Either<Throwable,R> Try(final Supplier<R> sr){ 
     try{ 
     return right(sr.get()); 
     }catch(Throwable t){ 
     return left(t); 
     } 
    } 

    public abstract <R2> Either<L,R2> flatMap(final Function<R,Either<L,R2>> f); 

    public abstract <R2> Either<L,R2> map(final Function<R,R2> f); 

    public abstract <L2> Either<L2,R> leftMap(final Function<L,L2> f); 

    public abstract Either<R,L> swap(); 

    public static class Left<L,R> extends Either<L,R> { 
     final L l; 

     private Left(final L l){ 
     this.l=l; 
     } 

     @Override public <R2> Either<L,R2> flatMap(final Function<R,Either<L,R2>> f){ 
     return (Either<L,R2>)this; 
     } 

     @Override public <R2> Either<L,R2> map(final Function<R,R2> f){ 
     return (Either<L,R2>)this; 
     } 

     @Override public <L2> Either<L2,R> leftMap(final Function<L,L2> f){ 
     return new Left<>(f.apply(l)); 
     } 

     @Override public Either<R,L> swap(){ 
     return new Right<>(l); 
     } 
    } 

    public static class Right<L,R> extends Either<L,R> { 
     final R r; 

     private Right(final R r){ 
     this.r=r; 
     } 

     @Override public <R2> Either<L,R2> flatMap(final Function<R,Either<L,R2>> f){ 
     return f.apply(r); 
     } 

     @Override public <R2> Either<L,R2> map(final Function<R,R2> f){ 
     return new Right<>(f.apply(r)); 
     } 

     @Override public <L2> Either<L2,R> leftMap(final Function<L,L2> f){ 
     return (Either<L2,R>)this; 
     } 

     @Override public Either<R,L> swap(){ 
     return new Left<>(r); 
     } 
    } 
    } 
} 

i jest to wynik:

Benchmark     Mode Cnt  Score  Error Units 
Measure.workImperatively avgt 5 1646,874 ± 137,326 ns/op 
Measure.workMonadically avgt 5 1990,668 ± 281,646 ns/op 

Więc nie ma prawie żadnej różnicy w ogóle.

+0

"Prawie bez różnicy"?!? ** Imperatyw jest o 15-20% szybszy. ** –

+0

@ Anony-Mousse Kiedy spojrzymy z wysokości "1000 razy szybciej", "0.2 razy szybciej" rzeczywiście brzmi jak "prawie bez różnicy". Nawet w kategoriach bezwzględnych różnica taka byłaby praktycznie nieistotna w każdej praktycznej sytuacji. Ale ponieważ kod OP nie skupia się wystarczająco tylko na różnicy między idiomami, spodziewam się, że stosunek czystych narzutów kroków wiążących razem będzie dość wysoki: bardziej jak 2-3. –

+0

W takich drobnych przypadkach zazwyczaj działa podciąganie. Dlatego różnica nie jest niczym więcej. Ale w bardziej złożonych sytuacjach może się niestety nagromadzić. W szczególności raz "albo", strumienie, itp. Są polimorficzne lub wielomorficzne itp. –

Powiązane problemy