2013-06-23 18 views
9

Như một bài tập, tôi đã thực hiện các ví dụ ScalaJava ví dụ về Akka này để chuyển sang Frege. Trong khi nó hoạt động tốt, nó chạy chậm hơn (11s) so với Scala (540ms) đối tác.Akka với Frege chạy chậm hơn so với đối tác Scala

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 

Tôi đang làm điều gì đó sai với Akka hoặc có liên quan gì đến sự lười biếng trong Frege vì chậm không? Ví dụ: khi tôi bước đầu tiên có fold (lần gấp) thay cho loop trong Worker.calculatePiFor, mất 27 giây.

Dependencies:

  1. Akka định nghĩa bản địa cho Frege: Akka.fr
  2. Java helper để mở rộng các lớp học Akka vì chúng ta không thể mở rộng một lớp trong Frege: Actors.java

Trả lời

6

Tôi không hẳn là quen thuộc với các diễn viên, nhưng giả sử rằng vòng lặp chặt chẽ nhất thực sự là loop, bạn có thể tránh hàm chuyển ngang f làm đối số.

Đối với một, các ứng dụng của các hàm được truyền không thể tận dụng mức độ nghiêm ngặt của hàm truyền thực tế. Thay vào đó, việc tạo mã phải đảm bảo một cách thận trọng rằng hàm được truyền đưa các đối số của nó một cách lười biếng và trả về một kết quả lười biếng.

Thứ hai, trong trường hợp của chúng tôi bạn sử dụng f thực sự chỉ một lần ở đây, vì vậy, người ta có thể nội tuyến nó. (Đây là cách nó được thực hiện trong các mã scala trong bài viết bạn liên kết.)

Nhìn vào mã được tạo cho đệ quy đuôi trong mã mẫu sau bắt chước bạ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 

Chúng tôi đạt được điều đó :

// 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() 
    );  

Bạn thấy ở đây rằng f được gọi là lười biếng mà gây ra tất cả các expressios lập luận để cũng được tính lười biếng. Lưu ý số lượng các cuộc gọi phương thức này yêu cầu! Trong trường hợp của bạn mã vẫn có cái gì đó như:

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

Điều này có nghĩa, hai đóng cửa được xây dựng với các giá trị acc đóng hộp và Curr và sau đó kết quả được tính toán, tức là chức năng f được gọi với các đối số không có hộp bọc và kết quả lại được đóng hộp, chỉ để được mở hộp lại (bắt buộc) cho vòng lặp tiếp theo.

Bây giờ so sánh sau đây, nơi chúng tôi chỉ không vượt qua f nhưng gọi nó là trực tiếp:

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 

Chúng tôi nhận được:

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

Tốt hơn nhiều! Cuối cùng, trong trường hợp trên chúng ta có thể làm mà không có một chức năng gọi tại tất cả:

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

Và đây được:

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

Vui lòng thử này ra và cho chúng tôi biết những gì xảy ra. Sự tăng trưởng chính trong hiệu suất nên đến từ không vượt qua f, trong khi nội tuyến có thể sẽ được thực hiện trong JIT anyway.

Chi phí bổ sung với fold có thể do bạn cũng phải tạo một số danh sách trước khi áp dụng.

+2

Điều đó thật tuyệt vời! Nó giảm xuống còn 1,3 giây. Tôi đã xem xét nguồn Java đã tạo. Bây giờ nó biến thành một vòng lặp '' 'while'''. –

Các vấn đề liên quan