Some thoughts on performance

I’m not sure, what a “single title list” is, but the keyvalues plugin will give you simple access to data-tiddler values without any wikify calls.

Firstly I’ll point out that I have mentioned that there can be some few situations in which using wikify is warranted. The question you pose is almost one of them. Also consider that there are potentially other performance considerations when using data tiddlers, which may negate the performance gains by avoiding wikify.

Most of the filter run prefixes introduced in the recent releases are geared towards facilitating exactly these kinds of needs.

This is how I would approach it (untested code since I never use data tiddlers and therefore cannot easily test):

Given a data tiddler called data:

[[data]indexes[]] :map[[data]getindex<currentTiddler>] :reduce[format:titlelist[]addprefix[ ]addprefix<accumulator>]

Note that you only need to reduce if you want to save the titlelist in a variable. If you want to use the values of the data tiddler in a list filter for example, you do not need the :reduce[] filter run.

Or if the reference to the data tiddler is in the variable currentTiddler:

[[<currentTiddler>]indexes[]] :map[<..currentTiddler>getindex<currentTiddler>] :reduce[format:titlelist[]addprefix[ ]addprefix<accumulator>]

The above constructions are perhaps easier to understand if one is unfamiliar with map and reduce, however they can be further simplified to omit the :map[] filter run.

For example:

[[data]indexes[]] :reduce[[data]getindex<currentTiddler>format:titlelist[]addprefix[ ]addprefix<accumulator>]
2 Likes

This information is pure gold, thanks a lot ! I wish there was something like https://jsben.ch/ but for tiddlywiki … or maybe I just need to learn how to use the build-in performance instrumentation lol.

I have a question : it’s never clear to me when should I use a list widget vs a reveal widget to conditionally show/hide something. The reveal widget always create a dom element even when the condition is not matched, whereas a list widget with no item output does not generate a DOM element.

Does that mean a list widget is better for performances ?

E.g, I have a ViewTemplate that shows a macro for CSS Stylesheet instead of the tiddler body if a tag is set in order to enable syntax highlighting on tiddlers without the text/css type.

Which one would be better to use for performance :

<$list filter="[all[current]tag[$:/tags/Stylesheet]then<folded-state>get[text]match[show]]" variable=_>
<$macrocall $name="copy-code" language=css display-language=show code={{!!text}} />
</$list>

or

<$list filter="[all[current]tag[$:/tags/Stylesheet]]">
<$reveal tag="div" type="nomatch" stateTitle=<<folded-state>> text="hide">
<$macrocall $name="copy-code" language=css display-language=show code={{!!text}} />
</$reveal>
</$list>

Hah! As it would happen I have been pondering this very question just this week, looking at the code for both $list and $reveal side by side. As it turns out I don’t think there is a significant difference between the two performance wise.

My choice for which to use depends on whether I want the generated DOM node, and whether I may want to animate the hide/reveal of content.

For simple conditional display of content both are theoretically overkill, and there is an argument to be made for a very simple <$if> widget. However, without testing it is difficult to predict if the difference in performance would be noticeable enough to be worthwhile.

In your particular example, using the $list widget alone is definitely preferable to combining both $list and $reveal.

Hope that helps, busy week. Will revisit later when I have given it some more consideration.

1 Like

Thanks! I obviously didn’t understand the full potential of map !

Yes, I try to warn newbies away from data tiddlers, but am seldom heeded (there is a discussion right now on GG where participants are eagerly pursuing a data tiddler approach).

In this case, I wanted to analyze OriginalTiddlerPaths, so there is no choice but to use data tiddlers.

Which Evan Balster provided a while back. I much prefer it over <$reveal> (perhaps only for semantic reasons). And the fact he allowed <$else> to be used following a falsey <$reveal> is pretty cool, too, See: Condition Plugin

I have a nagging feeling I read somewhere that @joshuafontany took over the condition plugin along with the formula plugin GitHub - EvanBalster/TiddlyWikiFormula: Functional Formulas for TiddlyWiki, in the style of Excel and Google Sheets

1 Like

That make sense, thanks !

I thought that combining the two could be interesting for a ViewTemplate that needs the fold animation. If a reveal widget where to be used alone, all tiddlers would get a DOM element, even those who do not match… that’s why I sometimes use a reveal widget inside a list widget, but I never did proper performances testing.

From the core performance side, there wouldn’t be a big difference. BUT If you have a lot of tiddlers visible in the DOM there definitely is a difference. Drawing the DOM has the biggest impact on TW’s UI response time.

The main problem is, that by default TW draws big lists, even if the elements are way outside the viewport. …

But if you show eg: 100 tiddlers at a time you probably would have problems even with a decent mobile. …

I have added some thoughts on using $eventcatcher and the use of wikitext in stylesheets.

2 Likes

Great stuff @saqimtiaz - I think I understand the stylesheets piece, but having trouble with the eventcatcher one. How do you replace using buttons if you “think you” need buttons?

1 Like

In TiddlyWiki if you think you need $button widgets, it is because you want to be able to click on something to invoke actions.

As an example imagine a long list generated by a $list widget that creates a button for each list item:

<$list filter="....">
<$button tag="div" class="myclass" actions=<<myactions>> />
</$list>

Alternatively you can do something along these lines, which tells the $eventcatcher to listen for clicks inside DIVs with the class “myclass” and invoke actions in response.

<$eventcatcher $click=<<myactions>> selector="div.myclass">
<$list filter="....">
<div class="myclass" />
</$list>
</$eventcatcher>

Not only is having a bunch of DIVs faster than $button widgets, but this also opens the door to be able to use static pre-rendered lists in some situations.

Beautiful, that’s probably exactly what I need for the bullets plugin for selecting inactive lines!

Static/pre-rendered as in pre-declared?

<$eventcatcher $click= ...>
 <div ... />
 <div ... />
 <div ... />
  ...
 <div ... />
</$eventcatcher>

Interesting.

EDIT: That’s pure gold! THANK YOU @saqimtiaz !!!

:trophy:

Okay, so event delegation is almost trivial in JavaScript but to see it working in wikitext with a couple of widgets and a little wiring… wow. :sunglasses:

There is a way to go around that, with the subfilter operator :

<$vars list="A B C D E +[allbefore:include[C]]" > {{{ [subfilter<list>] }}}

This can also be used to use several variables in one filter :

<$vars a="1" b="[<a>add[1]]"> {{{ [subfilter<b>] }}} = 2

Is $vars more performant than the new $let ? If not, shouldn’t the var widget be entirely replaced by the new let widget ?

Seems like this might become the basis for a WYSIWYG editor view template (though it might work a little different than true WYSIWY editors).

It is worth keeping in mind that a lot of these performance implications only matter in practice when you reach sufficient complexity and scale with your code. The average user probably needs never think about this unless perhaps writing code for others, hence why this discussion is in the developer’s area.

If your list content will change rarely enough, you can use the change itself as the trigger to render and save it, and then re-use until the next time it changes. Wont be worthwhile unless you really are dealing with very large lists and then Dynaview would be an alternative way to approach it.

@telumire there are many ways to return multiple items from $vars or $let. The intent here is to provide guidance as to when it might make sense to use $set. $let is only more performant than $vars if it allows you to use fewer widgets, and is a lot more flexible in that sense. We cannot modify the behaviour of $vars due to backwards compatibility, therefore we have a new widget $let. $vars should really be considered deprecated.

@Mark_S yes as long as each “chunk” of content is wrapped in an identifying DOM node that is possible. On a macro scale that is what Streams does to know which node you want to edit.

Since the variables provided to actions by $eventcatcher also provide information on the location of the clicks, you could even try to implement something like an etch-a-sketch. This demo uses the $eventcatcher to know which list item a user has swiped on and animates it in response to the swipe:

1 Like

Note: As of 17/03/2022 this no longer an issue in TW 5.2.2 pre-release. The CSS is only updated when it has changed, regardless of underlying widgets used in stylesheets.

A quick note follows which I will try to elaborate on better when the opportunity presents itself:

A recommendation if using wikitext in stylesheets:
Avoid using filtered transclusions.

So instead of:

.myclass [data-item-title="{{{ [{active}escapecss[]] }}}"] {
	color: #fff;
}

do this instead:

<$let activeTitle={{{ [{active}escapecss[]] }}}>
.myclass [data-item-title="<$text text=<<activeTitle>>/>"] {
	color: #fff;
}
</$let>

The problem with a filtered transclusion is that you are creating a $list widget where the default item template is a link. Not only is that more overhead in terms of rendering, but links refresh more aggressively and this can lead to unexpected refreshing of the stylesheet. (If one part of the stylesheet refreshes, the entire stylesheet needs to be refreshed.)

For example in the code above with a filtered transclusion, if the tiddler whose name is saved in the tiddler “active” changes, this will force the entire stylesheet to be re-rendered which is not what one would expect. This is because under the covers a link is being generated to that tiddler whose name is stored in the tiddler “active” and this link needs to be refreshed every time that tiddler changes, in case the tiddler becomes/or stops being a missing tiddler. This in turn triggers the entire stylesheet needing to be refreshed.

However with the $let approach the stylesheet will only be refreshed when the value of the “active” tiddler changes, which is what one would expect.

Edit: the nuance here is that the entire stylesheet is replaced/updated - potentially on every keystroke in some situations - despite the CSS not having changed at all. The natural assumption is that the stylesheet is updated when the CSS has changed.

4 Likes

If the issue is that the filtered transclusion output a link, why not assign the filter directly to the text widget or use another template for the filtered transclusion? Would that work too ?

.myclass [data-item-title="<$text text={{{ [{active}escapecss[]] }}}/>"] {
	color: #fff;
}

or

.myclass [data-item-title="{{{ [{active}escapecss[]]||$:/core/templates/canonical-uri-external-raw}}}"] {
	color: #fff;
}

Alternatively, a custom template with this content : 

<$view field="title"/>

The intent of my post is to point out a problematic code pattern to avoid, rather than to recommend a specific alternative.

There are several alternatives. The one I posted is a derivative of code I was working with where I needed to reference the output of the filter more than once and therefore a $let widget is a better choice than assigning directly to a $text widget.

Filtered transclusions are a poor choice in this scenario from a performance point of view, as previously stated the widget tree created is more complex. Not to mention that having to create or reference another template that you cannot see in situ is also going to be error-prone.

2 Likes

I see, that make sense. Thanks !