~michalr/blog2gmi

d64c12f9240be07f8fa33bef288d0c9a64bc8186 — Michał Rudowicz 3 years ago d07c0ae
map_parallel
5 files changed, 47 insertions(+), 17 deletions(-)

M spec/gmi_renderer_spec.cr
A spec/map_parallel_spec.cr
M spec/spec_helper.cr
M src/main.cr
A src/map_parallel.cr
M spec/gmi_renderer_spec.cr => spec/gmi_renderer_spec.cr +1 -0
@@ 1,4 1,5 @@
require "./spec_helper"
require "../src/gmi_renderer.cr"

def render_to_gmi(input : String)
  GmiRenderer.new.render(Markd::Parser.parse(input))

A spec/map_parallel_spec.cr => spec/map_parallel_spec.cr +21 -0
@@ 0,0 1,21 @@
require "benchmark"

require "./spec_helper"
require "../src/map_parallel"

describe "MapParallel" do
  it "has the same output effect as simple map" do
    input = (0..5000)

    input.map_parallel { |x| x + 1 }.should eq(input.map { |x| x + 1 })
  end

  it "should be faster than running all blocks serially" do
    ts = Time::Span.new(nanoseconds: 10_000)
    executions = 5000
    input = (0..executions)

    Benchmark.realtime { input.map_parallel { |x| sleep(ts) } }
      .should be < (ts*executions*0.5)
  end
end

M spec/spec_helper.cr => spec/spec_helper.cr +0 -1
@@ 1,2 1,1 @@
require "spec"
require "../src/gmi_renderer.cr"

M src/main.cr => src/main.cr +10 -16
@@ 1,24 1,18 @@
require "markd"
require "./gmi_renderer"

wait = Channel(Nil).new

files_to_process = 0
require "./map_parallel"

Dir.new(".").entries
  .select! { |x| /\.md$/ =~ x }
  .tap { |x| files_to_process = x.size }
  .each do |fname|
    spawn do
      rendered = GmiRenderer.new.render(
        Markd::Parser.parse(
          File.read(fname)
        )
  .map_parallel do |fname|
    {fname, GmiRenderer.new.render(
      Markd::Parser.parse(
        File.read(fname)
      )
      puts "======= #{fname} =======\n"
      puts "#{rendered}\n"
      wait.send(nil)
    end
    )}
  end
  .each do |fname, rendered|
    puts "======= #{fname} =======\n"
    puts "#{rendered}\n"
  end

files_to_process.times { wait.receive }

A src/map_parallel.cr => src/map_parallel.cr +15 -0
@@ 0,0 1,15 @@
module Enumerable(T)
  def map_parallel(&block : T -> U) forall U
    wait = Channel(U).new
    num_fibers = 0
    self
      .tap { |x| num_fibers = x.size }
      .each do |x|
        spawn do
          wait.send(block.call(x))
        end
      end
    Array.new(num_fibers, nil)
      .map { |x| wait.receive }
  end
end