# Would it be interesting to optimize print(io, xs...) to be type stable?

**URL:** <https://discourse.julialang.org/t/would-it-be-interesting-to-optimize-print-io-xs-to-be-type-stable/94816>\
**Category:** Performance\
**Created:** [February 18, 2023, 12:10am UTC](https://discourse.julialang.org/t/would-it-be-interesting-to-optimize-print-io-xs-to-be-type-stable/94816 "2023-02-18T00:10:43Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![taotree](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/taotree/32/31982_2.png) [@taotree](https://discourse.julialang.org/u/taotree)\
**Post date:** [February 18, 2023, 12:10am UTC](https://discourse.julialang.org/t/would-it-be-interesting-to-optimize-print-io-xs-to-be-type-stable/94816/1 "2023-02-18T00:10:43Z")

</div>

I noticed that the [varargs version of print/println](https://github.com/JuliaLang/julia/blob/12d329b6e3db76091688d73ea3e5b81f0de9e1fe/base/strings/io.jl#L42) 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](https://github.com/JuliaLang/julia/issues/41396)) and got sidetracked, and didn’t want that to affect these timings.

```julia
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))))

```

---

<div class="post-metadata">

**Author:** ![tim.holy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tim.holy/32/52_2.png) [@tim.holy](https://discourse.julialang.org/u/tim.holy)\
**Post date:** [February 19, 2023, 1:12pm UTC](https://discourse.julialang.org/t/would-it-be-interesting-to-optimize-print-io-xs-to-be-type-stable/94816/2 "2023-02-19T13:12:27Z")

</div>

In my opinion, probably not. In cases where performance matters, you can print one argument at a time and make everything inferrable. The problem with making it type-stable is excess specialization and longer compile times. The latency may be less of an argument now that we can cache native code, of course. But you can’t get away from larger memory requirements: if you have `k` types, then `print` statements with `N` arguments could require up to `k^N` specializations. If these were all realized, it doesn’t take very large `k` and `N` to eat all the RAM in typical computers. That’s a terrible price to pay for a bit better performance, when there are other ways you can get the same performance benefit.

You can experiment with “what life would be like with an inferrable vararg `print`” by defining

```julia
myprint(io::IO, arg) = print(io, arg)
@inline myprint(io::IO, arg1, arg2...) = (myprint(io, arg1); myprint(io, arg2...))

```

If you find yourself liking it, feel free to use it in your own code. But before you get too excited, do make sure you test something like `myprint(stdout, [rand(Char) for _ = 1:1000]...)` (though there are tricks for addressing that, search Julia’s `base/` for `Any16`).

---

<div class="post-metadata">

**Author:** ![taotree](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/taotree/32/31982_2.png) [@taotree](https://discourse.julialang.org/u/taotree)\
**Post date:** [February 20, 2023, 4:22pm UTC](https://discourse.julialang.org/t/would-it-be-interesting-to-optimize-print-io-xs-to-be-type-stable/94816/3 "2023-02-20T16:22:02Z")

</div>

Thank you for the reply. That could be included in application code and only used in places that need the extra performance based on profiling.
