When it comes to performance tuning, make sure you concentrate your efforts where they'll make the most difference.
Brief: Most programmers have a portfolio of techniques they believe will make their code perform better. The tests presented in this article show that while most of the standard tricks have an effect, in many cases it is so small that it becomes unimportant. By studying the results presented in this article, you'll be able to make the correct trade-offs between performance and readability of your RPG/400 code. You'll also be able to concentrate your efforts where they'll have the most impact.
There are many alternatives for improving performance on the AS/400 and many are surrounded by rumor and conjecture. In this and upcoming articles in Midrange Computing, I'll explore alternatives that work and those that don't. I won't discuss any of the performance tools; they are after-the-fact solutions. Instead, we'll talk about what you should do when you are designing and coding.
This article concentrates on a group of questions about performance that I hear frequently. These questions all take the general format: Is it better to code this way or that way in RPG? In most cases, there is a very easy answer-it doesn't matter! Almost all the tricks we've learned for writing faster RPG code do make the code perform better. But the difference on the current generation of AS/400s is usually so small that you'd have to process millions of records before you'd really notice the effect.
When I show you some performance tests that I've run, you'll see that, in most of these cases, the coding techniques matter only to the programmer-no user will ever be able to measure the impact on performance. I ran the tests on my Model D02 AS/400. (Refer to the "Testing Methodology" sidebar for details about how the tests were run.) The D02 is not the slowest of models, but it's close. If you have an F95, your CPU is about 50 times faster than the numbers I'm going to show you.
Determining how many seconds your system is actually going to take is not the purpose of these tests. What I want to show you is the relative performance difference of one method versus another. Unless the tests show a significant difference, one method versus another isn't worth considering. The raw test data is presented on page 92-the numbers correspond to the numbered information within the text. In most cases, I have used 50,000 iterations for each test; but I've made some exceptions, particularly in the array processing tests. The number of iterations is included for each test.
Some easily implemented techniques do have a significant impact on performance; I'll point them out so you can concentrate your resources where they'll make the most difference. I've grouped the results of my tests into three sections: No significant performance impact; Significant impact under certain circumstances; and Significant performance impact.
No Significant Performance Impact
1. Moving a small number of characters versus a large number.
If you read 50,000 records and perform a MOVE operation for every record, the differential between moving one character and 10 characters on my D02 is .3 seconds. On an F95, you would have to read 10 million records (not 50,000) and perform the move on each record before you generated a one-second differential. Moving a little bit of data doesn't cost very much but, as you can see from the test results, the difference between moving one character and 5,000 is worth considering.
Conclusion: No significant impact, even with millions of records.
2. Testing one character versus eight characters.
This test addresses the question: what does it cost to test for a one-character condition...
SWITCH IFEQ *ON
...versus an eight-byte switch?
FIELD1 IFEQ 'ABCDEFGH'
Testing the one-byte switch 50,000 times increased the CPU time of the base case by less than one-tenth of a second. Although the test for eight bytes can be measured (it adds 1.5 seconds over 50,000 records), the difference is so small that it almost doesn't matter. In general, the system is very efficient when testing and moving one-byte fields. But even movement of larger fields is relatively efficient. If you are only going to execute the operation 100 times a day, find something else to worry about.
Conclusion: Some impact, only when massive numbers of transactions are involved.
3. Rearranging date fields using MOVE exclusively versus using a data structure.
Rearranging a date format from MMDDYY into YYMMDD is typical of many applications. I compared two ways to do this: rearranging the components with a series of four MOVE and MOVEL instructions (1) and rearranging the fields using two MOVE instructions with data structure subfields (2). Another solution that some people use involves multiply or divide operations. This is generally a poor performer (see Example 11).
Rearranging a date format from MMDDYY into YYMMDD is typical of many applications. I compared two ways to do this: rearranging the components with a series of four MOVE and MOVEL instructions (Figure 1) and rearranging the fields using two MOVE instructions with data structure subfields (Figure 2). Another solution that some people use involves multiply or divide operations. This is generally a poor performer (see Example 11).
For 50,000 iterations, the difference between the two methods was only .3 seconds with the data structure method holding the edge. I'd say "so what?" unless you intend to execute this function millions of times per day. Choose whichever method you find clearer. Data structures help reduce the total number of operations you need to perform; so if you are assembling multiple fields, they begin to pay off.
Conclusion: Data structures are slightly better, but choose the solution you find clearest.
4. MOVEs versus arithmetic operations.
One of the most pervasive performance beliefs is that there is a significant difference in using arithmetic versus moving data. In truth, there is some difference, but very little to shout about. On 50,000 transactions, the difference between MOVE and Z-ADD for a five-position field is only .3 seconds on my D02. RPG operates on packed-decimal data to do arithmetic, and simple add or subtract operations are very efficient on the AS/400. The answer is not the same for multiply or divide (see test 11).
Conclusion: Both methods are efficient.
Significant Under Certain Circumstances
The next group of tests shows some clear performance benefits to choosing one method over the other. Just be sure to weigh the benefits against the cost. Minimum CPU time is an inexpensive commodity compared to the cost of maintaining code that is not clear. So bear in mind the evidence I'm presenting, but don't forget to consider other factors in your program design.
5. In-line code versus subroutines.
Although RPG subroutines (EXSR) are a very good coding technique, my tests show that subroutines do have a price. Executing an empty subroutine (no calculations in the subroutine) 50,000 times requires about .7 seconds above the base-case processing time of 3.1 seconds. Executing a second empty subroutine takes an additional .7 seconds. The difference was more than I expected, but not enough to make me want to give up on subroutines. If you have something very simple to do and you need to do it many times, consider coding it in-line. I still like subroutines from a programming design viewpoint.
Conclusion: Subroutines are not free.
6. Odd-length numeric fields versus even-length fields.
I'm sure you've all heard that odd-length decimal fields are better than even- length fields for packed-decimal arithmetic. The reason that even-length fields are less efficient is that the compiler generates instructions to zero out the high-order digit (the field is packed so there is an extra digit you don't need). In my tests, I found a difference of .7 seconds between the odd- and even-length fields over 50,000 iterations. In general, I would stick with odd- length fields when odd or even doesn't make any difference. If you need an even-length field for aesthetic reasons, it isn't a bad performer unless you have a lot of calculations.
Conclusion: Use odd-length fields unless circumstances specifically call for an even-length field.
7. Packed versus zoned numeric data for arithmetic operations.
Since RPG performs arithmetic only on packed decimal fields, the compiler generates code to convert a zoned field to packed and then convert back to zoned after performing the operation. Compared to our previous tests, the cost is higher-1.4 seconds-to add a constant to a seven-digit field 50,000 times. I would try to stay away from zoned and binary fields when designing your database, but the performance impact would probably not be noticeable unless you have a lot of calculations.
Conclusion: Use packed decimal fields whenever possible.
8. Using a binary field versus a packed decimal field for an array index.
When you use a field name as an index to move from an array, RPG has to determine where you are moving from and ensure that you are not outside of the array limits. The system converts the index to a binary value and then performs the multiplication (length times index equals the start point); binary is often more efficient for internal functions. Because the internal array handling is done in binary, some people think they should keep the array index in binary form. In fact, the opposite is true. As with all other arithmetic operations, it is more efficient to define an array index as packed decimal. For 99,999 iterations of a loop which increments the index and moves an array element, a binary index consumed 12.1 additional CPU seconds. Because RPG deals externally in packed decimal, it converts every use of a binary field to decimal and then converts back. It doesn't have the smarts to handle the index without causing extra overhead.
Conclusion: Follow the standard packed data conclusion (test 7) for best results.
Significant Performance Impact
The next group of tests point out some coding techniques that can have a significant impact on performance. These results can help you to concentrate resources where they'll make the most difference. Several of the examples in this section deal with array processing. This is because an application which processes an array can easily execute millions of instructions. Bear in mind that we are considering performance benefits measured in seconds over thousands of records. If you're processing a few hundred records, you can probably safely ignore the implications of these tests. Likewise you should not allow these considerations to override maintainability considerations.
9. Binary search versus LOKUP for large arrays.
When you have an array used for lookup, the normal function is to search through the array looking for a match. Unless the most frequently found items are first in the array, you look through half the array on the average. If the search argument does not exist in the array, you look through all the elements of the array. For arrays above 100 elements or applications with a high percentage of unsuccessful searches, this overhead can be reduced by using a binary search algorithm.
The RPG compiler has no syntax for a binary search which would be useful in these circumstances. For my tests, I compared a binary search using the QUSRTOOL Binary Search (BINSEARCH) tool versus an RPG LOKUP operation. BINSEARCH is implemented with standard RPG/400 code that you copy into a program, thus avoiding the need to pass data to and from the search program.
For each test, there were 5,000 search operations against an ordered array (the binary search technique requires that the array be in sequence). No invalid search arguments were submitted, so the search was successful in each case. For 100 three-byte elements, the binary search produced only a .6-second performance improvement; but for 500 elements, the improvement increased to 17.7 seconds.
The binary search method performs better than LOKUP as soon as the number of elements reaches about 100. It would also make sense on a smaller number of elements if the element length was longer or you expected a high percentage of unequal lookups.
Conclusion: Worth the effort for large arrays or if the number of unsuccessful searches is high.
10. Using data structures for array output versus output by array name.
When an array name (the entire array) is specified on the output specs, RPG loops to output each element rather than moving the entire array. Place the array in a data structure and output the data structure name for a significant performance improvement. This is particularly true for arrays that contain many elements.
The data structure answer doesn't change as long as the total array length is the same. In other words, a 10-element, 10-byte array would produce the same results as a 100-element, one-byte array.
A test that outputs a 10-element, 10-byte array 50,000 times shows a 3.5 second difference between the two methods. Change the array to a 100-element, one-byte array and the difference increases to 34.4 seconds (the data structure method shows no increase)! The cost to output a 100-element array using the output specs is very significant-use data structures if you are going to output arrays.
Conclusion: Use data structures to output arrays of any significant size.
11. Using multiply and divide operations.
For this test, 10,000 records were processed using three different sets of calculations with one instruction that varied: a Z-ADD, a MULT or a DIV operation. The Z-ADD test required 9.5 seconds; the DIV test required 29.2 seconds (a difference of 19.7 seconds); and the MULT test took 48.1 seconds (a difference of 38.6 seconds over Z-ADD).
Compared to other arithmetic operations, both multiply and divide are expensive instructions. This is partly due to the fact that multiply and divide are data- dependent in terms of how long they take to execute. The actual data, the length of the fields and the number of decimal positions all have an effect on how these operations perform. Unfortunately, most business applications cannot be designed without using multiplication or division; but keep these results in mind and stay away from multiply and divide if you can or use them infrequently.
Conclusion: Multiply and divide are very expensive instructions-stay away from them whenever you can.
12. Using MOVEA versus moving data one byte at a time.
This technique applies only in specialized circumstances; but when the conditions are right, it can have a significant effect on performance. My test compared using MOVEA to copy a 100-element array (one byte per element) to another array, versus moving data one byte at a time as shown in 3. The difference on 5,000 iterations was 65 seconds. Array handling in RPG is very slow when you start moving one element at a time and you have lots of volume.
This technique applies only in specialized circumstances; but when the conditions are right, it can have a significant effect on performance. My test compared using MOVEA to copy a 100-element array (one byte per element) to another array, versus moving data one byte at a time as shown in Figure 3. The difference on 5,000 iterations was 65 seconds. Array handling in RPG is very slow when you start moving one element at a time and you have lots of volume.
Conclusion: Use MOVEA for multiple characters rather than single-element indexed MOVEs.
13. Using a character field versus an indexed array element.
This test measures the overhead required to access an indexed array element. The code moved either an array element indexed by a field (ARR,X) or a stand- alone alphanumeric field (FIELDA) into a result field. The difference was only 4.5 seconds for 99,999 iterations of this test, but it can add up rapidly when you deal with arrays.
Conclusion: Consider moving an array element to a field before multiple calculations.
14. Using RPG's built-in scan functions versus a search using an array.
RPG/400 supplies the SCAN and CHEKR op codes to search character fields. These operations replace code you may have previously written using an array of one- byte elements to search for a specific character. The big difference between these op codes and a manual process is that SCAN provides consistent performance regardless of where the search argument is found.
For example, on 5,000 iterations, the SCAN test took 2.1 seconds to find the search argument in position 3; 2.3 seconds to find it in position 25; and 2.6 seconds to find it in position 75. By comparison, the array method actually performed better when the search was satisfied in position 3 (1.9 seconds), but deteriorated quickly (9.8 seconds for position 25 and 27.8 seconds for position 75).
Scanning more than a handful of bytes definitely favors the SCAN op code. SCAN is very good when you only want to scan for a single-byte value (e.g., an *). For an efficient scan that has more capabilities than the built-in function see the "Better Array Processing" sidebar.
CHEKR produces results consistent with the SCAN model. I ran a simple test that searched for the last nonblank position in a 100-byte field-position 80 was the last nonblank in all cases. On 5,000 iterations, CHEKR was 7.6 seconds faster than scanning from the right by moving one byte at a time from an array and checking for a nonblank. CHEKR is a winner for determining the length of the data that exists in a field.
Conclusion: The SCAN and CHEKR operations are generally very good.
15. Comparison of four methods to load arrays.
In some applications, the program creates an array entry for each unique value found in the data. A corresponding array is used for some form of accumulation (for example, accumulate the month-to-date sales for each salesman number). To test this, I had a program read a database file that contained 5,000 records. A field in the record contained one of 175 unique values in random order. An array of 200 elements was created in the program and loaded with the values. A corresponding array counted how many times each value occurred. At the end of the program, the unique value array and the corresponding array were printed. I compared five different methods to load the two arrays:
o In the most straightforward approach, a LOKUP operation searches the entire array. If the target value is not found, the unique value is entered at the next available empty location. This loads the array from front to back.
o Some people like to load the array from the back end because it theoretically reduces the number of elements that have to be compared. I made this my second test case.
o I tried a third case where a random number was generated from the input field and used to directly access the array. The "hashing" algorithm I used performed a calculation with the input data which generated the theoretical array element number where data about this value is stored. If more than one value randomized to the same number extra code was executed to test the next element until either the correct data or an available array element was found.
o A fourth version was also tried where the search argument itself was used as the random number. This approach only works if your search arguments are all digits and do not have a wide range.
o A fifth version was tried using the code from the QUSRTOOL; RPG Alternate Lookup (RPGALTLKP). This allows any data (character or decimal) to be used as a random number. The tool provides a "hashing" algorithm that does not use multiply or divide. It allows a maximum of 2000 unique values to be processed with multiple totals for each unique value. RPGALTLKP is a new tool which is in the August 25, 1993 update to QUSRTOOL. (To receive this update, ask your SE to send a note to QUSRTOOL at RCHASA04.)
The fastest results were attained by using the search value as the array index; but this technique does not work for all applications. The next fastest technique was the one that used the RPGALTLKP tool. The results of this technique will vary depending on your data and number of unique values.
The straightforward approach of loading from the front of the array was next best and was better than the back-to-front technique. Once the array is loaded to the point where most of the values exist, the amount of search time is roughly the same whether you are loading front-to-back or back-to-front. Therefore, the back-to-front method is more efficient only for the case where there are mostly unique values.
The solution that used a random number was fairly efficient. The only part of it that was slow was the generation of the random number which required a divide operation. If you have a different technique to generate a random number, this could be very effective as shown by the RPGALTLKP test.
In my test case, the data was mostly random. There was an average of 57 hits per value (10000 / 175 = 57+). In most cases, data is not truly random (it tends to follow the 80-20 rule). If your popular items are in the first few elements of the array, they are found quickly. The inverse of this tends to happen if you load back-to-front. The program spends a lot of time comparing for seldom-used values. It would take an unusual situation for the back-to- front method to have a payoff.
Conclusion: The best results occur if you can use a decimal field in your data as the index (the fourth case). If you don't have such a field, consider the RPGALTLKP tool.
Blueprint for Performance
In these tests, we have looked at some simple RPG functions and the use of arrays. When you are dealing with simple RPG functions, you'll usually realize very little difference in performance by programming things one way or another. Unless you execute the same line of code millions of times per day, you would probably have to look with a microscope to determine the difference. In general, it is good practice to:
o Use odd-length decimal fields.
o Avoid zoned and binary decimal fields in RPG.
When you have a highly used function:
o Try to operate (move and compare) on one-byte fields.
o If it's trivial, put the code in-line and not in a subroutine.
o Be careful with operations that access indexed array elements.
The performance differences for simple RPG functions are all very small in comparison with the time taken for input/output. There is usually very little to be gained by trying to tighten up the RPG code. If you have a program that isn't performing well, you'd probably be wasting your time to try and make any changes for the simple RPG functions we've discussed here. Unless you execute the same code millions of times per day, you won't see any difference.
As always, there are exceptions. A major exception is multiply and divide. They don't perform very well. Stay away from them if you can or use them infrequently.
The other major exception is array processing. When you deal with arrays, you can easily execute millions of instructions by the use of lookup, searches, or moving data to and from arrays. You have to be careful when using arrays if you have a lot of volume.
For most functions, you should try to write for clarity, maintainability and productivity. If you want to improve performance, the places to concentrate your efforts are:
o When you cause the system to perform disk access.
o When you do a CALL or cause a system function (e.g., writing a subfile).
These have a lot more payoff than worrying about squeezing blood out of a few RPG instructions. In future articles, I'll discuss how you can really make a difference by controlling disk access and system functions. Look for the next installment in this series in the November issue of Midrange Computing.
Jim Sloan is president of Jim Sloan, Inc., a consulting company. Now a retired IBMer, Sloan was a software planner on the S/38 when it began as a piece of paper. He also worked on the planning and early releases of the AS/400. In addition to his software planning job, Jim wrote the TAA Tools that exist in QUSRTOOL. He has been a speaker at COMMON and IBM AS/400 Technical Conferences for many years.
Performance test results #1 50,000 Iterations CPU Seconds Base case (no move) 3.1 Move 1 character 3.1+ Move 10 characters 3.4 Move 100 characters 4.3 Move 500 characters 8.8 Move 5000 characters 46.0 #2 50,000 Iterations CPU Seconds Base case 3.1 Testing a 1-byte switch 3.1+ Testing an 8-byte switch 4.6 #3 50,000 Iterations CPU Seconds MOVE/MOVEL 3.7 Move to a data structure 3.4 #4 50,000 Iterations CPU Seconds Move 5 characters 3.2 Z-ADD zero to a 5-digit field 3.5 #5 50,000 Iterations CPU Seconds No subroutine 3.1 One null subroutine 3.8 Two null subroutines 4.5 #6 50,000 Iterations CPU Seconds Add 1 to a 7-digit field 3.6 Add 1 to a 6-digit field 4.3 #7 50,000 Iterations CPU Seconds Add 1 to a 7-digit packed field 3.6 Add 1 to a 7-digit zoned field 5.0 #8 99,999 Iterations CPU Seconds Move using packed decimal index 12.7 Move using binary index 24.8 #9 5,000 Iterations CPU Seconds 100 elements using normal LOKUP 10.2 100 elements using binary search 9.6 200 elements using normal LOKUP 11.8 200 elements using binary search 10.5 500 elements using normal LOKUP 29.3 500 elements using binary search 11.6 #10 50,000 Iterations CPU Seconds 10-element array using an array name 10.8 10-element array using a data structure 7.3 100-element array using an array name 41.7 100-element array using a data structure 7.3 #11 10,000 Iterations CPU Seconds Z-ADD 9.5 Divide 29.2 Multiply 48.1 #12 5,000 Iterations CPU Seconds MOVEA 1.4 Move one element at a time 66.4 #13 99,999 Iterations CPU Seconds MOVE 8.2 Move using a field for the array index 12.7 #14 5,000 Iterations CPU Seconds SCAN with * in position 3 2.1 Search via an index into an array 1.9 SCAN with * in position 25 2.3 Search via an index into an array 9.8 SCAN with * in position 75 2.6 Search via an index into an array 27.8 CHEKR 1.2 Decrement and compare for nonblank 8.8 #15 10,000 Iterations CPU Seconds Load the array front to back 28.2 Load the array back to front 37.5 Load the array by generating a random number 31.9 Load the array using the search value as the index 11.2 Load the array using RPGALTLKP 16.8
SIDEBAR 1
Testing Methodology
I used a separate batch job for each test I ran on a Model D02 AS/400. No other system work was active as the tests were performed. I accessed the CPU time for each job with system job accounting. The QUSRTOOL Print Job Accounting (PRTJOBACG) tool generated a printout of the job accounting results.
Neither IBM nor Midrange Computing have verified or approved these numbers.
The basic test technique uses a series of RPG programs which perform a DO loop a fixed number of times. The base case is the DO loop without any instructions and requires 3.1 seconds for 50,000 iterations on my D02. This represents the overhead to crank up a program and perform the DO loop (it doesn't get any better than this). Each test is a variation of this process.
I used consistent lengths for fields and zero decimal positions when testing the differences between odd- and even-length packed fields and between MOVE and Z-ADD, packed-decimal, binary and zoned-decimal formats.
The subroutine test performed a DO loop without any instructions (the base test), a DO loop with one EXSR instruction and a DO loop with two EXSR instructions. Each subroutine contained no instructions.
Except where specifically noted each test was performed 50,000 times. This repetition makes it possible to see the effect of changing the instructions within the loop. Time was measured in elapsed CPU seconds to the nearest tenth of a second.
SIDEBAR 2
Better Array Processing
Overall, my test cases show that many of the traditional ideas about writing efficient RPG actually have very little real impact. Because of the number of operations required to process an array with many elements, greater caution must be used in coding routines that use arrays. A relatively simple process can quickly generate thousands of operations per transaction. Because any code that involves an array has the potential to impact performance significantly, I've provided some guidelines to help you make your array processing routines more efficient.
Find It Fast
In some cases, the use of SCAN or CHEKR doesn't make sense or would be too inefficient. You can use the following techniques (illustrated in B1) to code a faster search:
In some cases, the use of SCAN or CHEKR doesn't make sense or would be too inefficient. You can use the following techniques (illustrated in Figure B1) to code a faster search:
o Extract multiple characters each time you go through the loop. Moving characters into or out of an array based on a field index is relatively slow. If you move multiple characters at one time to a data structure, you can have in-line code that tests for the search argument.
o Make the array at least one character larger than necessary and place the search argument in the extra position. This avoids the test in the loop for not found in the entire array. If the search is successful, you must check to determine whether it is a real hit or the dummy value added at the end of the array.
o If you are going to compare the extracted value for more than a single character (e.g., both * and /), move the extracted value to a normal field. Use the field for your comparisons (this reduces the number of references to an indexed array). In my tests, I found that this rule was important but that increasing the target field beyond 20 characters did not significantly increase the impact.
The results of the tests I ran to verify the efficiency of these techniques are shown below.
Test results for the tight search loop.
5,000 Iterations CPU Seconds Normal array search found in position 3 1.9 Fast array search technique 1.6 Normal array search found in position 25 9.8 Fast array search technique 5.0 Normal array search found in position 75 27.8 Fast array search technique 13.1 Using a data structure to improve scan performance. 10,000 Iterations CPU Seconds Move 1 character from the array 83.2 Move 5 characters from the array 48.7 Move 10 characters from the array 31.6 Move 20 characters from the array 23.4 Move 30 characters from the array 21.5
The Truth About RPG Performance Coding Techniques
Figure 1 Rearranging a Date with MOVE/MOVEL
... 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6 ...+... 7 ...+... C MOVELDATE MMDD 4 C MOVE DATE YY 2 C MOVELYY YYMMDD 6 C MOVE MMDD YYMMDD ... 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6 ...+... 7 ...+...
The Truth About RPG Performance Coding Techniques
Figure 2 Rearranging a Date with a Data Structure
... 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6 ...+... 7 ...+... IYYMMDD DS I 1 2 YY I 3 6 MMDD C MOVELDATE MMDD C MOVE DATE YY ... 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6 ...+... 7 ...+...
The Truth About RPG Performance Coding Techniques
Figure 3 Moving Data One Byte at a Time
... 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6 ...+... 7 ...+... E ARY1 100 1 E ARY2 100 1 C DO 5000 C DO 100 X 30 C MOVE ARY1,X ARY2,X C END C END ... 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6 ...+... 7 ...+...
The Truth About RPG Performance Coding Techniques
Figure B1 Tight Search Loop
... 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6 ...+... 7 ...+... E* In this example, the array data is 100 bytes and 5 bytes E* are moved from the array (Move length = 5) E* The array should be larger than the data by the -Move length- E AR1 105 1 I* Make a data structure with one character subfields IDS DS I 1 1 CH1 I 2 2 CH2 I 3 3 CH3 I 4 4 CH4 I 5 5 CH5 C* Move the character you are looking for into the first position C* after the actual scan area in the array C Z-ADD101 IX 30 Array + 1 C MOVEA'*' AR1,IX Move an * . . C* Your point where you access the next set of data C FILL TAG Get nxt data . . C* Fill the array from your data C MOVEAXXXX AR1,1 Move to arr C* Initialize the array index to allow add of -Move length- C Z-ADD-4 IX 30 Inlz index C LOOP TAG Loop point C* Add the -Move length- to the array index C ADD 5 IX Bump index C* Move from the array to the data structure (5 bytes) C MOVEAAR1,IX DS Move frm arr C* Select for the character you want (* in this case) C* If you were searching for more than one character, C* you need more WHEQ statements. C SELEC Select C CH1 WHEQ '*' 1st byte C Z-ADDIX FX 30 Found index C* If you want to move CH1 to a common field, do so here C CH2 WHEQ '*' 2nd byte C 1 ADD IX FX Found index C CH3 WHEQ '*' 3rd byte C 2 ADD IX FX Found index C CH4 WHEQ '*' 4th byte C 3 ADD IX FX Found index C CH5 WHEQ '*' 5th byte C 4 ADD IX FX Found index C OTHER Other char C* The character does not exist in the bytes moved C GOTO LOOP Loop back C END Select C* If the -Found index- has found the character you placed in C* the array, the array is complete. Loop back. C FX CABEQ101 FILL If spcl char C* C* The * was found at the position of the FX index C* C* Your processing C* C* Bump past the position where the character was found. C* Reset the array index to allow for add of move length C* Loop back C ADD 1 FOUND 30 Bump index C FX SUB 4 IX Decrement C GOTO LOOP Loop back ... 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6 ...+... 7 ...+...
LATEST COMMENTS
MC Press Online