pi.rb                                                                          Page 1 of 3    1:   #! /usr/bin/env jruby  ...
pi.rb                                                                         Page 2 of 3   66:       # in generatl, Rubyi...
pi.rb                                                                         Page 3 of 3  131:     def postStop  132:    ...
Upcoming SlideShare
Loading in...5
×

Pi

754

Published on

Java to JRuby translation of Akka's first tutorial to compute Pi using Madhava-Leibniz series. (http://akka.io/docs/akka/1.1.2/intro/getting-started-first-java.html)

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
754
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
2
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Pi

  1. 1. pi.rb Page 1 of 3 1: #! /usr/bin/env jruby 2: 3: # First things first. 4: # Load Java integration support 5: require "java" 6: 7: # Add ’lib’ in the same directory as this file to the load path 8: $: << File.join(File.dirname(__FILE__), ’lib’) 9: 10: # Load Java libraries 11: require ’scala-library’ 12: require ’akka/akka-actor-1.1.2’ 13: 14: # Here, we import Java classes so that we don’t have to prefix them 15: # with Java::. 16: java_import ’akka.actor.Actors’ 17: java_import ’akka.actor.ActorRef’ 18: java_import ’akka.actor.UntypedActor’ 19: java_import ’akka.actor.UntypedActorFactory’ 20: java_import ’akka.routing.CyclicIterator’ 21: java_import ’akka.routing.InfiniteIterator’ 22: java_import ’akka.routing.Routing’ 23: java_import ’akka.routing.UntypedLoadBalancer’ 24: # Java’s built-in classes don’t need to be quoted (for a String) 25: java_import java.util.concurrent.CountDownLatch 26: 27: # Convenience method for creating an Actor 28: def actorOf(&code) 29: Actors.actorOf(Class.new do 30: include UntypedActorFactory 31: define_method(:create) do |*args| 32: code[*args] 33: end 34: end.new) 35: end 36: 37: class Calculate; end 38: # Struct.new(...) creates an instance having the instance variables 39: # passed 40: class Work < Struct.new(:start, :nrOfElements); end 41: class Result < Struct.new(:value); end 42: 43: class Worker < UntypedActor 44: # needed by actorOf 45: def self.create(*args) 46: new *args 47: end 48: 49: # define the work 50: def calculatePiFor(start, nrOfElements) 51: # Here, we are using the identity 52: # Pi = 4 * sum_{k=0}^{infty} (-1)^k/(2 k + 1) 53: # We divide the work into chunks of nrOfElements 54: # Enumerable#inject may be a little foreign for Java programmers, but 55: # it can be thought of as a shorthand for looping and re-assigning 56: # the value of the block inside to the memo varialbe ("acc" in this case) 57: # "M...N" means a Range starting at M, ending *1 before* N. 58: ((start * nrOfElements)...((start + 1) * nrOfElements)).inject(0) do |acc,i| 59: acc + 4.0 * (1 - (i.modulo 2) * 2) / (2 * i + 1) 60: end 61: end 62: 63: # message handler 64: def onReceive(message) 65: # examining the class of an object is not very Ruby-esque.
  2. 2. pi.rb Page 2 of 3 66: # in generatl, Rubyists prefer duck typing 67: if message.kind_of? Work 68: work = message 69: 70: # perform the work 71: result = calculatePiFor(work.start, work.nrOfElements) 72: 73: # reply with the result 74: context.replyUnsafe(Result.new(result)) 75: 76: else 77: raise IllegalArgumentException.new "Unknown message [#{message + b}]" 78: end 79: end 80: end 81: 82: class PiRouter < UntypedLoadBalancer 83: attr_reader :seq 84: 85: def initialize(workers) 86: super() # make sure the underlying Java proxy is properly initialized 87: @seq = CyclicIterator.new(workers) 88: end 89: end 90: 91: class Master < UntypedActor 92: def initialize(nrOfWorkers, nrOfMessages, nrOfElements, latch) 93: super() 94: @nrOfMessages, @nrOfElements, @latch = nrOfMessages, nrOfElements, latch 95: @nrOfResults, @pi = 0, 0.0 96: 97: # create the workers 98: workers = java.util.ArrayList.new 99: nrOfWorkers.times { workers << Actors.actorOf(Worker).start } 100: 101: # wrap them with a load-balancing router 102: @router = actorOf { PiRouter.new(workers) }.start 103: end 104: 105: # message handler 106: def onReceive(message) 107: if message.kind_of? Calculate 108: # schedule work 109: @nrOfMessages.times do |start| 110: @router.sendOneWay(Work.new(start, @nrOfElements), context) 111: end 112: 113: # send a PoisonPill to all workers telling them to shut down themselves 114: @router.sendOneWay(Routing::Broadcast.new(Actors.poisonPill)) 115: 116: # send a PoisonPill to the router, telling him to shut himself down 117: @router.sendOneWay Actors.poisonPill 118: elsif message.kind_of? Result # handle result from the worker 119: @pi += message.value 120: @nrOfResults += 1 121: context.stop if @nrOfResults == @nrOfMessages 122: else 123: raise IllegalArgumentException.new "Unknown message [#{message}]" 124: end 125: end 126: 127: def preStart 128: @start = java.lang.System.currentTimeMillis 129: end 130:
  3. 3. pi.rb Page 3 of 3 131: def postStop 132: # tell the world that the calculation is complete 133: puts format("ntPi estimate: tt%sntCalculation time: t%s millis", 134: @pi, (java.lang.System.currentTimeMillis - @start)) 135: @latch.countDown 136: end 137: end 138: 139: 140: class Pi 141: def self.calculate(nrOfWorkers, nrOfElements, nrOfMessages) 142: # this latch is only plumbing to know when the calculation is completed 143: latch = CountDownLatch.new(1) 144: 145: # create the master 146: master = Actors.actorOf do 147: Master.new(nrOfWorkers.to_i, nrOfMessages.to_i, nrOfElements.to_i, latch) 148: end.start 149: master.sendOneWay(Calculate.new) # start the calculation 150: latch.await # wait for master to shut down 151: end 152: end 153: 154: # Ready to do the real work 155: if (ARGV.length < 3) 156: exit "Usage: $0 num_workers num_elements num_messages" 157: end 158: 159: Pi.calculate(*ARGV[0..2])

×