Speed of <<currentTiddler>> vs {{!title}}

<<currentTiddler>> vs {{!!title}}

For the cases where they give the same output: Which is faster?

I figure it’s the latter because it directly looks at the title field whereas the former calls a variable which, presumably, in turn looks at the title field. Correct?

If there is a speed difference, it’d be interesting to get a grasp of it. (It could be significant in e.g complex filtering or nested lists)

tl;dr

It doesn’t matter and any gains you could gain are meaningless and much smaller than gains you’d get from anything else. Use whatever you like best and use it always.

Below is a verbose answer with some well-meaning-but-might-sound-serious jabs about microoptimization.


It doesn’t matter.

Any gain in speed is insignificant.

It’s increasing your car mileage by emptying the ashtray.

But I know how it is to microoptimize, thank goodness I grew out of it as a programmer. So here are the results of my tests:

Ran 3 tests, 1000000 times each, everything was repeated 50 times
##### [all[current]] #####
    Total time: 42685ms
    Average repeat (1000000 runs): 853.7000ms
    Average test run: 0.0009ms
##### [<currentTiddler>] #####
    Total time: 29842ms
    Average repeat (1000000 runs): 596.8400ms
    Average test run: 0.0006ms
##### [{!!title}] #####
    Total time: 34223ms
    Average repeat (1000000 runs): 684.4600ms
    Average test run: 0.0007ms

MEANINGLESS I tell you

  • A typical computer will have refresh rate of 60 Hz, which gives you 16 milliseconds per frame. Regardless of the method, you need to use this filter operator more than thousand times to get close to it taking a full millisecond.
  • Go to my plugin showcase, take a look at the Advanced Performance footer, click on the search bar. On the same computer are ran the above tests just switching the focus takes 12ms. That’s 12 000, twelve hundred, retrievals of current tiddler.
  • Optimize anything else because, I can bet you money, anything else you optimize will give you an order of magnitude better results than this.

But if you insist:

  • [<currentTiddler>] consistently gives the best time. Makes sense because it’s just a variable lookup.
  • [{!!title}] consistently is in the second place. Makes sense because it does three things:
    • Variable lookup.
    • Tiddler retrieval.
    • Reading the field from the tiddler.
  • [all[tiddler]] consistently gives the worst time. Makes sense because it does four things:
    • Run the filter operator.
    • Do the three things [{!!title}] has to do.

It still doesn’t matter

Here is the code if you’d like to try it yourself. I made sure to prepare the fake data such that each filter does return test:

(function run(){
    const attempts = 1000000;
    const repeats = 50;

    const attempt = function(callback) {
        const start = Date.now();
        for (let i = 0; i < attempts; i++) {
            callback();
        }
        const end = Date.now();
        const delta = end - start;
        return delta;
    }

    const getVariableFauxWidget = (name, value) => {
		return {
			getVariable: function (name_) {
				if (name_ === name) {
					return value;
				}
				return "";
			}
		}
	}

    const tempTiddler = new $tw.Tiddler({title: 'test'});
    const filter = (f) => {
        $tw.wiki.filterTiddlers(f, getVariableFauxWidget('currentTiddler', 'test', (callback) => {
            callback(tempTiddler, 'test');
        }));
    }

    const tests = [
        ["[all[current]]", () => attempt(() => {filter('[all[current]]');})],
        ["[<currentTiddler>]", () => attempt(() => {filter('[<currentTiddler>]');})],
        ["[{!!title}]", () => attempt(() => {filter('[{!!title}]');})],
    ]
    const results = tests.map(x => []);
    for (let i = 0; i < repeats; i++) {
        for (let j = 0; j < tests.length; j++) {
            results[j].push(tests[j][1]());
        }
        console.log(`Tests done in ${(100*i/repeats).toFixed(2)}%`)
    }

    console.log(`Finished running tests!`)
    console.log(`Ran ${tests.length} tests, ${attempts} times each, everything was repeated ${repeats} times`);
    for (let i = 0; i < tests.length; i++) {
        const [name] = tests[i];
        const testResults = results[i];
        const totalRuntime = testResults.reduce((total, next) => total + next);
        const totalRuns = attempts * repeats;
        const averageRepeat = totalRuntime / repeats;
        const averageRun = totalRuntime / totalRuns;

        console.log(`##### ${name} #####`);
        console.log(`Total time: ${totalRuntime.toFixed(0)}ms`);
        console.log(`Average repeat (${attempts} runs): ${averageRepeat.toFixed(4)}ms`);
        console.log(`Average test run: ${averageRun.toFixed(4)}ms`);
    }

})()
7 Likes

Wow, thanks for running the numbers! Great reply and good to know “once and for all” :smiley: