This does work at runtime, i.e., with a format string that is not known at
compile time. A new format string (one you've never seen before) will indeed
cause codegen, but only once; after that, every usage of the same format
string will be fast. It's basically a cached-method-table version of
@eval @printf($fmt, ...)
but in practice, the caching is a big deal.
We could, of course, do it the old-fashioned way, using a let block and
caching the compiled versions in a Dict{String,Function}.
That's not to say there isn't room for a purely-runtime solution, but this
occupies a nice niche of runtime-vs-compiletime performance.
Best,
--Tim
On Wednesday, September 23, 2015 12:21:20 PM Stefan Karpinski wrote:
> This is very but it doesn't entirely solve the problem. The f"..." macro
> still entails code generation, which *can* be done at compile time, but to
> support runtime formats the code generation has to be deferred until run
> time – and the exact same thing could be done with the @printf macro. The
> main thing that the generated function approach gives you is a way of
> caching generated code associated with a particular format. But we could
> also do code caching with @printf by just maintaining a dict mapping format
> strings to generated code (this is essentially what generated functions do)
> and it would avoid polluting the generated function cache, so it's arguably
> better. The concern with this is that someone is going to write a format
> string that depends on variables and they'll end up generating a huge
> amount of formatting code. To see why this could be an exponential
> explosion of code, consider the format "%$(w1)d %$(w2)d %$(w3)d %$(w4)d\n",
> and suppose that each of w1 through w4 ranges from 1 to 32. That would
> generate over a million formatting functions. Ruh roh.
>
> I don't think there's a clever way around this without fundamentally
> changing the design – you have make some of the values that are currently
> being specialized on a compile time into runtime parameters so that you can
> reuse the same code for more formats. The good news is that I'm pretty sure
> that this is entirely doable, but it's a chunk of work.
>
> On Tue, Sep 22, 2015 at 9:34 PM, Tim Holy <[email protected]> wrote:
> > On Tuesday, September 22, 2015 05:21:10 PM Luke Stagner wrote:
> > > Would it be possible to rewrite @printf as a generated function instead
> >
> > of
> >
> > > a macro. That way the calling syntax would be more familiar.
> >
> > That's a good suggestion.
> >
> > At the risk of encouraging emacs users to "fix" the syntax with ctrl-T,
> > I'd
> > propose the following (apparently complete?) solution:
> >
> >
> > immutable FormatString{S} end
> >
> > FormatString(str::AbstractString) = FormatString{symbol(str)}
> >
> > macro f_str(arg)
> >
> > :(FormatString{symbol($arg)})
> >
> > end
> >
> > @generated function Base.print{format}(::Type{FormatString{format}},
> > args...)
> >
> > meta = Expr(:meta, :inline)
> > fmt = string(format)
> > allargs = [:(args[$d]) for d = 1:length(args)]
> > quote
> >
> > @printf($fmt, $(allargs...))
> >
> > end
> >
> > end
> >
> >
> >
> > Demo:
> > julia> print(f"%.3f", pi)
> > 3.142
> > julia> function foo(strs)
> >
> > for str in strs
> >
> > print(FormatString(str), pi)
> >
> > end
> >
> > end
> >
> > foo (generic function with 1 method)
> >
> > julia> strs = ("%.3f\n", "%.5f\n")
> > ("%.3f\n","%.5f\n")
> >
> > julia> foo(strs)
> > 3.142
> > 3.14159
> >
> > julia> @time 1 # just to warm up @time
> >
> > 0.000004 seconds (148 allocations: 10.151 KB)
> >
> > 1
> >
> > julia> @time foo(strs)
> > 3.142
> > 3.14159
> >
> > 0.000106 seconds (18 allocations: 704 bytes)
> >
> > Nice that we get to re-use the macro that Stefan worked so hard on!
> >
> > Best,
> > --Tim
> >
> > > On Tuesday, September 22, 2015 at 1:07:23 PM UTC-7, Stefan Karpinski
> >
> > wrote:
> > > > Possible, but I don't relish the thought of forever explaining to
> >
> > people
> >
> > > > that they need to use printf with or without the @ depending on if
> > > > they
> > > > want it to be fast or flexible. If you really don't care about speed,
> >
> > you
> >
> > > > can just do this right now:
> > > >
> > > > printf(fmt::AbstractString, args...) = @eval
> >
> > @printf($(bytestring(fmt)),
> >
> > > > $(args...))
> > > >
> > > >
> > > > But actually don't do that because it's so horrifically slow and
> > > > inefficient I just can't.
> > > >
> > > > On Tue, Sep 22, 2015 at 3:57 PM, Daniel Carrera <[email protected]
> > > >
> > > > <javascript:>> wrote:
> > > >> On 22 September 2015 at 20:40, Stefan Karpinski <[email protected]
> > > >>
> > > >> <javascript:>> wrote:
> > > >>> I think that before any further discussion takes place of how easy
> > > >>> or
> > > >>> hard implementing a high-performance printf is, anyone who'd like to
> > > >>> comment should spend some time perusing GNU libc's vfprintf
> > > >>> implementation
> > > >>> <
> >
> > http://repo.or.cz/w/glibc.git/blob/ec999b8e5ede67f42759657beb8c5fef87c8
> >
> > > >>> cc63:/stdio-common/vfprintf.c>. This code is neither easy nor
> >
> > trivial –
> >
> > > >>> it's batsh*t crazy.
> > > >>
> > > >> That is insane... 2388 lines, half of it macros, and I have no idea
> >
> > how
> >
> > > >> it works.
> > > >>
> > > >>> And we want to match its performance yet be much more flexible and
> > > >>> generic. The current printf implementation does just that, while
> >
> > being
> >
> > > >>> somewhat less insane GNU's printf code. If someone has bright ideas
> >
> > for
> >
> > > >>> how
> > > >>> to *also* allow runtime format specification without sacrificing
> > > >>> performance or generality, I'm all ears.
> > > >>
> > > >> This might be a stupid question, but what's the harm in sacrificing
> > > >> performance as long as we keep the current @sprintf for scenarios
> > > >> that
> > > >> call
> > > >> for performance? I don't always need printf() to be fast.
> > > >>
> > > >>> I have some thoughts, but they're just that – thoughts. One option
> >
> > is to
> >
> > > >>> change the design and avoid printf-style formatting altogether. But
> >
> > then
> >
> > > >>> I'm sure I'll never hear the end of it with people kvetching about
> >
> > how
> >
> > > >>> we
> > > >>> don't have printf.
> > > >>
> > > >> Probably. Everyone is used to printf and they are comfortable with
> > > >> it.
> > > >>
> > > >> Daniel.