Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions benchmarks/seqset-memsize.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# frozen_string_literal: true

$LOAD_PATH.unshift "./lib"
require "net/imap"
require "objspace"

def seqset(n, min: 1, max: (n * 1.25).to_i)
inputs = Array.new(n) { rand(min..max) }
Net::IMAP::SequenceSet[inputs]
end

def obj_tree(obj, seen: Set.new)
seen << obj
children = ObjectSpace.reachable_objects_from(obj)
.reject { _1 in Module or seen.include?(_1) }
.flat_map { obj_tree(_1, seen:) }
[obj, *children]
end

def memsize(obj) = obj_tree(obj).sum { ObjectSpace.memsize_of _1 }

def avg(ary) = ary.sum / ary.count.to_f

def print_avg(n, count: 10, **)
print "Average memsize of SequenceSet with %6d inputs: " % [n]
sizes = Array.new(count) {
print "."
memsize seqset(n, **)
}
puts "%9.1f" % [avg(sizes)]
end

# pp obj_tree(seqset(200, min: 1_000_000, max: 1_000_999)).to_h { [_1, memsize(_1)] }
print_avg 1
print_avg 10
print_avg 100

print_avg 1_000
print_avg 10_000
print_avg 100_000
94 changes: 94 additions & 0 deletions benchmarks/sequence_set-and.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
---
prelude: |
require "yaml"
require "net/imap"

INPUT_COUNT = Integer ENV.fetch("BENCHMARK_INPUT_COUNT", 1000)
MAX_INPUT = Integer ENV.fetch("BENCHMARK_MAX_INPUT", 1400)
WARMUP_RUNS = Integer ENV.fetch("BENCHMARK_WARMUP_RUNS", 200)

SETS = Array.new(1000) {
Net::IMAP::SequenceSet[Array.new(INPUT_COUNT) { rand(1..MAX_INPUT) }]
}

def sets
l, r = SETS.sample(2)
[l.dup, r]
end

class Net::IMAP
class SequenceSet
def and0(other) remain_frozen dup.and0! other end
def and1(other) remain_frozen dup.and1! other end
def and2(other) remain_frozen dup.and2! other end

# L - ~R
def and0!(other)
modifying!
subtract SequenceSet.new(other).complement!
end

# L - (L - R)
def and1!(other)
modifying!
subtract dup.subtract(SequenceSet.new(other))
end

# TODO: add this as a public method
def xor!(other) # :nodoc:
modifying!
copy = dup
other = SequenceSet.new(other)
merge(other).subtract(other.subtract(copy.complement!))
end

# L - (L ^ R)
def and2!(other)
modifying!
subtract SequenceSet.new(other).xor! self
end
end
end

# warmup (esp. for JIT)
WARMUP_RUNS.times do
lhs, rhs = sets
lhs | rhs
lhs & rhs
lhs - rhs
lhs ^ rhs
~lhs
lhs.and0 rhs
lhs.and1 rhs
lhs.and2 rhs
end

benchmark:
" L & R": l, r = sets; l & r
" L - ~R": l, r = sets; l - ~r
"and0 L - ~R": l, r = sets; l.and0 r
"and0! L - ~R": l, r = sets; l.and0! r
" L - (L - R)": l, r = sets; l - (l - r)
"and1 L - (L - R)": l, r = sets; l.and1 r
"and1! L - (L - R)": l, r = sets; l.and1! r
" L - (L ^ R)": l, r = sets; l - (l ^ r)
"and2 L - (L ^ R)": l, r = sets; l.and2 r
"and2! L - (L ^ R)": l, r = sets; l.and2! r

contexts:
- name: local
prelude: |
$LOAD_PATH.unshift "./lib"
require: false
- name: v0.5.9
gems:
net-imap: 0.5.9
require: false
- name: v0.5.0
gems:
net-imap: 0.5.0
require: false
- name: v0.4.21
gems:
net-imap: 0.4.21
require: false
97 changes: 97 additions & 0 deletions benchmarks/sequence_set-new.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
---
prelude: |
require "net/imap"
SeqSet = Net::IMAP::SequenceSet

N_RAND = 100

def rand_nums(n, min: 1, max: (n * 1.25).to_i) = Array.new(n) { rand(1..max) }
def rand_entries(...) = SeqSet[rand_nums(...)].elements.shuffle
def rand_string(...) = SeqSet[rand_nums(...)].string.split(?,).shuffle.join(?,)

def build_string_inputs(n, n_rand, **)
Array.new(n_rand) { rand_string(n, **) }
end

def build_int_inputs(n, n_rand, **)
Array.new(n_rand) { rand_entries(n, **) }
end

inputs = nil
i = 0

# warm up, especially for YJIT
1000.times do
ints = rand_nums(1000)
seqset = SeqSet[ints]
string = seqset.string.split(?,).shuffle.join(?,)
SeqSet[string]
end

benchmark:

- name: n=10 ints
prelude: inputs = build_int_inputs 10, N_RAND
script: SeqSet[inputs[i = (i+1) % N_RAND]]

- name: n=10 string
prelude: inputs = build_string_inputs 10, N_RAND
script: SeqSet[inputs[i = (i+1) % N_RAND]]

- name: n=100 ints
prelude: inputs = build_int_inputs 100, N_RAND
script: SeqSet[inputs[i = (i+1) % N_RAND]]

- name: n=100 string
prelude: inputs = build_string_inputs 100, N_RAND
script: SeqSet[inputs[i = (i+1) % N_RAND]]

- name: n=1000 ints
prelude: inputs = build_int_inputs 1000, N_RAND
script: SeqSet[inputs[i = (i+1) % N_RAND]]

- name: n=1000 string
prelude: inputs = build_string_inputs 1000, N_RAND
script: SeqSet[inputs[i = (i+1) % N_RAND]]

- name: n=10,000 ints
prelude: inputs = build_int_inputs 10_000, N_RAND
script: SeqSet[inputs[i = (i+1) % N_RAND]]

- name: n=10,000 string
prelude: inputs = build_string_inputs 10_000, N_RAND
script: SeqSet[inputs[i = (i+1) % N_RAND]]

- name: n=100,000 ints
prelude: inputs = build_int_inputs 100_000, N_RAND / 2
script: SeqSet[inputs[i = (i+1) % N_RAND]]

- name: n=100,000 string
prelude: inputs = build_string_inputs 100_000, N_RAND / 2
script: SeqSet[inputs[i = (i+1) % (N_RAND / 2)]]

# - name: n=1,000,000 ints
# prelude: inputs = build_int_inputs 1_000_000
# script: SeqSet[inputs[i = (i+1) % N_RAND]]

# - name: n=10,000,000 ints
# prelude: inputs = build_int_inputs 10_000_000
# script: SeqSet[inputs[i = (i+1) % N_RAND]]

contexts:
- name: local
prelude: |
$LOAD_PATH.unshift "./lib"
require: false
- name: v0.5.9
gems:
net-imap: 0.5.9
require: false
- name: v0.5.0
gems:
net-imap: 0.5.0
require: false
- name: v0.4.21
gems:
net-imap: 0.4.21
require: false
91 changes: 91 additions & 0 deletions benchmarks/sequence_set-normalize.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
---
prelude: |
require "yaml"
require "net/imap"

INPUT_COUNT = Integer ENV.fetch("BENCHMARK_INPUT_COUNT", 1000)
MAX_INPUT = Integer ENV.fetch("BENCHMARK_MAX_INPUT", 1400)
WARMUP_RUNS = Integer ENV.fetch("BENCHMARK_WARMUP_RUNS", 200)

def init_sets(count: 100, set_size: INPUT_COUNT, max: MAX_INPUT)
Array.new(count) {
Net::IMAP::SequenceSet.new(Array.new(set_size) { rand(1..max) })
}
end

def init_normal_sets(...)
init_sets(...)
end

def init_frozen_normal_sets(...)
init_sets(...)
.map(&:freeze)
end

def init_unsorted_sets(...)
init_sets(...)
.each do |seqset|
entries = seqset.entries.shuffle
seqset.clear
entries.each do |entry|
seqset.append entry
end
end
end

def init_abnormal_sets(...)
init_sets(...)
.each do |seqset|
entries = seqset.entries.shuffle
seqset.clear
entries.each do |entry|
if [true, false].sample
seqset.append entry
elsif entry.is_a? Range
seqset.append "#{entry.end || "*"}:#{entry.begin}"
else
seqset.append "#{entry}:#{entry}"
end
end
end
end

# warmup (esp. for JIT)
WARMUP_RUNS.times do
init_sets(count: 20, set_size: 100, max: 120).each do |set|
set.normalize
end
end

benchmark:
- name: "normal"
prelude: $sets = init_normal_sets
script: $sets.sample.normalize
- name: "frozen and normal"
prelude: $sets = init_frozen_normal_sets
script: $sets.sample.normalize
- name: "unsorted"
prelude: $sets = init_unsorted_sets
script: $sets.sample.normalize
- name: "abnormal"
prelude: $sets = init_abnormal_sets
script: $sets.sample.normalize

contexts:
# n.b: can't use anything newer as the baseline: it's over 500x faster!
- name: v0.5.9
gems:
net-imap: 0.5.9
require: false
- name: local
prelude: |
$LOAD_PATH.unshift "./lib"
require: false
- name: v0.5.0
gems:
net-imap: 0.5.0
require: false
- name: v0.4.21
gems:
net-imap: 0.4.21
require: false
Loading
Loading