Online documentation - Websydian v6.1 |
This document is the result of an investigation of the performance of Websydian TransacXML.
The focus on the performance investigation has been the handling of large documents in the Java variant. However, this investigation has also shown a number of general and some very specific facts that you can take into consideration when you develop applications where the performance of TransacXML is critical.
Most of these considerations are relevant if you handle large XML-documents - but in special situations, they might also be relevant for smaller documents. So if you are going to work with large documents or if you see performance issues when working with smaller documents, we recommend checking the issues described in this document.
The considerations are not a list of absolute answers - but if you do need to optimize the performance of the XML handling, they are a place to start.
All functions that inherit from XMLShell inherit a validation subroutine that performs validation of the object references specified in the input parameter of the function.
This validation can be quite valuable during development - but when you have created the correct calls to the functions, we often see that the validation really isn't necessary anymore.
You can avoid this validation by specifying the following triple for the XMLShell function for each of your XMLElement entities:
Source Object | Verb | Target Object |
---|---|---|
MyXMLElement.XMLShell | function option NME
...value SYS |
ValidateInput
No |
After this, you must generate and build the functions scoped to the MyXMLElement entity.
Especially when using the TransacXML import tools, it is very easy to just create definitions for all the fields (attributes and simple elements) contained in a document.
Even if you make the definitions manually, the most natural thing to do is to define the total content of the document in the Plex model.
However, you need to be aware that when you call a SingleFetch function, each field that is defined in the Plex model result in a number of calls to the low-level DOM-layer of TransacXML.
This means that if you want to model the following document - and only need to read the name and the e-mail of the person:
Then you should only make the following definitions:
Source Object | Verb | Target Object |
---|---|---|
top | is a ENT | XMLElement |
top | includes ENT | person |
top.person | is a ENT | XMLElement |
top.person.fields | field | name
|
top.person.fields.name | is a FLD | ElementField |
top.person.fields.email | is a FLD | ElementField |
top.person | has FLD | top.person.fields.name
top.person.fields.email |
This will reduce the time needed for a SingleFetch call to around 10 % of time needed if you had defined all of the fields.
Similarly, when you call the InsertRow / InsertRowDangling functions, the number of DOM calls is directly related to the number of fields that is defined for the scoping XMLElement.
You can read the instances of a repeating element field using two different methods:
1. Use RepeatingElementField.GetFirst to retrieve the first instance of the field - then call RepeatingElementField.GetNext to retrieve the rest of the instances.
2. Call the RepeatingElementField.Process function - this will read all instances of the field automatically.
The second option should always be used for performance-critical applications. The performance impact is directly related to the number of instances of the repeating element field under the parent element.
If you are using Plex 5.5 SP1 (or earlier) - the best thing you can do to improve the performance is to upgrade to Plex 6.0 (or Plex 6.1).
The processing time when using Plex 6.1 is reduced to around 50 % of the processing time for Plex 5.5SP1.
If you have to keep on using Plex 5.5 SP1 you must at least be at build 93.025. Earlier builds contains memory leaks that severely degrades the performance of TransacXML.
When using Plex 6.0 or Plex 6.1 it is extremely important that the dll-caching implemented by Plex is enabled.
In Plex 6.1, this should be the default setting. The dll-caching is controlled by the ini-file setting:
[Settings]
Cache DLL References=False
or
[Settings]
Cache DLL References=True
The setting should always be set to True.
We have seen processing times that went from under 2 seconds to around 30 seconds when the dll-caching was removed.
We have seen a number of cases, where virus scanners had a significant performance impact. It seems that the opening and closing of dll files can trigger a scan - even when the dlls are cached.
If you have the option, exclude the folder containing the dlls from the virus scanner.
If you can't do this, make the XML-functions (SingleFetch, GetFirstOccurrence, InsertRow etc.) internal. This seems to change the way the virus scanner handles the scanning of the files.
In one severe case, we have seen a program that takes 5 seconds to execute without the virus scanner take 7 minutes when the virus scanner was active.
A rather surprising finding in our performance investigations has been the effect of specifying NULL for the parent element when calling GetFirstOccurrence functions when using the WinC variant.
As an example, consider the following document:
If you want to read the values for the fields A, B, and C, the common approach would be to start by calling First.GetFirstOccurrence, then First.Second.GetFirstOccurrence, specifying the output from First.GetFirstOccurrence as the parent element - and so on until you would call First.Second.Third.Fourth.Fifth.GetFirstOccurrence specifying the output from First.Second.Third.Fourth.GetFirstOccurrence as the parent element.
A much simpler way to do this is to call First.Second.Third.Fourth.Fifth.GetFirstOccurrence - and just specify NULL as the parent element.
The surprising thing is that it is also quite a lot faster to do so.
In fact it seems that even though you have already called First.Second.Third.Fourth.GetFirstOccurrence - and in this way already have got the identification of the of the Fourth element in the document - it can still be faster to specify NULL for the parent element when calling First.Second.Third.Fourth.Fifth.GetFirstOccurrence.
So the recommendation is to always use NULL for the parent element, when there is no reason not to do so.
An example, where you can't just use NULL for the parent element is shown below:
In this case, you will always get the first <Fifth> element if you specify NULL for the parent element when calling First.Second.Third.Fourth.Fifth.GetFirstOccurrence. So in this case, you will have to find the reference to the correct Fourth element - and use this reference as the parent element.
Generating for the action diagram debugger has seriously adverse effects on the performance - even when you have not checked "Build for action diagram debugging" generate and build setting when building the application.
When generating for action diagram debugging, we have seen up to 10 times increase of processing time - so this is an issue you have to take seriously.
So if you have used the AD debugger at any point, ensure that you have regenerated and rebuilt all the objects before evaluating the performance - or even more important: before moving the objects to production.
For the Java variant, it can be very important to specify a parent element when calling the GetFirstOccurrence function.
A very simple way to find the "Fifth" element in the following document would be to just call First.Second.Third.Fourth.Fifth.GetFirstOccurrence - and just specify NULL as the parent element.
This will work in the Java variant. However it can't be recommended unless you are working with quite small documents. Instead you should:
1. Call First.GetFirstOccurrence (specify null as parent element as this is the top element).
2. Call First.Second.GetFirstOccurrence (use output from First.GetFirstOccurrence as parent element).
3. Call First.Second.Third.GetFirstOccurrence (use output from First.Second.GetFirstOccurrence as parent element).
4. Call First.Second.Third.Fourth.GetFirstOccurrence (use output from First.Second.Third.GetFirstOccurrence as parent element).
5. Call First.Second.Third.Fourth.Fifth.GetFirstOccurrence (use output from First.Second.Third.Fourth.GetFirstOccurrence as parent element).
This is a more complex way to get the reference to the "Fifth" element - but for large documents and for cases where the element you want to retrieve is placed "deep" in the document the performance is significantly better (when using the Java variant).
The general experience is that it can be a problem to run performance-critical Plex-generated TransacXML Java applications on the iSeries.
You should be aware that even if you need to access data or call RPG programs on the iSeries, you can still run the Java programs on a PC/Windows server by either accessing the database using JDBC or making remote calls to Plex generated RPG programs using the Plex dispatcher.
If you must run your Java programs on the iSeries, we recommend that you look into using the IBM 32 bit JVM instead of the standard 64 bit JVM.
The 64bit JVM is also known as the classic JVM and the 32bit version is known as the standard JVM.
Our tests show that a consistent performance improvement is achieved by switching to the 32 bit JVM.
The default initial heap size seems to be 100 MB. This does not mean that the application does not run if it needs more memory (the default max heap size seems to be unlimited) - but it does mean that the garbage collector will be be active as soon as the heap size gets close to 100 MB.
Increasing the initial heap size does improve the performance significantly.
The initial heap size is set using the -Xms option on the java command. E.g. in order to set the initial heap size to 512 megabyte use the parameter -Xms512m.
To get an idea of how the Java performance is on the current iSeries models, we have run a number of tests on a PC, on the iSeries using the 64 bit JVM, and on the iSeries using the 32 bit JVM.
All tests has been performed using Java 5.
The PC is a Dell XPS720 using an Intel Quad Core 2.40 GHz CPU. The PC has 2 GB RAM.
The iSeries is a 520 8203-E4A 4300 CPW 1-core with 16 GB RAM running V5R4.
We have distinguished between write and read operations, as it seems the performance is a bit different for these operations.
In the table, the average processing time of the test when run on the PC is set to index 100.
PC | iSeries 32 bit | iSeries 32 bit (1024 heap) | iSeries 64 bit | iSeries 64 bit (1024 heap) | |
Write | 100 | 200 | 175 | 400 | 335 |
Read | 100 | 175 | 135 | 280 | 260 |
We have tried to optimize the Java code on the iSeries using CRTJVAPGM - without seeing any significant performance improvements.
We have also tried to set the initial heap size to 2048 MB - but this does not give any significant improvements.
When we run the tests on the PC, one of the 4 processors are being used fully. When we run the tests on the iSeries, we get around to 90 % CPU usage.
The iSeries was newly installed - it did not execute anything else while the tests were being run.
No performance tests can be valid for all cases. The above results should only be used as an inspiration to where you can gain additional performance for your Java application. It is important that you perform your own tests on the cases where you see the performance issues.
If you have a performance-critical application, we recommend that you try to run the application both on the iSeries and on a PC-server so that you can compare the performance.