1
0
parallel-programming-labs/ruby-egzas/main.rb
2023-12-30 03:46:41 +02:00

221 lines
5.4 KiB
Ruby

require "json"
$VERBOSE = nil # Suppress verbose warnings
# Define a class for data entries
class DataEntry
attr_reader :name
attr_reader :sugar
attr_reader :criteria
def initialize(name, sugar, criteria)
@name = name
@sugar = sugar
@criteria = criteria
end
def to_s
"#<DataEntry:#{@name}, #{@sugar}, #{@criteria}>"
end
end
# Define a class for result entries
class ResultEntry
attr_reader :name
attr_reader :sugar
def initialize(name, sugar)
@name = name
@sugar = sugar
end
def to_s
"#<ResultEntry:#{@name}, #{@sugar}>"
end
end
# Define a class for input messages
class InputMessage
attr_reader :data
def initialize(data)
@data = data
end
end
# Define a class for result messages
class ResultMessage
attr_reader :tid
attr_reader :data
def initialize(tid, data)
@tid = tid
@data = data
end
end
# Define a class for finished messages
class FinishedMessage
attr_reader :tid
def initialize(tid)
@tid = tid
end
end
# Define a class for final results messages
class FinalResultsMessage
attr_reader :data
def initialize(data)
@data = data
end
end
# Method to read data from a file and convert it to DataEntry objects
def read_data(file_path)
data = []
json_data = File.read(file_path)
JSON.parse(json_data).each do |entry|
data.push(DataEntry.new(entry["name"], entry["sugar"].to_f, entry["criteria"].to_i))
end
return data
end
# Constants
worker_count = 12
data = read_data("IF-1-1_PuzonasR_dat_1.json")
results_path = "results.txt"
logs_path = "logs.txt"
# Method to process data and return a ResultMessage or FinishedMessage
def process_data(tid, data_entry)
if data_entry.sugar > data_entry.criteria
sleep 0.25
ResultMessage.new(tid, ResultEntry.new(data_entry.name, data_entry.sugar))
else
FinishedMessage.new(tid)
end
end
# Create Ractors for workers
workers = Array.new(worker_count) do |index|
Ractor.new(index) do |index|
distributer = Ractor.receive
loop do
data_entry = Ractor.receive
if data_entry == nil then break end
distributer.send(process_data(index, data_entry))
end
end
end
# Create Ractors for result collector, results writer, and logger
result_collector = Ractor.new do
results = []
distributer = Ractor.receive
loop do
result = Ractor.receive
if result == nil then break end
index = results.index { |old_value| old_value.sugar > result.sugar }
if index then
results.insert(index, result)
else
results.push(result)
end
end
distributer.send(FinalResultsMessage.new(results))
end
results_writer = Ractor.new(results_path) do |results_path|
results = Ractor.receive
File.open(results_path, "w") do |file|
file.puts "--------------------------------"
file.puts "| %-15s | %-10s |" % ["Name", "Sugar"]
file.puts "--------------------------------"
results.each do |result|
file.puts "| %-15s | %-10s |" % [result.name, result.sugar]
end
file.puts "--------------------------------"
end
end
logger = Ractor.new(logs_path) do |logs_path|
File.open(logs_path, "w") do |file|
loop do
msg = Ractor.receive
if msg == nil then break end
file.puts msg
end
end
end
# Create a distributer Ractor to manage communication between other Ractors
distributer = Ractor.new(logger, result_collector, results_writer, workers, worker_count) do |logger, result_collector, results_writer, workers, worker_count|
workers.each do |worker|
worker.send(Ractor.current)
end
result_collector.send(Ractor.current)
worker_counter = 0
input_counter = 0
result_counter = 0
workers_active = true
loop do
msg = Ractor.receive
if msg == nil then break end
if workers_active and msg.is_a?(InputMessage) then
tid = input_counter % worker_count
logger.send("[ ->worker#{tid.to_s.ljust(2)} ] Input message, data: #{msg.data}")
workers[tid].send(msg.data)
input_counter += 1
elsif workers_active and msg.is_a?(ResultMessage) then
logger.send("[worker#{msg.tid.to_s.ljust(2)} ->result_collector] Result message, data: #{msg.data}")
result_counter += 1
result_collector.send(msg.data)
elsif workers_active and msg.is_a?(FinishedMessage) then
logger.send("[worker#{msg.tid.to_s.ljust(2)} -> ] Finished message")
result_counter += 1
elsif msg.is_a?(FinalResultsMessage) then
logger.send("[result_collector->result_writer ] Writing results to file")
results_writer.send(msg.data)
else
logger.send("[ -> ] Unknown message")
end
if input_counter == result_counter then
logger.send("[ ->result_collector] Stop message")
result_collector.send(nil)
workers.each_with_index do |worker, tid|
logger.send("[ ->worker#{tid.to_s.ljust(2)} ] Stop message")
worker.send(nil)
end
end
end
logger.send("[ ->logger ] Stop message")
logger.send(nil)
end
# Send input messages to the distributer
data.each do |data_entry|
distributer.send(InputMessage.new(data_entry))
end
# Finalize Ractors by sending stop messages and collecting results
distributer.take
workers.each(&:take)
result_collector.take
results_writer.take
logger.take
# when 'worker_count = 1' , time taken: 10.058
# when 'worker_count = 12', time taken: 1.055