[e-lang] Computing Pi in parallel with several VATs. How to write it well ?

Kevin Reid kpreid at attglobal.net
Sat Jul 23 13:28:49 EDT 2005


On Jul 23, 2005, at 10:29, Kevin Reid wrote:
> On Jul 21, 2005, at 12:25, Pascal Grange wrote:
>> I am new to E and I am seeking advice about good programming in E. 
>> Here is
>> the issue :
> ...
>> So that was for the properties of the algorithm. My way of 
>> implementing it
>> with E is the following : I have several computers (PiComputer), on 
>> different
>> remote VATs. I submit a job (an interval) to compute asynchronously 
>> to each
>> computer. When a computer finished its computation, I add its result 
>> to the
>> current value of Pi being computed and I send the computer a new job. 
>> When
>> there are no more job to do and no computation is being performed, the
>> algorithm stops and displays the value of Pi computed.
> ...
>> This code is too spagheti-like for me but I was not able to do it 
>> best. This
>> spaghetiness is not a fact, just a feeling, for instance the printing 
>> of the
>> result is in the middle of the code, not the end...and so on. I know 
>> that
>> it's asynchronous but I would like to know if a E-programmer would 
>> code
>> that in a drastically different E-way.
>>
>
> This is an interesting problem, and I'm trying to find a nice 
> solution; so far, I have an uglier one.
>
> I'm still trying,

Here's another attempt, which I think is better than my previous one.

This one has the same approach as your original code: setting up a 
'local' process for each computer.

Neither of these handle failure as well as could be done: if one 
'computer' fails permanently (the 'computer' reference becomes broken, 
as opposed to the result of <-compute), it should be discarded and the 
interval given to another computer; overall failure should only occur 
if all computers fail, or consistently fail when asked to compute a 
particular interval.

pragma.enable("easy-return")
pragma.disable("explicit-result-guard")
pragma.enable("easy-when")

def makeComputer() {
   def computer {
     to compute(piece) {
       if (entropy.nextInt(5) == 0) {
         return Ref.broken(`BOOM ($piece)`)
       } else {
         return timer.whenPast(timer.now() + entropy.nextInt(2000),
                               thunk{ "'" <- add(piece) <- add("', ") })
       }
     }
   }
   return computer
}

/** Slightly silly for just this problem, but this seems to be a
     common pattern, so it should be packaged up.

     Returns a 0-parameter function. 'reactor' will be eventually
     invoked exactly once when the function has been called 'n'
     times. */
def makeEventCountdown(var n :(int >= 0), reactor) {
   def test() { if (n <=> 0) { reactor <- () }}
   def countdown() { n -= 1; test() }
   test()
   return countdown
}


def distribute(pieces :Set, seed, computers :Set, traceOut) {

   def [result, resultResolver] := Ref.promise()
   def piecesLeft := pieces.getElements().diverge()
   var accumulation := seed

   def doneAll() {
     traceOut.println("resolving result")
     resultResolver.resolveRace(accumulation)
   }
   def doneOne := makeEventCountdown(piecesLeft.size(), doneAll)

   for i => computer in computers {
     def sendOne() {
       if (piecesLeft.size() > 0 && !Ref.isBroken(accumulation)) {
         def inPiece := piecesLeft.pop()
         traceOut.println(`($i) sending piece $inPiece`)
         when (computer <- compute(inPiece)) \
             -> computed(resultPiece) {
           traceOut.println(`($i) $inPiece => $resultPiece`)
           accumulation := accumulation <- add(resultPiece)
         } catch p {
           traceOut.println(`($i) $inPiece => problem $p`)
           accumulation := Ref.broken(p)
           doneAll()
         } finally {
           doneOne()
           sendOne()
         }
       } else {
         traceOut.println(`($i) quitting`)
       }
     }
     sendOne()
   }

   return result
}


def intervals := ["A", "B", "C", "D", "E", "F", "G"].asSet()
def computers := [makeComputer(), makeComputer(), 
makeComputer()].asSet()

when (distribute(intervals, "", computers, stderr)) \
     -> computed(result) {
   println(`The answer is $result.`)
} catch p {
   println(`Oops, $p.`)
}

-- 
Kevin Reid                            <http://homepage.mac.com/kpreid/>



More information about the e-lang mailing list