Is it suggested to write functions for ::IO?

I found that some of the functions in Base are written like (::IO,::Any), and then uses sprint to wrap it so that it can return a string.
For the following code:

function A(io::IO)
print(io,"foo")
end
A()=sprint(A)
B()="foo"
B(io::IO)=print(io,B())

Which is quicker, A or B (maybe in more complicated cases than a single “foo”), and why?
If one of them is quicker, does the conclusion also work for inputting(using IOBuffer to wrap a string)?

It’s usually written that way to save allocations. Strings in julia are immutable, so if you don’t actually need a string and just want to write some result somewhere (possibly on disk, to a socket, …), you can just pass the IO around instead. As such, “which is quicker” depends on your context, e.g. if you really do need a String you’re not really going to get around creating it.

1 Like

I see, but I need the exact answer to “which is quicker” partly because I will use it for both cases(somtimes I need String and sometimes IO).

I would have a look at BenchmarkTools.jl then. Just watch out for global variables when benchmarking, see the docs for more info on how to use it.

Well, I couldn’t answer that precisely because what’s quicker depends on how it’s used and what exactly you mean with that :slight_smile: Benchmarking your use case is definitely the way to go, but having an API that takes an IO that you explicitly print to is (generally) going to be easier to optimize because you don’t create new strings all the time. Wrapping that in a function that passes an IOBuffer you then convert to a string will probably be fast.

1 Like
julia> @btime A()
  89.172 ns (3 allocations: 160 bytes)
"foo"

julia> @btime B()
  1.500 ns (0 allocations: 0 bytes)
"foo"

julia> io=open("D:/1.txt","w")
IOStream(<file D:/1.txt>)

julia> @btime A($io)
  62.590 ns (0 allocations: 0 bytes)

julia> @btime B($io)
  61.151 ns (0 allocations: 0 bytes)

julia> function encode(obj::Vector)
           l=length(obj)
           if isempty(l)
               return "[]"
           end
           s="[$(obj[1])"
           for i in 2:l
               s*=",$(obj[i])"
           end
           return s*']'
       end
encode (generic function with 1 method)

julia> encode([0,1,2,"foo"])
"[0,1,2,foo]"

julia> @btime encode([0,1,2,"foo"])
  1.440 μs (26 allocations: 1.22 KiB)
"[0,1,2,foo]"

julia> function encode2(io::IO,obj::Vector)
       l=length(obj)
       if isempty(l) print(io,"[]") end
       print(io,"[$(obj[1])")
       for i in 2:l
       print(io,",$(obj[i])")
       end
       print(io,']')
       end
encode2 (generic function with 1 method)

julia> encode2(v::Vector)=sprint(encode2,v)
encode2 (generic function with 2 methods)

julia> @btime encode2([0,1,2,"foo"])
  1.580 μs (25 allocations: 1.27 KiB)
"[0,1,2,foo]"

The benchmark above somehow shows that using ::IO (for writing) should be more likely to be chosen when it’s complicated.