SRL Archive - OSR Help

Global DTMs and you


Cigue

2010-01-29 19:12:34

Do you think it's a good idea to declare all my DTMs (6 of them) locally to save me the hassle of making sure they're there when I need it? or will it slow down the script a lot?

noidea

2010-01-29 19:19:09

It will slow down the script a lot. I'd just create them when needed, and then free them.

Smartzkid

2010-01-29 20:51:15

It depends how often you use them.

Cigue

2010-01-29 21:47:14

I'll probably keep them global for testing purposes, and then make them local before I release script. :p

Awkwardsaw

2010-01-29 22:39:50


function loaddtm(dtm: string):integer;
begin
case lowecase(dtm) of
'dtm1' : result := DTMFromString('78DA63F4626260C8636440054C601226CAE80' +
'6E4371250130EE4971350630FE4571250130AE41710501348847B' +
'5C80FC7CFC6A0004E80581');
'dtm2' : result := DTMFromString('78DA63F4626260C8636440054C601226CAE80' +
'6E4371250130EE4971350630FE4571250130AE41710501348847B' +
'5C80FC7CFC6A0004E80581');
end;
end;

function isdtm(dtm : string):boolean;
begin
result := finddtm(x, y, loaddtm(dtm), blah blah blah);
freedtm(loaddtm(dtm));
end;

begin
if isdtm('dtm1') then writeln('yay :D');
end;

etc :)

remember to free dtms

Smartzkid

2010-01-29 22:48:27

From: Awkwardsaw

function loaddtm(dtm: string):integer;
begin
case lowecase(dtm) of
'dtm1' : result := DTMFromString('78DA63F4626260C8636440054C601226CAE80' +
'6E4371250130EE4971350630FE4571250130AE41710501348847B' +
'5C80FC7CFC6A0004E80581');
'dtm2' : result := DTMFromString('78DA63F4626260C8636440054C601226CAE80' +
'6E4371250130EE4971350630FE4571250130AE41710501348847B' +
'5C80FC7CFC6A0004E80581');
end;
end;

function isdtm(dtm : string):boolean;
begin
result := finddtm(x, y, loaddtm(dtm), blah blah blah);
freedtm(loaddtm(dtm));
end;

begin
if isdtm('dtm1') then writeln('yay :D');
end;

etc :)

remember to free dtms


That doesn't work.

program New;
begin
writeln(DTMFromString('78DA635CC2C8C0F0940105DCBD79094C3342F98C6540D67C5435FFFFFF475103003C23082A'));
writeln(DTMFromString('78DA635CC2C8C0F0940105DCBD79094C3342F98C6540D67C5435FFFFFF475103003C23082A'));
end.

Even though they are the same DTM, they are loaded separately.

If a dtm is used often, keep it global. If it is only used once in a while, then leave it local.

It comes down to a memory/speed tradeoff.
Global = more memory usage, more speed;
Local = less memory usage, but slower.

Cigue

2010-01-29 23:49:44

Sorry Smartz, but it'd work. In fact it looks like a great idea :)

Awkwardsaw

2010-01-30 00:10:31

From: Smartzkid
That doesn't work.

program New;
begin
writeln(DTMFromString('78DA635CC2C8C0F0940105DCBD79094C3342F98C6540D67C5435FFFFFF475103003C23082A'));
writeln(DTMFromString('78DA635CC2C8C0F0940105DCBD79094C3342F98C6540D67C5435FFFFFF475103003C23082A'));
end.

Even though they are the same DTM, they are loaded separately.


wat?

Smartzkid

2010-01-30 01:13:14

The program I posted prints out the memory handle to each DTM. Notice that the two are different? This means each call to DTMFromString loads the DTM to a new place in memory, even though the data is the same.

I can understand your confusion; while the code 'works', it is a memory leak:
You load the DTM into memory pos 1, and search for it.
Next, you load it into memory pos 2, and free it.

The DTM in position 1 never gets freed.

Luckily, a better solution is not far away:

function isdtm(dtm : string):boolean;
var
tmpdtm: integer;
begin
tmpdtm := loaddtm(dtm);
result := finddtm(x, y, tmpdtm, blah blah blah);
freedtm(tmpdtm);
end;

The problem now, though, is that you are allocating memory, filling it up with data, and then freeing it - every time you search for a DTM. Memory operations like this take a good amount of time, so if you are using a DTM often, you should consider making it global and keeping it in memory, which will boost your script's efficiency and lower your CPU usage.

In addition, case statements in SCAR are very slow. They act as large if/else statements; thus, as your list of DTMs grows, so will the time it takes to load each one. This is an additional reason why you may want to keep some DTMs global.

Nava2

2010-01-30 03:50:55

The best option, in my opinion would be using an array of boolean, set them to true as each dtm is set which is allocated by a constant in an array. Then in the script terminate procedure, have a for loop free every loaded dtm.

I'd write some code but I'm on my iPod.

E: oh and the dtms stored in a TIntegerArray associated to the same object. To be honest a record would work here too.

Awkwardsaw

2010-01-30 08:16:54

From: Smartzkid
The program I posted prints out the memory handle to each DTM. Notice that the two are different? This means each call to DTMFromString loads the DTM to a new place in memory, even though the data is the same.

I can understand your confusion; while the code 'works', it is a memory leak:
You load the DTM into memory pos 1, and search for it.
Next, you load it into memory pos 2, and free it.

The DTM in position 1 never gets freed.

Luckily, a better solution is not far away:

function isdtm(dtm : string):boolean;
var
tmpdtm: integer;
begin
tmpdtm := loaddtm(dtm);
result := finddtm(x, y, tmpdtm, blah blah blah);
freedtm(tmpdtm);
end;

The problem now, though, is that you are allocating memory, filling it up with data, and then freeing it - every time you search for a DTM. Memory operations like this take a good amount of time, so if you are using a DTM often, you should consider making it global and keeping it in memory, which will boost your script's efficiency and lower your CPU usage.

In addition, case statements in SCAR are very slow. They act as large if/else statements; thus, as your list of DTMs grows, so will the time it takes to load each one. This is an additional reason why you may want to keep some DTMs global.


only one dtm is being called per function use, because of the case, how would both of them be called in memory?

also, the case statements "slowness" isn't noticeable at all, even then a little "pause" might be better anyways

Gu3st...

2010-01-30 08:49:06

From: Awkwardsaw
only one dtm is being called per function use, because of the case, how would both of them be called in memory?

also, the case statements "slowness" isn't noticeable at all, even then a little "pause" might be better anyways
Please do not take this the wrong way, but you should know this already.. :/

Whenever you call DTMFromString (or BitmapFromString) the DTM (or Bitmap) is loaded into the memory. So if you call your function two times with the same parameter then SCAR will load two different DTMs into the memory, but with the same information (Better explained with bitmaps: Two different bitmaps, but with all the pixels exactly the same)

Coh3n

2010-01-31 02:02:47

From: Nava2
The best option, in my opinion would be using an array of boolean, set them to true as each dtm is set which is allocated by a constant in an array. Then in the script terminate procedure, have a for loop free every loaded dtm.

I'd write some code but I'm on my iPod.

E: oh and the dtms stored in a TIntegerArray associated to the same object. To be honest a record would work here too.

Boolean arrays work wonders when used in the right situations.

Smartzkid

2010-01-31 02:12:02

Since a DTM handle is simply an integer corresponding to an index in an array, we cannot use a (dtm != 0) test as we would on a normal memory handle (the first DTM created will always be equal to 0, so it would never get freed). In addition, this test is useless without a wrapper function to free DTMs, because SCAR doesn't set handles to 0 when it frees the memory they point to.

Even so, there are a few possible solutions:

In a situation with only local DTMs, and no explicit calls to FreeDTM, this function will take care of freeing all DTMs. Thus, if called at the end of every procedure that uses DTMs, your memory will stay nice and clean.
procedure FreeAllDTMs;
var
i: integer;
begin
try
repeat
FreeDTM(i);
inc(i);
until(false)
except end;
end;

For a situation requiring both global and local DTMs, the most desirable solution is a memory management system - something along the lines of what Nava described.

Personally, I would create wrapper functions for DTMFromString and FreeDTM. These functions would add to and remove from an array of integers. This array could be inspected to glean two important pieces of information - the current number of DTMs allocated, and the handles of all current DTMs. Using this data, memory cleanup becomes quite trivial.
A rough implementation of this idea is below.

Mixter's bitmap debugging code operates on somewhat the same principal.

In a perfect world, all DTMs would be loaded into a 'DTM Manager', which would store raw DTM data and a string identifier for each DTM. To use a DTM, one would call a function GetDTM(dtmName: String): integer; which would return the handle to a DTM. Inside the GetDTM function, statistical analysis would be applied over a period of time to determine exactly which DTMs ought to be kept in memory, and which ought to be freed. The DTMs that ended up being freed would be re-allocated when needed, and freed at the next call to this function. In reality, a system like this would not be hard to set up. Rather than running complicated statistics, one could simply designate whether or not to make a DTM stay allocated ('global') or be allocated on the fly ('local'). The rest is quite easy to implement.

@Awkwardsaw: There is a big difference between inefficient code and wait statements. Inefficient code makes the processor do more work than it needs to, whereas wait statements defer processor cycles to another program. Inefficient code causes lag; wait statements prevent it.


The code below is different than what many people are used to. Rather than freeing DTMs by reference to a specific handle, they are freed by order of creation. The concept is that you create your global DTMs at the beginning of the script, and whenever local DTMs are used, you clean up afterward with a call to DTMFreeRecent with the number of DTMs used in the current procedure. If you have no global DTMs, you can clean up after each procedure with a call to DTMFreeAll.

var
xxDTMArray: Array of Integer;

procedure DTMFreeRecent(quantity: integer);
//Free the last q DTMs made
var
i, startIndex: integer;
begin
startIndex := High(xxDTMArray) - quantity;
for i := 0 to quantity do
FreeDTM(xxDTMArray[startIndex + i]);
SetLength(xxDTMArray, startIndex);
end;

function DTMCreate(s: String): integer;
//Create a DTM from input string s
var
size: integer;
begin
result := DTMFromString(s);
size := Length(xxDTMArray);
SetLength(xxDTMArray, size + 1);
xxDTMArray[size] := result;
end;

function DTMQuantity: integer;
//Get number of DTMs currently in use
begin
result := Length(xxDTMArray);
end;

procedure DTMFreeAll;
//Free all DTMs
var
i: integer;
begin
for i := 0 to high(xxDTMArray) do
FreeDTM(xxDTMArray[i]);
SetLength(xxDTMArray, 0);
end;