Getting started
Outline
We use example/mandelbrot as an example for use of DRbQS. To use DRbQS we needs two files: a file to define class for tasks and a file to define a server. In addition, it is convenient to create a file to define processes of a server and nodes.
Example
Class to calculate tasks: mandelbrot.rb
require 'complex'
class Mandelbrot
def initialize(iterate, threshold)
@iterate = iterate
@threshold = threshold
end
def map(z, c)
z * z + c
end
def iterate_map(z, c, &block)
z_old = z
@iterate.times do |i|
z_new = map(z_old, c)
z_old = z_new
yield(z_old) if block_given?
end
z_old
end
def diverge?(c)
iterate_map(Complex(0.0, 0.0), c) do |z|
if z.abs > @threshold
return true
end
end
false
end
end
class CalcMandelbrot
def initialize(mandelbrot)
@mandelbrot = mandelbrot
end
def calc(io, xrange, yrange, step_size)
xrange.step(step_size) do |x|
yrange.step(step_size) do |y|
c = Complex(x, y)
unless @mandelbrot.diverge?(c)
io.puts "#{c.real}\t#{c.imag}"
end
end
end
end
def calc_save(basename, *args)
file = DRbQS::Temporary.file
open(file, 'w') do |f|
calc(f, *args)
end
DRbQS::Transfer.enqueue(file, compress: true, rename: basename) # Return basename.
end
end
Server: server.rb
require_relative 'mandelbrot.rb'
RESULT_DIR = 'result_mandelbrot'
DRbQS.option_parser("Calculate Mandelbrot set.") do |prs, opts|
prs.on('-s NUM', '--step NUM', Float, 'Set the step size.') do |v|
opts[:step] = v
end
prs.on('-l NUM', '--limit NUM', Float, 'Set the limit to search.') do |v|
opts[:limit] = v
end
prs.on('-i NUM', '--iterate NUM', Integer, 'Set the iterate number.') do |v|
opts[:iterate] = v
end
prs.on('-t NUM', '--threshold NUM', Float, 'Set the threshold value.') do |v|
opts[:threshold] = v
end
end
DRbQS.define_server(file_directory: RESULT_DIR) do |server, argv, opts|
step_size = opts[:step] || 0.1
limit = opts[:limit] || 2.0
iterate = opts[:iterate] || 1000
threshold = opts[:threshold] || 5
mandelbrot = Mandelbrot.new(iterate, threshold)
calc = CalcMandelbrot.new(mandelbrot)
ranges = [[-limit..0, -limit..0, step_size], [-limit..0, 0..limit, step_size],
[0..limit, -limit..0, step_size], [0..limit, 0..limit, step_size]]
ranges.each_with_index do |ranges, i|
args_ary = ["%02d.txt" % i] + ranges
note_str = "#{ranges[0].inspect} #{ranges[1].inspect}"
task = DRbQS::Task.new(calc, :calc_save, args: args_ary, note: note_str) do |srv, result|
DRbQS::Transfer.decompress(srv, result)
end
server.queue.add(task)
end
server.add_hook(:finish) do |srv|
puts "Save results to #{RESULT_DIR}"
result_path = File.join(RESULT_DIR, "result.txt")
Dir.glob(File.join(RESULT_DIR, "*.txt")).sort.each do |path|
open(result_path, 'a+') do |out|
out.print File.read(path)
end
FileUtils.rm(path)
end
end
end
Definition of processes (optional): execute.rb
DIR = File.dirname(__FILE__)
server :local_server, "localhost" do |srv|
srv.load File.join(DIR, 'server.rb')
end
node :local_node do |nd|
nd.load File.join(DIR, 'mandelbrot.rb')
nd.process 2 # For dual core CPU
end
mandelbrot.rb
This file defines class Mandelbrot and CalcMandelbrot. The class CalcMandelbrot and its method calc_save are for DRbQS::Task. In the method calc_save we creates a temporary file by DRbQS::Temporary.file and save results to the file. Then, we rename the temporary file and add it to queue of transferring. The method calc_save returns basename of the file transferred to the server and the returned value will become an argument of hook of the task on the server.
Definition of server
A file for drbqs-server
The command "drbqs-server" takes a ruby file that defines a DRbQS server and gets settings of the server (port number, settings of sftp, and so on) from options of command line. In the definition file we load necessary files to process tasks, set a parser of command line arguments for the server by the method "DRbQS.option_parser", and defines the body of server by the method "DRbQS.define_server".
DRbQS.option_parser
DRbQS.option_parser takes a block that has two arguments. The first argument is a OptionParser object and the second argument is a hash that is used to save data from options of command line.
DRbQS.define_server
The block of DRbQS.define_server takes three arguments. An argument of DRbQS.define_server is the same as DRbQS::Server.new and the first block argument is an object of DRbQS::Server. the second block argument is ARGV parsed by DRbQS.option_parser and the third block argument is the second block argument of DRbQS.option_parser. Using these variables, we can get arguments from command line.
We set tasks on the block of DRbQS.define_server. We create tasks by DRbQS::Task.new and add them to queue of the server as the following
server.queue.add(task)
In the block of DRbQS::Task.new we decompress files transferred from nodes. Then, when server finishes, :finish hook concatenates the files of results.
Execute a server
To execute the server of which port number is 12345, we type the following on terminal.
drbqs-server server.rb -p 12345
We can set options of the server. First, we confirm the options of the server.
drbqs-server server.rb -h
The help messages are displayed.
We set the options of the command drbqs-server, add '--', and set the options of the server.
For example,
drbqs-server server.rb --execute-node 2 -- --limit 3.0 --step 0.1
The option --execute-node is useful, it executes also nodes connecting to the server just after the server runs.
Execute nodes
If the server runs with port 12345, then the command
drbqs-node -l mandelbrot.rb druby://:12345
connects to the server of druby://:12345 and calculate tasks obtained from the server. If we want to execute two node processes then we use either -P or -process option. The command
drbqs-node -P 2 -l mandelbrot.rb druby://:12345
execute two node processes.
If the calculation finishes, we find the directory 'result_mandelbrot' including 'result.txt'.
Execution over SSH
For example, if we want to execute on example.com then we type
drbqs-ssh server [email protected] -d /path/to/mandelbrot -- server.rb -- --limit 3.0 --step 0.1
and
drbqs-ssh node [email protected] -d /path/to/mandelbrot -- -l mandelbrot.rb -P 2
Execute a server and nodes all together
We can execute a server and nods simultaneously as daemon processes. We type the following
drbqs-execute execute.rb