Friday, 20 March 2009

Memory Usage

Reducing the memory usage of our load modules is something that we are working on at present, so to kick off the blog with a very technical subject...

Much of the code that we develop relies on repeating group views to process lists of objects.

Group views in Gen need to have a fixed, maximum size, and memory for the group view is allocated at program load time. This means that there is a trade-off between the memory usage of a load module and the limits imposed by the group view size. In many cases you can size the group view to cater for the maximum number of rows that are needed, or repeat processing with some sort of 'start from' value.

However there are quite a few instances where this is not possible, and then the group view size becomes a fixed limit on the data that can be processed by the function.

Over the years we have increased these limits based on customer requirements, and this has resulted in an increased memory requirement for the load modules. This is particularly noticeable in Windows and UNIX environments where the server load modules are kept in memory by the Transaction Enabler. (Note that there are tuning parameters for the number of load modules that are loaded by the TE).

We have also made more use of DLLs in Windows and UNIX (Operations Libraries) and these can also increase the memory requirement if they result in a load module ending up loading a DLL for one action block and therefore also allocating the memory for all of the other action blocks in the opslib and any dependent opslibs. Our models have a lot of shared and reused code, and complex dependencies between modules has meant that many of the DLLs are loaded multiple times because of code dependencies, even if they are not required for each execution.

We are therefore working on some strategies to reduce memory usage. Ones that we have come up with so far include:

1) Delete unused views and attributes
2) Improve use of perfect view matching
3) Reduce use of uninitialised local views
4) Share group views between action blocks
5) Delay loading of DLLs
6) Delay allocation of local view storage

The first four are all standard coding techniques.

1) Maintaining an action block over many years (some of our action blocks are now 20 years old) can result in quite a few unused views. Finding and deleting these can be tedious and time-consuming, so we developed some tools in VerifIEr and genIE to automate finding and then deleting the unused views and also unused attribute views.

2) If view matching is not perfect, extra working storage is required in the generated code, so improving view matching (especially for large group views) will help.

3) In C generated code, having even one uninitialised local view will double the memory usage of all of the local views, so avoid this if you can.

4) An example of sharing memory is where many action blocks require a large local group view with the same structure. For example, we have a case where many action blocks need a 1Mb group view and each is called from the same parent. If each action block has its own local view, then each AB needs 1Mb of storage. However if the group view is defined as an exportable import view and is passed from a local view in the parent, then they all share the same storage. You then need to ensure that the view is properly initialised, but there is a big memory saving. In one load module we managed to halve the memory usage using this technique.

5) On windows we are experimenting with using the /DELAYLOAD: linker option to delay loading of opslibs. If the route through the code never requires a function in the opslib, it will not get loaded.

6) This is very much work in progress, so more on this later...

7 comments:

Anonymous said...

I accidentally stumbled onto this blog just now, and added it to my favorites straight away.

Thanks for the tips Darius.
This is a great idea. I will follow this blog regularly. I wish you good luck and fun with it;)

A, the Netherlands

Anonymous said...

what i do when matching the group is arrange the sequence by work view first, entity second, group view third. is it true it will increase the performance?

Anonymous said...

a little bit confused at
"4) Share group views between action blocks"

if you don't mind, please share with example.

Darius Panahy said...

The sequence of the views will not change performance, but the order of the attributes within a view will if it affects perfect view matching.

Darius Panahy said...

An example of (4) is:

AB1 calls AB2, AB3, AB4, etc.

AB2,AB3 & AB4 use a local group view. If each has its own local group view, there are 3 copies of it. However if the group view is defined as local in AB1 and passed into AB2,3,4 as an exportable import view, then AB2,3,4 can use it as if it were a local view and the storage is allocated once in AB1.

Anonymous said...

Darius - is this one of the built-in checks in the VerifIEr product i.e. could VerifIEr tell us where the views are not perfectly matched and help us get a performance boost from our application ?

Darius Panahy said...

Gentalk - Yes, VerifIEr has a check for perfect view matching. It can check all USE statements, or only those that have group views, USE statements that are within a loop (READ EACH, REPEAT, etc.) or group views and loops.