2015-03-12 5 views
10

Является ли монадическое программирование на Java 8 медленнее? Ниже мой тест (используется правая привязка, которая создает новые экземпляры для каждого вычисления). Настоятельная версия в 1000 раз быстрее. Как программировать monadicaly в Java8, получая сопоставимую производительность?Как программировать monadicaly в Java8, получая сопоставимую производительность?

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

Просто запустите 'work' еще несколько раз. Зарядка лямбда стоит дорого. –

+1

С 1.000.000 прогонов разница больше похожа на 10x медленнее. С 80% времени, проведенного в Main.parseInt (String), почему-то ... –

+0

изменил работу, чтобы вернуть дельта времени. Заработал он за 1 000 000 пробегов и 10 000 000 пробегов. Все еще нашла разницу примерно в 1000. Я схватил только последнюю оценку (когда она должна быть довольно теплой). Вы можете увидеть результаты в микросекундах ниже Func: 57411000 \t Imper: 83000 – jordan3

ответ

9

Хотя я не совсем понимаю ваши усилия – по-видимому, вы используете map для побочных эффектов, и вы действительно не имеют какой-либо альтернатива для получения результата unboxed из любого типа – Я измерил вашу работу над JMH как есть. Ваше использование Random было неправильным, я исправил это. Это код, который я использовал:

@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); 
     } 
    } 
    } 
} 

и это результат:

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 

Так что почти никакой разницы.

+0

«Почти нет разницы»?!? ** Императив на 15-20% быстрее. ** –

+0

@ Anony-Mousse Когда он смотрел с высоты «1000 раз быстрее», «в 0,2 раза быстрее» действительно звучит как «почти без разницы». И даже в абсолютном выражении такая разница была бы практически незначительной в любой практической ситуации. Но поскольку код OP не фокусируется достаточно на различии между идиомами, я бы ожидал, что отношение чистых накладных расходов к шагам привязки будет достаточно высоким: больше, чем 2-3. –

+0

В таких крошечных случаях обычно работает inlining. Вот почему разница не более того. Но в более сложных ситуациях он, к сожалению, может накапливаться. В частности, один раз «Либо», потоки и т. Д. Являются полиморфными или мультиморфными и т. Д. –

 Смежные вопросы

  • Нет связанных вопросов^_^