Skip to content

Instantly share code, notes, and snippets.

@ffevotte
Last active January 3, 2019 16:00
Show Gist options
  • Save ffevotte/29b538870839b42eef2906bc3833893c to your computer and use it in GitHub Desktop.
Save ffevotte/29b538870839b42eef2906bc3833893c to your computer and use it in GitHub Desktop.
Benchmarking several ways of building iterators in Julia (see https://discourse.julialang.org/t/prefered-pattern-for-generators/18749)
# Initial tests
using BenchmarkTools
N = 1_000_000
@btime let s = 0
for i in 1:$N
s += i&1
end
s
end
@btime let s = 0
for i in 1:$(10*N)
s += i&1
end
s
end
# Collect benchmarking statistics
import Statistics: mean
stats = []
function pushStats(title, comment, bstats)
println("Benchmarking $title...")
push!(stats, (title,
bstats.memory/1024^2,
bstats.allocs,
minimum(bstats.times)/1000,
mean(bstats.times)/1000,
maximum(bstats.times)/1000,
comment))
end
# UnitRange
pushStats("UnitRange", "",
@benchmark let s = 0
for i in 1:$N
s += i&1
end
s
end)
# Closure implementation
function countto1(n)
let n = n
count = 0
() -> (count >= n ? nothing : count += 1)
end
end
pushStats("closure", "",
@benchmark let itr = countto1($N), s = 0
while (i = itr()) !== nothing
s += i%2
end
s
end)
# Channel implementation
function countTo_channel(n)
Channel(ctype=Int) do c
count = 0
while count < n
put!(c, count+=1)
end
end
end
pushStats("Channel", "",
@benchmark let s = 0
for i in countTo_channel($N)
s += i&1
end
s
end)
# ResumableFunctions
using ResumableFunctions
@resumable function countTo_resumable(n :: Int)
count = 0
while count < n
count += 1
@yield count
end
end
pushStats("ResumableFunctions", "optimized, as per @tkoolen's comment",
@benchmark let s = 0
for i in countTo_resumable($N)
s += i&1
end
s
end)
# Implementation of the iterator interface
struct CountTo_struct
n :: Int
end
Base.iterate(c::CountTo_struct, state=0) = state >= c.n ? nothing : (state+1, state+1)
Base.length(c::CountTo_struct) = c.n
pushStats("struct", "",
@benchmark let s = 0
for i in CountTo_struct($N)
s += i&1
end
s
end)
# Using IterTools
using IterTools
countTo_iterTools(n) = Iterators.take(iterated(x -> x + 1, 1), n)
pushStats("IterTools", "as per @tkf's comment",
@benchmark let s = 0
for i in countTo_iterTools($N)
s += i&1
end
s
end)
# Nicely format statistics
using Markdown: Table, MD
using Formatting
table = [["Generator", "memory [Mb]", "allocs", "min time [µs]", "avg time [µs]", "max time [µs]", "comment"]]
for (title, mem, allocs, time1, time2, time3, comment) in sort(stats; by=x->x[5])
push!(table, [title,
fmt(".2f", mem),
fmt("d", allocs),
fmt(".2f", time1),
fmt(".2f", time2),
fmt(".2f", time3),
comment])
end
println(MD(Table(table, [:l,:r,:r,:r,:r,:r, :l])))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment