ruby-llvm/ruby-llvm

View on GitHub
samples/factorial.rb

Summary

Maintainability
A
0 mins
Test Coverage
require 'llvm/core'
require 'llvm/execution_engine'
require 'llvm/transforms/scalar'
require "benchmark"

# run this with any argument to get a speed comparison between llvm and different ruby implementations

# Start the "engine" before driving
LLVM.init_jit

# modules hold functions and variables
mod = LLVM::Module.new("Factorial")

mod.functions.add("fac", [LLVM::Int], LLVM::Int) do |fac, n|
  n.name    = "n"
  entry     = fac.basic_blocks.append("entry")
  recur     = fac.basic_blocks.append("recur")
  result    = fac.basic_blocks.append("result")
  n_fac_n_1 = nil # predeclare within function's scope

  entry.build do |b|
    test = b.icmp(:eq, n, LLVM::Int(1), "test")
    b.cond(test, result, recur)
  end

  recur.build do |b|
    n_1       = b.sub(n, LLVM::Int(1), "n-1")
    fac_n_1   = b.call(fac, n_1, "fac(n-1)")
    n_fac_n_1 = b.mul(n, fac_n_1, "n*fac(n-1)")
    b.br(result)
  end

  result.build do |b|
    fac = b.phi(LLVM::Int,
               { entry => LLVM::Int(1),
                 recur => n_fac_n_1 },
               "fac")
    b.ret(fac)
  end
end

mod.verify
mod.dump

engine = LLVM::JITCompiler.new(mod)

def rec_factorial(n)
  if n <= 1
    1
  else
    n * rec_factorial(n - 1);
  end
end
def iter_factorial(n)
  nn = n
  i = n - 1
  while(i > 1) do
    nn *= i
    i -= 1
  end
  return nn;
end
def array_factorial(n)
    (1..n).inject(:*)
end

if( ARGV.length == 0 )
  puts "Single run with 42 = "  + engine.run_function(mod.functions["fac"], 42).to_i.to_s
  exit
end
puts "Times show factorial execution times in milliseconds. Both for llvm and ruby iterative and recusive algorithms"
puts ["Num" , "llvm rec","recursive" ,"iterative","arrar iter."].collect{|u|u.ljust(11)}.join
res = [ 1, 5 , 20 , 50 , 100 , 200 , 500 , 1000 , 2000 , 5000  ].each do |i|
  res = [ ]
  res << Benchmark.realtime {engine.run_function(mod.functions["fac"], i)}
  res << Benchmark.realtime {rec_factorial(i)}
  res << Benchmark.realtime {iter_factorial(i) }
  res << Benchmark.realtime {array_factorial(i) }
  res = [i] + res.collect {|u| ((u*1000)).round(4) }
  res = res.collect {|z| z.to_s.ljust(8)}
  next if i < 10
  puts res.join("   ")
end