I noticed that the varargs version of print/println is not type stable and it affects the performance. Some test code below. It’s interesting that map, foreach, and fold are implemented in a type stable way, so… println could probably just be a foreach call inside a lock.
The custom show methods aren’t directly related. I discovered those were allocating (found an issue about it) and got sidetracked, and didn’t want that to affect these timings.
using BenchmarkTools
function testPrint()
io = Base.devnull
f3 = x -> print(io, x)
f4 = x -> print(io, x)
tup = MyTuple((:d, MyInt(7)))
n1 = MyInt(1)
n2 = MyInt(18)
println("#### print")
display(@benchmark print($io, "a", $n1, :c, $n2, $tup))
# median ≈ 213 ns
println("\n#### testVargs1:")
display(@benchmark testVargs1($io, "a", $n1, :c, $n2, $tup))
# median ≈ 190 ns : Same as print, but doesn't lock so a touch faster.
println("\n#### testVargs2:")
display(@benchmark testVargs2($io, "a", $n1, :c, $n2, $tup))
# median ≈ 99 ns
println("\n#### foreach:")
display(@benchmark foreach($f3, $("a", n1, :c, n2, tup)))
println("\n#### map:")
display(@benchmark map($f4, $("a", n1, :c, n2, tup)))
# println("#### print 1 arg")
# display(@benchmark print($io, "a"))
return
end
function testVargs1(io, args...)
for x in args
print(io, x)
end
end
@inline testVargs2(io, arg) = print(io, arg)
@inline function testVargs2(io, arg1, args...)
print(io, arg1)
@inline testVargs2(io, args...)
end
struct MyInt
x::Int
end
struct MyTuple{T}
x::T
end
@inline Base.show(io::IO, n::MyInt) = show(io, Char(0x30 + n.x % 10))
testMyInt(io) = show(io, MyInt(15))
@inline function Base.show(io::IO, x::MyTuple)
# This only handles 2 tuples, but that's enough for this test
print(io, '(')
print(io, first(x.x))
print(io, ',')
print(io, last(x.x))
print(io, ')')
end
testMyTuple(io) = show(io, MyTuple((:a, MyInt(15))))