Skip to content

Instantly share code, notes, and snippets.

@leonid-shevtsov
Last active March 12, 2024 21:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save leonid-shevtsov/be7ce04f734fe7bdf07fd00bba832e37 to your computer and use it in GitHub Desktop.
Save leonid-shevtsov/be7ce04f734fe7bdf07fd00bba832e37 to your computer and use it in GitHub Desktop.
# frozen_string_literal: true
require 'json'
SEMI = String.new(';', encoding: Encoding::BINARY)
NEWLINE = String.new("\n", encoding: Encoding::BINARY)
FILENAME = 'measurements.txt'
NUMPARTS = 16
endpos = File.size(FILENAME)
partsize = (endpos / NUMPARTS).ceil
pipe_out, pipe_in = IO.pipe
topfile = File.open(FILENAME, encoding: Encoding::BINARY)
(NUMPARTS - 1).downto(0).map do |part_index| # rubocop:disable Metrics/BlockLength
partstartpos = 0
if part_index.positive?
topfile.seek(endpos - partsize)
topfile.readline(NEWLINE)
partstartpos = topfile.pos
end
partendpos = endpos
endpos = topfile.pos
fork do
pipe_out.close
filepart = File.open(FILENAME, encoding: Encoding::BINARY)
filepart.seek(partstartpos)
stats = Hash.new { |h, k| h[k] = [10_000, -10_000, 0, 0] }
loop do
city = filepart.readline(SEMI)
temp = filepart.readline(NEWLINE).to_f
existing = stats[city]
min, max, sum, count = existing
if temp < min
existing[0] = temp
elsif temp > max
existing[1] = temp
end
existing[2] = sum + temp
existing[3] = count + 1
# endpos should be exact, but still
break if filepart.pos >= partendpos
end
pipe_in.puts JSON.generate(stats)
rescue EOFError
# all good
ensure
filepart.close
end
end
pipe_in.close
stats = {}.merge(*pipe_out.readlines.map { |l| JSON.parse(l) }) do |_, s1, s2|
[
s1[0] < s2[0] ? s1[0] : s2[0],
s1[1] > s2[1] ? s1[1] : s2[1],
s1[2] + s2[2],
s1[3] + s2[3]
]
end
puts JSON.pretty_generate(stats.sort.map do |(city, cstats)|
"#{city[0..-2]}=#{cstats[0]}/#{format('%0.1f', (cstats[2] / cstats[3]))}/#{cstats[1]}"
end)
FILENAME = 'measurements.txt'
NUMPARTS = 8
endpos = File.size(FILENAME)
partsize = (endpos / NUMPARTS).ceil
threads = (NUMPARTS - 1).downto(0).map do |part_index| # rubocop:disable Metrics/BlockLength
filepart = File.open(FILENAME, encoding: Encoding::BINARY)
if part_index.positive?
filepart.seek(endpos - partsize)
filepart.readline("\n")
end
partendpos = endpos
endpos = filepart.pos
Ractor.new(filepart, partendpos) do |filepart, partendpos|
semi = String.new(';', encoding: Encoding::BINARY)
newline = String.new("\n", encoding: Encoding::BINARY)
stats = Hash.new { |h, k| h[k] = [10_000, -10_000, 0, 0] }
loop do
city = filepart.readline(semi)
temp = filepart.readline(newline).to_f
existing = stats[city]
min, max, sum, count = existing
if temp < min
existing[0] = temp
elsif temp > max
existing[1] = temp
end
existing[2] = sum + temp
existing[3] = count + 1
# endpos should be exact, but still
break if filepart.pos >= partendpos
end
stats
rescue EOFError
# all good
ensure
filepart.close
end
end
stats = {}.merge(*threads.map(&:take)) do |_, s1, s2|
[
s1[0] < s2[0] ? s1[0] : s2[0],
s1[1] > s2[1] ? s1[1] : s2[1],
s1[2] + s2[2],
s1[3] + s2[3]
]
end
require 'json'
puts JSON.pretty_generate(stats.sort.map do |(city, cstats)|
"#{city[0..-2]}=#{cstats[0]}/#{format('%0.1f', (cstats[2] / cstats[3]))}/#{cstats[1]}"
end)
SEMI = String.new(';', encoding: Encoding::BINARY)
NEWLINE = String.new("\n", encoding: Encoding::BINARY)
FILENAME = 'measurements.txt'
NUMPARTS = 8
endpos = File.size(FILENAME)
partsize = (endpos / NUMPARTS).ceil
threads = (NUMPARTS - 1).downto(0).map do |part_index| # rubocop:disable Metrics/BlockLength
filepart = File.open(FILENAME, encoding: Encoding::BINARY)
if part_index.positive?
filepart.seek(endpos - partsize)
filepart.readline(NEWLINE)
end
partendpos = endpos
endpos = filepart.pos
Thread.new do
stats = Hash.new { |h, k| h[k] = [10_000, -10_000, 0, 0] }
loop do
city = filepart.readline(SEMI)
temp = filepart.readline(NEWLINE).to_f
existing = stats[city]
min, max, sum, count = existing
if temp < min
existing[0] = temp
elsif temp > max
existing[1] = temp
end
existing[2] = sum + temp
existing[3] = count + 1
# endpos should be exact, but still
break if filepart.pos >= partendpos
end
stats
rescue EOFError
# all good
ensure
filepart.close
end
end
stats = {}.merge(*threads.map(&:value)) do |_city, s1, s2|
[
s1[0] < s2[0] ? s1[0] : s2[0],
s1[1] > s2[1] ? s1[1] : s2[1],
s1[2] + s2[2],
s1[3] + s2[3]
]
end
require 'json'
puts JSON.pretty_generate(stats.sort.map do |(city, cstats)|
"#{city[0..-2]}=#{cstats[0]}/#{format('%0.1f', (cstats[2] / cstats[3]))}/#{cstats[1]}"
end)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment