bpvickers/csv_decision

View on GitHub
benchmarks/rufus_decision.rb

Summary

Maintainability
A
0 mins
Test Coverage
# frozen_string_literal: true

require 'benchmark/ips'
require 'benchmark/memory'
require 'rufus/decision'

require_relative '../lib/csv_decision'

SPEC_DATA_VALID ||= File.join(CSVDecision.root, 'spec', 'data', 'valid')

CSV_OPTIONS = { regexp_implicit: true }.freeze
RUFUS_OPTIONS = { open_uri: false, ruby_eval: false }.freeze

benchmarks = [
  {
    name: 'String compares only with index',
    data: 'index_example.csv',
    input: { 'topic' => 'politics', 'region' => 'Arctic' },
    # Expected results for first_match and accumulate
    first_match: { 'team_member' => 'Henry' },
    accumulate: { 'team_member' => ['Henry', 'Zach'] }
  },
  {
    name: 'String compares only',
    data: 'simple_example.csv',
    input: { 'topic' => 'culture', 'region' => 'America' },
    # Expected results for first_match and accumulate
    first_match: { 'team_member' => 'Zach' },
    accumulate: { 'team_member' => 'Zach' }
  },
  {
    name: 'Regular expressions',
    data: 'benchmark_regexp.csv',
    input: { 'age' => '40', 'trait' => 'cheerful' },
    # Expected results for first_match and accumulate
    first_match: { 'salesperson' => 'Swanson' },
    accumulate: { 'salesperson' => %w[Swanson Korolev] }
  }
].freeze

tag_width = 70

puts ""
puts "Benchmarking Decisions per Second"
puts '=' * tag_width
puts ""

# First match true and false run options
[true, false].each do |first_match|
  puts "Table Decision Option: first_match: #{first_match}"
  puts '-' * tag_width

  csv_options = CSV_OPTIONS.merge(first_match: first_match)
  rufus_options = RUFUS_OPTIONS.merge(first_match: first_match, accumulate: !first_match)

  benchmarks.each do |test|
    name = test[:name]
    data = Pathname(File.join(SPEC_DATA_VALID, test[:data]))

    rufus_table = Rufus::Decision::Table.new(data.to_s, rufus_options)
    csv_table = CSVDecision.parse(data, csv_options)

    # Prepare input hash
    input = test[:input].deep_dup
    input_symbolized = input.symbolize_keys

    # Test expected results
    expected = first_match ? test[:first_match] : test[:accumulate]

    result = rufus_table.transform(input)

    unless result.slice(*expected.keys).eql?(expected)
      raise "Rufus expected results check failed for test: #{name}"
    end

    result = csv_table.decide!(input_symbolized)

    unless result.eql?(expected.symbolize_keys)
      raise "CSV Decision expected results check failed for test: #{name}"
    end

    Benchmark.ips do |x|
      GC.start
      x.report("CSV decision   (first_match: #{first_match}) - #{name}: ") do |count|
        count.times { csv_table.decide!(input_symbolized) }
      end

      GC.start
      x.report("Rufus decision (first_match: #{first_match}) - #{name}: ") do |count|
        count.times { rufus_table.transform!(input) }
      end

      x.compare!
    end
  end
end

puts ""
puts "Benchmarking Memory"
puts '=' * tag_width
puts ""

def benchmark_memory(test, quiet: false)
  name = test[:name]
  data = Pathname(File.join(SPEC_DATA_VALID, test[:data]))
  file_name = data.to_s

  rufus_tables = {}
  csv_tables = {}
  key = File.basename(file_name, '.csv').to_sym

  Benchmark.memory(quiet: quiet) do |x|
    GC.start
    x.report("Rufus new table - #{name}        ") do
      rufus_tables[key] = Rufus::Decision::Table.new(file_name, RUFUS_OPTIONS)
    end

    GC.start
    x.report("CSV Decision new table - #{name} ") do
      csv_tables[key] = CSVDecision.parse(data, CSV_OPTIONS)
    end

    x.compare!
  end
end

# Warmup
benchmarks.each { |test| benchmark_memory(test, quiet: true) }

# Run the test
benchmarks.each { |test| benchmark_memory(test, quiet: false) }

puts ""
puts "Benchmarking Table Loads per Second"
puts '=' * tag_width
puts ""

benchmarks.each do |test|
  name = test[:name]
  data = Pathname(File.join(SPEC_DATA_VALID, test[:data]))
  file_name = data.to_s

  Benchmark.ips do |x|
    GC.start
    x.report("CSV new table   - #{name}: ") do |count|
      count.times { CSVDecision.parse(data) }
    end

    GC.start
    x.report("Rufus new table - #{name}: ") do |count|
      count.times { Rufus::Decision::Table.new(file_name, RUFUS_OPTIONS) }
    end

    x.compare!
  end
end