The 5,000 Tiddler Club – People with the Need for Speed

Is there any reference?

  1. Why every DOM get updated when a tiddler (even a draft) changes? I thought there is a vdom and compare algorithm like React.js
  2. Why event-catcher can prevent DOM change?

For 2. , I see

Use of the event catcher widget is beneficial when using large numbers of other trigger widgets such as the ButtonWidget is causing performance problems. The workflow it enables is akin to what is referred to as “event delegation” in JavaScript parlance.

in https://tiddlywiki.com/#EventCatcherWidget. But didn’t explain why. I guess because ButtonWidget is a widget that will refresh? But the DOM inside EventCatcherWidget will refresh too, so this doesn’t make sense.

It’s not rendering the HTML and updating the DOM that is slow, it’s the number of event handlers that have to be initialized and maintained by the browser. eg: Every <$link widget does attach a javascript event-handler to the link element. So if you have 1000 links you’ll have 1000 event-handlers.

If you have a event-catcher as a wrapper that catches clicks on DOM elements that internally can be selected using a class-selector, there will be only 1 event-handler for 1000 DOM elements. That’s much faster.

Hope that explains it a bit.

That’s not quite right.

Every change to the tiddler store (even a draft) triggers the refresh cycle. It is up to the refresh processing of each individual widget whether it needs to recreate or update itself in the light of the changes to the store.

No, TiddlyWiki doesn’t use a virtual DOM.

I’m not sure what you mean. As you note, the eventcatcher widget enables event delegation patterns to be used.

My main project (flux.wvwlogs.com) is currently 9,592 non system tiddlers [!is[system]sort[title]] of which 8,939 are echarts data tiddlers, type:application/javascript [tag[ChartData]].

I am running on nodejs with lazyload-all.

Currently about 75mb in uncompressed initial load size. Mainly because type:application/javascript are not lazyloaded and I haven’t found the magic to get echarts to process text tiddlers that are lazyloaded. Initial load would be around 7mb if I find the work around.

Beyond the lazyload issue, I don’t think there is much complexity involved that is slowing it down.

Update: Solved my lazy load issue and modified my ChartData tiddlers to type: vnd.tiddlywiki.
I am now serving 1.5 years worth of data and charts to 250 members with a ~7mb uncompressed initial load. :slight_smile:

  • The html snapshot method I used was effective, used only TiddlyWiki script and was very performant.
  • Unfortunatly I do not know what you are talking about here?
  • Perhaps we could
    • Provide some examples and guidence in the documentation
    • In 5.3.x Write a custom procedure or widget that implements this.

Somehow I’ve missed this thread. This is right up my alley!

Over the years I’ve specialized in using TiddlyWiki for searchable image archives, so my wikis nowadays often have very large numbers of tiddlers. Performance became a principal concern to keep these projects usable. The two biggest causes of lag for my projects are the construction of filters and the size of the wiki, in that order. Ensuring filters that handle these large numbers of tiddlers are efficient makes the biggest difference.

My largest one currently is BlinkieWiki, which as of writing this is a single-file wiki with a hefty 40,000+ tiddlers. Last year when it had half the tiddlers, it struggled to handle many simple search queries because of inefficiently constructed filters that handled displaying the tiddlers that matched the query. At one time it was slow enough to take up to 15 seconds(!) to even process going to the next page of results for a broad query. I’ve been at war with the lag for some time, because each discovered trick would work for a while, only for the lag to creep back in as the tiddler count increased. If nothing else, it’s forced me to get creative with my solutions.

Currently this lag has been reduced to far more bearable lengths. The average query now broadly takes between 390-550ms to navigate between search results. Some queries are as fast as ~250ms. I’m rather proud of the speed I’ve managed to accomplish for such a large website.

I’ve read up on and independently figured out a number of tricks to improve the performance, although my own tricks are brittle and not broadly applicable to all use cases. I’ve made a few posts about the ones I’ve discovered, such as

  • Avoiding the filter Operator by using substitution in macros instead. I’ve since discovered the same trick similarly applies to subfilter[], which was a minor but noticeable source of lag.
  • Another one was using search:tags[] instead of tag[], which has faster performance but only works with tags that don’t have whitespace in their names. I simply avoid using tags with whitespace to work around it.
  • The order in which a filter is constructed impacts the performance as well. It’s been too long ago for me to recall how much of a difference it made in BlinkieWiki specifically, but placing operators at the beginning that would return narrow results (such as creators, language, and text search) and putting broad ones last (such as colors and graphic type) improved the speed of certain queries significantly.
  • Other impactful optimizations have already been mentioned here, such as
    • limiting results (pagination)
    • deferring unnecessary refreshing of filters with Saqimtiaz’s refresh widget(!)
    • keeping the SideBar closed. For the end user of BlinkieWiki, it does not need to be open for any normal usage. It additionally keeps a custom blank tab selected, which slightly improves performance over having a default tab open - even when the SideBar is closed
  • In the rare situation I can leverage field[] over search[], it is lightning fast for handling operations that would otherwise cause significant performance problems. This is used in some statistics pages and the list of creators, but not the search page.

More recently, upgrading the wiki to 5.4.0 presented additional interesting challenges. The new filters checking for tiddlers in the BackgroundActions and MediaQueryTracker tags created noticeable lag operating on those tens of thousands of tiddlers, about ~30ms on each refresh (which I only noticed thanks to the Advanced Performance plugin). 30ms sounds trivial for the search page, but the lag was noticeable on other pages that were ordinarily meant to load very quickly. To reduce this I modified the filters to only evaluate system tiddlers instead of all tiddlers, which made a big difference.

I’ve been very interested lately in figuring out unusual methods of optimization for the pursuit of seeing how fast TiddlyWiki can truly get, and it’s quite rewarding.

@MaxDimo thanks for sharing but perhaps you could add details such as it is a single file wiki, or running on node as this can also influence performance? Perhaps update the above post?

BlinkieWiki is a single file wiki, as are all of my projects. Frankly I’m so used to it that I forget about the Node version, so I can’t say that I’m familiar with the differences in performance between the two. I’ve updated the post to specify the type of wiki it is :grinning_face_with_smiling_eyes:

There are good arguments that if you have a large wiki a node server implementation, it will help, its more complex if you want it online but for local use a good step. TiddlyDesktopTS etc… allow you to import a single file and turn it into a folder or node implimentation.

There is a PR to run filter on node server side, if so, UI will be very fast, simply wait for server to return the filter result.

Can you link that PR here just to take a look at it

I have a single-file TiddlyWiki that uses tm-http-requestto submit a query to a remote database server containing Canadian “address” data. The server returns a block of JSON text, which is then expanded into separate tiddlers using a custom $action-unpackdata widget that recursively “walks” the JSON object structure and outputs a tiddler for each field of each object in the JSON. Depending upon the query criteria, this results in somewhere between several hundred and about 30,000 $:/temp tiddlers… for which the “in-use” lag is barely noticeable.

However, the “worst case” usage (with no specific query criteria) returns the entire database contents as a very large block of JSON text (about 27Mb). When unpacked, this creates almost 680,000 $:/temp tiddlers!

As you would expect, this seriously affects the responsiveness of the entire TiddlyWiki interface (I even get some brower-based “do you want to wait?” messages during the “unpack” processing). Nonetheless, it does NOT crash the TiddlyWiki, and as soon as I delete those $:/temp tiddlers (which triggers some more “do you want to wait?” messages), the responsiveness returns to normal.

Note that because these are $:/temp tiddlers, they are NEVER saved in the TiddlyWiki file, so the in-use lag is only a transient effect that does not affect the load time in any way.

-e

wow this is fascinating stuff ! thanks for that !!

and here I thought my wiki was enormous (measly 9192 tiddlers :slight_smile: )

going to be watching this thread for more useful tips !