2013-06-23 10 views
9

Bir egzersiz olarak, bu Scala ve Java Akka örneklerini Frege bağlantı noktasına götürdüm. İyi çalışıyor olsa da, Scala (540 ms) karşılığı daha yavaş (11s) çalışır. Ben Akka yanlış bir şey yapıyor ya da yavaş olduğu için Frege de tembellik ile ilgili bir şey olduğunuAkka with Frege, Scala karşılığı daha yavaş çalışıyor

module mmhelloworld.akkatutorialfregecore.Pi where 
import mmhelloworld.akkatutorialfregecore.Akka 

data PiMessage = Calculate | 
       Work {start :: Int, nrOfElements :: Int} | 
       Result {value :: Double} | 
       PiApproximation {pi :: Double, duration :: Duration} 

data Worker = private Worker where 
    calculatePiFor :: Int -> Int -> Double 
    calculatePiFor !start !nrOfElements = loop start nrOfElements 0.0 f where 
     loop !curr !n !acc f = if n == 0 then acc 
           else loop (curr + 1) (n - 1) (f acc curr) f 
     f !acc !i = acc + (4.0 * fromInt (1 - (i `mod` 2) * 2)/fromInt (2 * i + 1)) 

    onReceive :: Mutable s UntypedActor -> PiMessage -> ST s() 
    onReceive actor Work{start=start, nrOfElements=nrOfElements} = do 
     sender <- actor.sender 
     self <- actor.getSelf 
     sender.tellSender (Result $ calculatePiFor start nrOfElements) self 

data Master = private Master { 
    nrOfWorkers :: Int, 
    nrOfMessages :: Int, 
    nrOfElements :: Int, 
    listener :: MutableIO ActorRef, 
    pi :: Double, 
    nrOfResults :: Int, 
    workerRouter :: MutableIO ActorRef, 
    start :: Long } where 

    initMaster :: Int -> Int -> Int -> MutableIO ActorRef -> MutableIO UntypedActor -> IO Master 
    initMaster nrOfWorkers nrOfMessages nrOfElements listener actor = do 
     props <- Props.forUntypedActor Worker.onReceive 
     router <- RoundRobinRouter.new nrOfWorkers 
     context <- actor.getContext 
     workerRouter <- props.withRouter router >>= (\p -> context.actorOf p "workerRouter") 
     now <- currentTimeMillis() 
     return $ Master nrOfWorkers nrOfMessages nrOfElements listener 0.0 0 workerRouter now 

    onReceive :: MutableIO UntypedActor -> Master -> PiMessage -> IO Master 
    onReceive actor master Calculate = do 
     self <- actor.getSelf 
     let tellWorker start = master.workerRouter.tellSender (work start) self 
      work start = Work (start * master.nrOfElements) master.nrOfElements 
     forM_ [0 .. master.nrOfMessages - 1] tellWorker 
     return master 
    onReceive actor master (Result newPi) = do 
     let (!newNrOfResults, !pi) = (master.nrOfResults + 1, master.pi + newPi) 
     when (newNrOfResults == master.nrOfMessages) $ do 
      self <- actor.getSelf 
      now <- currentTimeMillis() 
      duration <- Duration.create (now - master.start) TimeUnit.milliseconds 
      master.listener.tellSender (PiApproximation pi duration) self 
      actor.getContext >>= (\context -> context.stop self) 
     return master.{pi=pi, nrOfResults=newNrOfResults} 

data Listener = private Listener where 
    onReceive :: MutableIO UntypedActor -> PiMessage -> IO() 
    onReceive actor (PiApproximation pi duration) = do 
     println $ "Pi approximation: " ++ show pi 
     println $ "Calculation time: " ++ duration.toString 
     actor.getContext >>= ActorContext.system >>= ActorSystem.shutdown 

calculate nrOfWorkers nrOfElements nrOfMessages = do 
    system <- ActorSystem.create "PiSystem" 
    listener <- Props.forUntypedActor Listener.onReceive >>= flip system.actorOf "listener" 
    let constructor = Master.initMaster nrOfWorkers nrOfMessages nrOfElements listener 
     newMaster = StatefulUntypedActor.new constructor Master.onReceive 
    factory <- UntypedActorFactory.new newMaster 
    masterActor <- Props.fromUntypedFactory factory >>= flip system.actorOf "master" 
    masterActor.tell Calculate 
    getLine >> return() --Not to exit until done 

main _ = calculate 4 10000 10000 

Am? Örneğin, loop yerine fold (sıkı kıvrım) Worker.calculatePiFor yerine ilk kez 27s aldım.

Bağımlılıklar: Frege için

  1. Akka yerli tanımları: Akka.fr
  2. Java biz Frege bir sınıf uzatamaz beri Akka sınıfları uzatmak için yardımcı: Actors.java

cevap

6

ben tam olarak aşina değilim Aktörler ile, ancak en sıkı döngünün gerçekten loop olduğunu varsayarak, f işlevini argüman olarak geçirmekten kaçınabilirsiniz.

Birincisi için, geçirilen işlevlerin uygulamaları, gerçek geçirilen işlevin katılığından yararlanamaz. Daha ziyade, kod üretimi, geçirilen işlevin argümanlarını tembel bir şekilde aldığını ve tembel bir sonuç verdiğini muhafazakar bir şekilde varsaymalıdır.

İkincisi, bizim durumumuzda, f'u gerçekten sadece bir kez kullanacaksınız, böylece bir satır içi olsun. (Bu size bağlı makaledeki scala kodunda yapılır nasıl.) Taklit eder sizindir aşağıdaki örnek kodda kuyruk özyineleme için oluşturulan koda

Görünüş:

test b c = loop 100 0 f 
    where 
     loop 0 !acc f = acc 
     loop n !acc f = loop (n-1) (acc + f (acc-1) (acc+1)) f -- tail recursion 
     f x y = 2*x + 7*y 

Biz oraya :

// arg2$f is the accumulator 
arg$2 = arg$2f + (int)frege.runtime.Delayed.<java.lang.Integer>forced(
     f_3237.apply(PreludeBase.INum_Int._minusƒ.apply(arg$2f, 1)).apply(
      PreludeBase.INum_Int._plusƒ.apply(arg$2f, 1) 
     ).result() 
    );  

Sen f tüm argüman expressios da tembel bilgisayarlı neden olur hangi lazily denir buraya bakın. Bunun gerektirdiği yöntem çağrılarının sayısını not edin! İki kapanışları fonksiyonu f yani kutusuz argümanlarla çağrılan, kutulu değerler acc ve Curr ve ardından sonuç hesaplanır ile inşa vardır,

(double)Delayed.<Double>forced(f.apply(acc).apply(curr).result()) 

Bunun anlamı şudur: Senin durumunda kod hala bir şey gibi olmalıdır Ve sonuç bir kez daha kutulu hale gelir, sadece bir sonraki döngü için tekrar (zorlanmadan) kutsanmak için.

Şimdi sadece f geçemiyor ancak doğrudan diyoruz aşağıdakileri karşılaştırmak:

test b c = loop 100 0 
    where 
     loop 0 !acc = acc 
     loop n !acc = loop (n-1) (acc + f (acc-1) (acc+1)) 
     f x y = 2*x + 7*y 

Biz alıyorum:

arg$2 = arg$2f + f(arg$2f - 1, arg$2f + 1); 

çok daha iyi!

 loop n !acc = loop (n-1) (acc + f) where 
     f = 2*x + 7*y 
     x = acc-1 
     y = acc+1 

Ve bu olur: Son olarak, hani hiç bir işlev çağrısı olmadan yapabileceği yukarıda

final int y_3236 = arg$2f + 1; 
final int x_3235 = arg$2f - 1; 
... 
arg$2 = arg$2f + ((2 * x_3235) + (7 * y_3236)); 

bu denemek ve bize ne bildirin. Performanstaki ana güçlenme, f'u geçmemeli, oysaki astar muhtemelen JIT'de yapılacaktır.

fold ile ek maliyet, muhtemelen uygulamadan önce bazı liste oluşturmak zorunda olmanızdır.

+2

Bu harika! Şimdi 1.3 saniyeye düşüyor. Oluşturulan Java kaynağına baktım. Şimdi bir '' 'while''' döngüsüne dönüşüyor. –

İlgili konular