Thursday | 21 NOV 2024
[ previous ]
[ next ]

The Template Language - Performance Check

Title:
Date: 2023-06-20

Table of Contents

  1. Speed Attemp 1 - Didn't work

A Better Attempt

Now that I have a rough shape of my language done and the core parts of it working, it's time to turn my attention to the speed issues. It's likely that there is an issue with the way I structured things but I should still be able to make some improvements. The first thing is to test things though.

I'm going to compare the BASIC loop of selecting and building a list of my posts. There are 500 records currently and in BASIC the code looks like:

   T1 = TIME()
   FOR I = 1 TO NUMBER.OF.POSTS
      X = POSTS<I>
   NEXT I
   T2 = TIME()
*
   PRINT 'BASIC Took: ' : T2 - T1 : 's'

The code for my rendering is:

{{ FOR I = 1 TO NUMBER.OF.POSTS }}
    {{ X = POSTS<I> }}
{{ NEXT I }}

I time my render function around the CALL statement in my BASIC routine.

The timings are:

BASIC Took: 0.0003s
Template Took: 1.0649s

BASIC is roughly 300-500 times faster than the template. This is a pretty big margin and I'm hoping there are some low hanging fruit that I can grab. I'm sure that this is actually much worse for complex templates.

The most basic render program:

{{ FOR I = 1 TO 500 }}
{{ NEXT I }}

Timing:

Template Took: 0.019s

The fastest possible time for my render with all of the logic stripped out is 19ms. This is about 5-10 times slower than the BASIC loop with logic. This is probably the lower bound of how faster my template engine can be. It likely can't actually come close this due to all the recursion and subroutine calls I make.

A variable look up:

{{ FOR I = 1 TO 500 }}
   {{ I }}
{{ NEXT I }}

Timing:

Template Took: 0.0653s

This is about 200 times slower than the BASIC program. This likely means that the issue is related to the variable look up which I might be stuck with unfortunately. I'm already using hashmaps so I think this should be faster than using the LOCATE statement. It might be faster to do the hashmap logic as internal subroutines but that would require quite a bit of work that I'm not sure if it's worth yet.

Inside my RAW.RENDER function is:

   FOR LOOP.INDEX = LOOP.START TO LOOP.END
      CALL MAP.SET(MAT ENV,ENV.SIZE,LOOP.VAR,LOOP.INDEX)
      CALL RAW.RENDER(MAT ENV,ENV.SIZE,MAT FILES,FILE.CTR,BLOCK,RESULT.TEMP)
      RESULT = RESULT : RESULT.TEMP
   NEXT LOOP.INDEX

This is where the bulk of the time comes from.

MAP.SET - 10ms
RAW.RENDER - 50ms

The recursive RAW.RENDER is where the bulk of the time is spent.

Inside the RAW.RENDER the time is spent in the EVALUATE call.

The EVALUATE call is about 30ms.

Speed Attemp 1 - Didn't work

I changed the REGISTERS variable from dynamic to dimensioned and that somehow caused a slowdown of 200ms. Interesting. The allocation of the variables for a dimensioned array might be slow here. I wonder if this would be better if I went 100 to 100. I didn't check this. This could be a fun check in the future. Changing the dimension size to 10 did nothing for the speed.

The EVALUATE subroutine is probably where I need to get my performance. This is the tightest part of the loops and this is the subroutine that gets called constantly. Currently it looks each iteration of the loop in that simple FOR loop is 1ms which is extremely expensive.

There is some tips here about using <-1> and dealing with large items. I haven't tried these methods yet as the EVALUATE loop shouldn't end up with giant text strings. It might be the issue is somewhere else.