StringTemplates deferred evaluation

Anatoly Kupriyanov kan.izh at gmail.com
Sun Mar 17 21:11:26 UTC 2024


Re "list containing { hugeObject, expensiveObject }", please look at the
real API of a real library:
https://www.slf4j.org/api/org/slf4j/Logger.html#debug(java.lang.String,java.lang.Object)
. All these one and two args overrides were aiming to overcome the "
if(isDebugLogging)" boilerplate.
I understand that StringTemplate was not aiming to solve all the possible
problems, but obviously it is not good enough for logging frameworks.
Meanwhile, the C# covered the issue, maybe not so nicely, by the string
template interceptors.

On Sun, 17 Mar 2024 at 19:45, Brian Goetz <brian.goetz at oracle.com> wrote:

> It depends on how you measure "don't care anything".  Even this, which is
> pretty fast, still creates an object with a list containing { hugeObject,
> expensiveObject }.  This is pretty cheap but not necessarily free.  (It may
> be free if the debug() call gets inlined.)
>
> I think for 9x% of the cases, this is entirely cheap enough; for those
> cases where it is not, the old fussy technique of
>
>     if (isDebugLogging)
>         logger.debug(...)
>
> will recapture the rest.
>
> On 3/17/2024 3:41 PM, Anatoly Kupriyanov wrote:
>
> I guess it is not fair to think about it in terms of premature
> optimization. I am trying to look at it from a logging-framework developer
> point of view. As a library developer I should give the smallest overhead
> possible. So library users could safely do
>   LOGGER.debug("Some thing={}, another={}", hugeObject, expensiveObject);
> and don't care about anything at all as debug is usually disabled. So the
> debug statement could be used even in a tight computation loop without
> worries. In other words, logger devs could not assume anything about how
> their framework would be used, but they will fight for the easiest to use
> api with the fastest performance.
> The idea is that the trace is usually disabled and an app could chew-up
> gigs of data, but while debugging/investigating, the trace could be
> temporarily enabled and expected to degrade performance.
>
>
> On Sun, 17 Mar 2024 at 17:36, David Alayachew <davidalayachew at gmail.com>
> wrote:
>
>> Thank you for the code example. That helps clarify your desired behaviour.
>>
>> Ok, if your intent is to have absolutely no garbage collection
>> whatsoever, then yes, my solution does not guarantee that. How much the JIT
>> will help or optimize, I cannot say.
>>
>> That said, a String is a "blessed type" in Java. It has a handful of
>> optimizations that are specific to it and it alone. I am actually expecting
>> that StringTemplates will receive at least a small taste of this sort of
>> optimization.
>>
>> Furthermore, the cost of creating a StringTemplate is rarely, if ever,
>> the expensive part of the operation. In fact, I am certain it is trivial,
>> if not optimized away like you were asking.
>>
>> I won't presume to call this premature optimization -- perhaps your
>> application needs require it. But if so, I would first wait and see (or
>> better yet, calculate!) the performance metrics of these StringTemplates.
>> If they fail to meet your performance needs, then communicate your
>> performance needs to this mailing list, with a hard example (and ideally
>> metrics!) in tow.
>>
>> On Sun, Mar 17, 2024 at 1:07 PM Anatoly Kupriyanov <kan.izh at gmail.com>
>> wrote:
>>
>>> Classically (how it was done a decade or so ago) the logger method would
>>> look like:
>>> void debug(String template, Object arg1, Object arg2) {
>>>   if(!enabled) return;
>>>   writeLog(interpolate(template, arg1.toString(), arg2.toString()));
>>> }
>>>
>>> That allows that  if enabled==false, then it will be zero-gc,
>>> guaranteed.
>>> It even used a lot of overrides with zero args, one arg, two args to
>>> avoid ...-ellipsis array allocation. E.g. see
>>> https://www.slf4j.org/api/org/slf4j/Logger.html
>>>
>>> There is a C# hacky approach the String Interpolation Handler (
>>> https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/interpolated-string-handler
>>> ) to use struct (allocated on stack) and magical isEnabled flags to cover
>>> logging effectively.
>>>
>>> I don't see how it would be possible to use the StringTemplate to
>>> achieve the similar effectiveness, unless there are some good and reliable
>>> optimizations in JIT to avoid allocations.
>>>
>>> On Sun, 17 Mar 2024 at 16:41, David Alayachew <davidalayachew at gmail.com>
>>> wrote:
>>>
>>>> The cost would be no different than evaluating a StringTemplate lazily.
>>>> Utlimately, log.debug(blah) is still a method call, and therefore, some
>>>> object is going into the method. Whether we evaluate the StringTemplate
>>>> lazily or the Supplier<StringTemplate> lazily is largely the same thing.
>>>>
>>>> Or am I misunderstanding you?
>>>>
>>>> On Sun, Mar 17, 2024 at 12:28 PM Anatoly Kupriyanov <kan.izh at gmail.com>
>>>> wrote:
>>>>
>>>>> But the "() ->" thing is still a runtime object to be allocated on the
>>>>> heap... or is the JIT smart enough to always optimize it out?
>>>>>
>>>>> On Sun, 17 Mar 2024 at 16:17, David Alayachew <
>>>>> davidalayachew at gmail.com> wrote:
>>>>>
>>>>>> I feel like that would be on the logging libraries to provide, not so
>>>>>> much the language.
>>>>>>
>>>>>> Let's say that your problem (as I understand it) is like this.
>>>>>>
>>>>>> log.debug("metrics =
>>>>>> \{this.expensiveComputationThatNormallyWouldntRunUnlessYouActivateDebug()}");
>>>>>>
>>>>>> Sounds to me like the solution is this instead.
>>>>>>
>>>>>> log.debug(() -> "metrics =
>>>>>> \{this.expensiveComputationThatNormallyWouldntRunUnlessYouActivateDebug()}")
>>>>>>
>>>>>> So, your logging library just needs to add a new overload for a
>>>>>> Supplier<StringTemplate>, and then this problem is solved entirely outside
>>>>>> of the language. Log4J and friends are pretty good about keeping up with
>>>>>> new additions, so it should not take long.
>>>>>>
>>>>>> I know it's a little less convenient, but doing it this way helps
>>>>>> keep out complexity from the feature, and only introduces it where
>>>>>> necessary (and it's only necessary at use-site).
>>>>>>
>>>>>> Would this meet your needs?
>>>>>>
>>>>>> On Sun, Mar 17, 2024 at 11:15 AM Justin Spindler <
>>>>>> justin.spindler at gmail.com> wrote:
>>>>>>
>>>>>>> I was toying around with the second preview of StringTemplates and I
>>>>>>> had a question regarding their design.  I was wondering if it had been
>>>>>>> considered for the embedded expressions to be evaluated lazily?
>>>>>>>
>>>>>>> One of the first use cases that came to mind when I was exploring
>>>>>>> StringTemplates is logging.  That is an extremely common case where we want
>>>>>>> to produce a form of interpolated value, and the current syntax generally
>>>>>>> has the same concerns that a formatted string would, in that the inputs are
>>>>>>> removed from where they are defined within the message format.  However, if
>>>>>>> the log message is below the log level threshold you generally don't want
>>>>>>> to incur the cost of building the message, including evaluating those
>>>>>>> embedded expressions.  Log libraries typically offer a mechanism to defer
>>>>>>> evaluation via Suppliers, but that feels like it would be a challenge to
>>>>>>> use within StringTemplate.
>>>>>>>
>>>>>>> C# is an example of a language that offers this functionality via
>>>>>>> FormattableString, which gives a method the ability to choose whether or
>>>>>>> not to interpret the template or evaluate the expressions.  That allows
>>>>>>> logging below threshold to be more or less a no-op.
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>
>>>>> --
>>>>> WBR, Anatoly.
>>>>>
>>>>
>>>
>>> --
>>> WBR, Anatoly.
>>>
>>
>
> --
> WBR, Anatoly.
>
>
>

-- 
WBR, Anatoly.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-dev/attachments/20240317/1013e7e6/attachment-0001.htm>


More information about the amber-dev mailing list