Skip to main content

Notice

Please note that most of the software linked on this forum is likely to be safe to use. If you are unsure, feel free to ask in the relevant topics, or send a private message to an administrator or moderator. To help curb the problems of false positives, or in the event that you do find actual malware, you can contribute through the article linked here.
Topic: Boolean funcs TF behavior (Read 701 times) previous topic - next topic
0 Members and 1 Guest are viewing this topic.

Boolean funcs TF behavior

Is it indented that boolean TF functions evaluate all the expressions even when not needed?

Code: [Select]
$and($ifequal(1,0,$puts(a,'hi1'),),$ifequal(0,0,$puts(a,'hi2'),))$get(a)

Returns 'hi2' instead of nothing. But in this case the second expression should not need to be evaluated at all, the first expression already returns false.

Not that I intend to use short-circuiting (although it would be great), but on big expressions, this kind of evaluation is just wasted performance.

Re: Boolean funcs TF behavior

Reply #1
I wonder if you know that your example function is nonsense. $ifequal() does not return anything for $and() to react to. All its behavior is contained within.
foobar2000 titleformat functions don't return booleans. What you want to think of as boolean true is returned when tag field exists. Though there is at least one exception, $strcmp/$stricmp() returns a true and you can use with $and(). Normally $and() is meant to be used to test the existence of tag fields.

But it's true, titleformat parser doesn't stop inside $and() when detecting first missing item. I don't know how the titleformat parser works, does the engine even know when it's running inside $and() or is everything always evaluated starting from innermost functions and going outwards.

Re: Boolean funcs TF behavior

Reply #2
Scripting component fans can theoretically benchmark the performance of evaluating title format strings over every item in their library:

JSP3 code
Code: [Select]
var tfo = fb.TitleFormat("%title%");
var items = fb.GetLibraryItems();

var profiler = utils.CreateProfiler();
var arr = tfo.EvalWithMetadbs(items).toArray();
console.log("array length same as handle list count", arr.length == items.Count);
console.log(profiler.Time);

SMP/JSplitter code (not tested, I think I made the right changes??)
Code: [Select]
var tfo = fb.TitleFormat("%title%");
var items = fb.GetLibraryItems();

var profiler = fb.CreateProfiler();
var arr = tfo.EvalWithMetadbs(items);
console.log("array length same as handle list count", arr.length == items.Count);
console.log(profiler.Time);

After testing %title%, you can then change it to something more complex and see if you can observe a difference.

Re: Boolean funcs TF behavior

Reply #3
I wonder if you know that your example function is nonsense. $ifequal() does not return anything for $and() to react to. All its behavior is contained within.
foobar2000 titleformat functions don't return booleans. What you want to think of as boolean true is returned when tag field exists. Though there is at least one exception, $strcmp/$stricmp() returns a true and you can use with $and(). Normally $and() is meant to be used to test the existence of tag fields.

But it's true, titleformat parser doesn't stop inside $and() when detecting first missing item. I don't know how the titleformat parser works, does the engine even know when it's running inside $and() or is everything always evaluated starting from innermost functions and going outwards.
I hope you understand that was clearly just an example xd The thread is focused on and behavior, not the example. My bad.
Well, I could have done
Code: [Select]
$and($ifequal(1,0,$not(0)$puts(a,'hi1'),),$ifequal(0,0,$not(0)$puts(a,'hi2'),))$get(a)
or
Code: [Select]
$and($ifequal(1,0,%artist%$puts(a,'hi1'),),$ifequal(0,0,%artist%$puts(a,'hi2'),))$get(a)


Quote
But it's true, titleformat parser doesn't stop inside $and() when detecting first missing item. I don't know how the titleformat parser works, does the engine even know when it's running inside $and() or is everything always evaluated starting from innermost functions and going outwards.
That's the point. Asking why the parser works this way

Re: Boolean funcs TF behavior

Reply #4
Scripting component fans can theoretically benchmark the performance of evaluating title format strings over every item in their library:
Quote
After testing %title%, you can then change it to something more complex and see if you can observe a difference.

Well, I have observed differences on Library viewers like album list panels, where the TF expression changes the processing time (not necessarily talking about this particular example). Will do so for these kind of examples. Anyway I was just asking about why it was implemented that way, not necessarily claiming it has a great impact (although it's unnecessary processing and limits scripting).

 

Re: Boolean funcs TF behavior

Reply #5
Ok, so this kind of execution, for this pretty simple example... takes x2 time than it should.

X

Code: [Select]
$puts(a,)$and($ifequal(1,0,$not(0)$puts(a,hi1),),$ifequal(0,0,$not(0)$puts(a,hi2),))$get(a)

vs
Code: [Select]
$puts(a,)$ifequal(1,0,$not(0)$puts(a,hi1)$ifequal(0,0,$not(0)$puts(a,hi2),),)$get(a)

And the second obviously behaves as expected, only setting a if at least the first expression is true.

I would say the difference is pretty big. And checking for a real life example, as soon as the expression includes real tags or more complexity is like 4x or more in the worst case. And again, this is just a pretty basic example in a not so big library. If this is applied to multiple tags, differences may be from a few ms to seconds. *
Code: [Select]
'use strict';

const handleList = fb.GetLibraryItems();
const tfArr = [
    fb.TitleFormat('$puts(a,)$and($ifequal(1,0,$not(0)$puts(a,%ARTIST%%GENRE%),),$ifequal(0,0,$not(0)$puts(a,%ARTIST%%STYLE%),))$get(a)'),
    fb.TitleFormat('$puts(a,)$ifequal(1,0,$not(0)$puts(a,%ARTIST%%GENRE%)$ifequal(0,0,$not(0)$puts(a,%ARTIST%%STYLE%),),)$get(a)')
];
const profiler = fb.CreateProfiler();

tfArr.forEach((tf) => {
    profiler.Reset();
    tf.EvalWithMetadbs(handleList);
    console.log(profiler.Time + ' ms for ' + handleList.Count + ' items');
});


X

*  Obviously in this case the problem is the second part of 'and' is always evaluated, while on the second expression if-based nothing is evaluated, but if we change the 'ifequal' conditions to evaluate actual tags instead of numbers, the problem remains, where they are evaluated when not needed and comparison is not 'unfair'. These examples below still give x2 worse performance when using $and() for only 2 nested conditions, so I imagine it being much worse for complex scripts with multiple nested conditions based on boolean functions like or, and, etc.

Code: [Select]
$puts(a,)$and($iflonger(%ARTIST%,60,$not(0)$puts(a,%ARTIST%|%GENRE%),),$iflonger(%COMPOSER%,60,$not(0)$puts(a,%ARTIST%|%STYLE%),))$get(a)

Code: [Select]
$puts(a,)$iflonger(%ARTIST%,60,$not(0)$puts(a,%ARTIST%|%GENRE%)$iflonger(%COMPOSER%,60,$not(0)$puts(a,%ARTIST%|%STYLE%),),)$get(a)