Caching

Simba tutorial on caching to improve performance.

This tutorial is going to be on caching, caching is a great and easy way to improve performance.

First of all, what is a cache and/or caching? In few words, it's just storing a result or a variable in another variable to use later.

It's possible you already do this and didn't know what it was since it's something pretty easy and common to do.

You usually want to cache something when you either need to use a function several times and you know the result won't change or because you are going to update a variable but want to access it's previous value later. You might also want to cache a result of a function to compare it later to see if the result changed.

For example:

var
  RSW: TRSWalker;
  TickCountCache: UInt64;

begin
  RSW.Setup('world'); //I choose world map on purpose because it's super slow!

  while True do
  begin
    TickCountCache := GetTickCount;
    WriteLn('Position: ', RSW.GetMyPos);
    WriteLn('This took ', GetTickCount - TickCountCache, 'ms to compute'); 
  end;
end;

In this example we cache GetTickCount in TickCountCache to calculate how long we took to perform WriteLn('Position: ', RSW.GetMyPos);

Now let's say we want to improve the performance of our script.

This is a extremely simple script it only gets your position and prints it.

This example is not even useful in a real script scenario but anyway, because RSW.GetMyPos is actually quite slow, specially in the full 'world' map and because we know we will not move while the script is running, RSW.GetMyPos will always return the same coordinates so we can improve the performance dramatically by checking RSW.GetMyPosonce and caching it's result, then we print the cache!

We can do so like this:

var
  RSW: TRSWalker;
  Position: TPoint;
  TickCountCache: UInt64;

begin
  RSW.Setup('world');
  Position := RSW.GetMyPos;

  while True do
  begin
    TickCountCache := GetTickCount;
    WriteLn('Position: ', Position);
    WriteLn('This took ', GetTickCount - TickCountCache, 'ms to compute'); 
  end;
end;

You see now that this will be blazing fast compared to the previous example. However like I said before, this example only works properly the result doesn't change throughout the script executive.

If you were to move while the script is moving not be printing you actual position anymore while the first script would still work, so keep that in mind.

That's about everything I can teach you about caches they can be very useful when you are creative with them.

If you want to check some examples have a look at TBaseBankScript.BankTab which I use to cache a script bank tab or SRL's TRSMM2MS.ZoomLevel which is used to cache you zoom level.

Lastly we can use it to figure out if RSW.GetMyPos changed.

var
  RSW: TRSWalker;
  Position: TPoint;

begin
  RSW.Setup('world');
  Position := RSW.GetMyPos;

  while True do
  begin
    if Position = RSW.GetMyPos then
      WriteLn('Position didn''t change')
    else
    begin
      Position := RSW.GetMyPos;
      WriteLn('New position: ', Position); 
    end;
  end;
end;

Now we are using the cache to compare it to a previous result, if the new result is different we update it.

But if you notice, when we get a new result we are actually RSW.GetMyPos twice in that loop, we could also improve the performance of that by caching it as well!

For example:

var
  RSW: TRSWalker;
  PrevPos, NewPos: TPoint;

begin
  RSW.Setup('world');
  PrevPos := RSW.GetMyPos;

  while True do
  begin
    NewPos := RSW.GetMyPos;
    if PrevPos = NewPos then
      WriteLn('Position didn''t change')
    else
    begin
      PrevPos := NewPos;
      WriteLn('New position: ', NewPos); 
    end;
  end;
end;

Now we will only ever call RSW.GetMyPos once per loop, which gives us the best performance possible for this particular script.

I hope you found this quick tutorial useful!