Make Mine MAPPER #1 -------------------- by Rob Haeuser MAPPER Run Design: The Adventure Begins ..... or, If One Function Won't Do, How About Two? ----------------------------------------------------------------- Working in close proximity to a large computer room can give one an appreciation for the amount of effort necessary to accomplish the simplest of tasks. Fetching tapes seems simple enough, until you can't find one. Printout shouldn't be a problem, until the printer eats the paper. After a few years of working for the machine, I developed the attitude that it is in everyone's best interest to minimize the work the machine does. We can define this work as occurring on two levels: internal logic processing in main memory, or "going outside" to access external I/O devices, such as disk and tape drives, printers, or other peripherals. In MAPPER we can measure I/O requests and Logical Lines Processed with the reserved words IO$ and LLP$ respectively, using LLP$ as a rough indicator of CPU usage. Run functions can be categorized as being logic intensive, such as IF/CHG, or I/O intensive, such as SRH/SORT. As a MAPPER Run Designer, my ultimate goal is to achieve the desired "result" with the least amount of waiting for the answer, by minimizing I/O and LLP usage. Run Design is so appealing to me because of it's tremendous power and flexibility. There is almost always more than one way to write the code. This can present problems if Run Designers do not consider the ramifications of a particular coding technique. There may be a tendency to code I/O intensive runs, for example, because it is generally easier to code a search and then duplicate the search code fifty times, rather than write a Read/IF loop. On the other hand, a COBOL programmer turned Run Designer may tend to write nothing but Read/IF loops, even when a search would be more appropriate. Serious consideration must be given to the working environment. Is the system heavily loaded on either I/O or CPU requests? After doing some benchmark timings on our 1100/70's and 1100/80's, I found that even at 100% CPU utilization with disk usage at 30% (and not much queuing going on), a Read/IF loop will generally run faster than a run using the I/O intensive logic of multiple searches. I'm not recommending Read/IF logic over multiple searches in all cases. Search, and the other "macro" functions, such as Match, Totalize, and Calculate, are very powerful and usually suffice. I'll expand on this subject in a future article. Convincing myself that if a choice is necessary (given the environment), it was more desirable to minimize I/O rather than logic lines processed, I began coding logic intensive runs. As the logic grows in complexity, the importance of each function cannot be overstated. A single function can literally make or break a run. One of the first applications I wrote, a problem tracking system, demonstrates the impact a single function can have. The "Help Desk" is located right up the hall, so I have seen it in regular use over the years. Because of its' close proximity, it is easy to get personal feedback when implementing new coding techniques, which I try to do with every new MAPPER release. When a caller reports a problem, a screen full of data is entered, including the caller's name, phone number, location, hardware configuration, problem codes, etc, etc, etc... In addition, a block of eight lines of narrative may be entered. If more than eight lines are required, a second screen allows up to 15 additional lines, for a total of 23 lines of narrative that can be entered at initial intake. The numbers "8" and "15" have no obvious significance; they are what I had left over after designing the rest of the screen. Even the importance of the number 23 escaped me at first (the sum of 8 and 15, obviously), leading me to "hard code" all the IF and Write-Line (WRL) statements, as evidenced by the code in Figure 1. ------------------------------------------------------------------- FIGURE 1 -------- @IF V71(1-18) = '',(51) ;. (minimum of 1 line is required) @IF V72(1-18) NE '' INC V70 ;IF V73(1-18) NE '' INC V70 ;\ IF V74(1-18) NE '' INC V70 ;IF V75(1-18) NE '' INC V70 ;\ IF V76(1-18) NE '' INC V70 ;IF V77(1-18) NE '' INC V70 ;\ IF V78(1-18) NE '' INC V70 ;IF V79(1-18) NE '' INC V70 ;\ IF V80(1-18) NE '' INC V70 ;IF V81(1-18) NE '' INC V70 ;\ IF V82(1-18) NE '' INC V70 ;IF V83(1-18) NE '' INC V70 ;\ IF V84(1-18) NE '' INC V70 ;IF V85(1-18) NE '' INC V70 ;\ IF V86(1-18) NE '' INC V70 ;IF V87(1-18) NE '' INC V70 ;. @IF V88(1-18) NE '' INC V70 ;IF V89(1-18) NE '' INC V70 ;\ IF V90(1-18) NE '' INC V70 ;IF V91(1-18) NE '' INC V70 ;\ IF V92(1-18) NE '' INC V70 ;IF V93(1-18) NE '' INC V70 ;. @LOK,M,T,R LLN,M,T,R V94I4 LN+,M,T,R,V94,V70 INC V94\ WRL,M,T,R,V94,Y *,V71 . (one line will always be written) @IF V72(1-18) NE '' INC V94 WRL,M,T,R,V94,Y *,V72 ;\ (more?) IF V73(1-18) NE '' INC V94 WRL,M,T,R,V94,Y *,V73 ;\ (more?) IF V74(1-18) NE '' INC V94 WRL,M,T,R,V94,Y *,V74 ;\ (more?) IF V75(1-18) NE '' INC V94 WRL,M,T,R,V94,Y *,V75 ;\ (more?) etc. for V76 on up through V93... ------------------------------------------------------------------- In the figure, V70 is the number of lines to write, V71 through V93 are the narrative lines, and V94 is the line number to be written. It occurred to me that using "double V's", where the numerical value of one variable is used to identify a second variable, could shorten the code considerably (see Figure 2). ------------------------------------------------------------------- FIGURE 2 -------- @LDV V95I2=72 . @IF VV95(1-18) NE '' INC V70 ;INC V95 IF V95 < 94,(LIN-0) ;. @LDV V95=71 LOK,M,T,R LLN,M,T,R V94I4 LN+,M,T,R,V70,V94 INC V94 . @IF VV95(1-18) NE '' WRL,M,T,R,V94,Y *,VV95 INC V94 ;\ INC V95 IF V95 < 94,(LIN-1) ;ULK . ------------------------------------------------------------------- In Figure 2, V95 is used to loop on variables V72 through V93, first to count the number of non-blank narrative variables to be written, and then again to write them. All other variables are the same as Figure 1. This method took fewer lines of physical code, saving some space in the Run Control Report (RCR), but it simply substitutes a logical loop for hard-coded logic without reducing the actual amount of code executed or the amount of I/O required to write the data, since each line was still being written individually. I was aware of WRL's ability to write multiple lines simultaneously, but had used it very little due to the fact that every line being written had to conform to a single set of field columns. However, that was quite appropriate for narrative. Having observed the Help Desk staff wait a good bit longer after transmitting extensive, rambling narratives, I decided to utilize multiple-line writes if at all possible. My problem was one of flexibility. How could a multiple line write be coded to cover the near infinite (yes, a slight exaggeration) combinations of possible line numbers to be written? It wasn't simply a question of having 23 different WRL statements, each hard-coded to write from one on up to 23 lines, and branching to the appropriate statement. If the first 18 characters of a narrative variable are blank, it is assumed that the entire line is blank, and is not written. In effect, all blank lines are suppressed, even if they are imbedded within the narrative text. This is done mainly to save disk space. Obviously, the WRL statements couldn't be hard-coded, but it seemed as though string variables could be used somehow. I decided to load a string variable with the syntax required for a multiple- line write (see Figure 3). ------------------------------------------------------------------- FIGURE 3 -------- @LDV V101S131='*,V71' . (still have at least one line to write) @IF V72 NE '' LDV V101(6-6)='/*,V72' ;. (more?) ------------------------------------------------------------------- After coding a couple lines, it became obvious that double V's would be necessary again. Two string variables were required to hold all of the syntax necessary to write the maximum of 23 lines. Finally, I ended up with a WRL that looked like this: @WRL,M,T,R,V94,Y 2-68 V101V102 . Unfortunately, it didn't work. The combined number of characters in V101 and V102 exceeded the 68 columns being written, giving the error message "Trying to update beyond character position limit." Even if V101 and V102 were equal to 68, the imbedded syntax gets written as though it were the text, rather than being executed as code. Apparently WRL couldn't do it alone, so I began to look for other functions that might help. The often overlooked Execute (XQT) function has the ability to execute code from variables, which is exactly what I had been trying to do (see Figure 4). ------------------------------------------------------------------- FIGURE 4 -------- @LDV V95I2=72,V96I3=6,V101S131='*,V71',V102H6,V103I3=101 . @IF VV95(1-18) NE '' LDV VV103(V96-6)=',/V'V95 INC V70 INC,6 V96 ;\ IF V96 > 131 LDV V96=1,V103=102 ;INC V95 IF V95 < 94,(LIN-1) ;. @LOK,M,T,R LLN,M,T,R V94 LN+,M,T,R,V94,V70 INC V94 . @XQT '@WRL,M,T,R,V94,Y 2-68 'V101V102 ULK . ------------------------------------------------------------------- In Figure 4, V101 and V102 contain the syntax required for the multiple line write; V96 is the column within either V101 or V102 to load the syntax (with the length of each syntactical piece hard- coded as 6 characters); and V103 is used to indicate either V101 or V102 for the load. All other variables are used as described for prior figures. The first line of code loads the necessary variables. V95 is initialized to 72 (the second narrative variable) because there will always be at least one line to write from V71. Notice that V101 is initially loaded with the syntax for the first write. The second line loads the required syntax into either V101 or V102 if a narrative variable is not blank in the first 18 columns. Then it increments the line counter (V70) by one and the column indicator by six. Please note that V95 is outside the ticks surrounding the string '/,V'. This is required for the LDV statement to pick up the value contained in V95. The third line is a continuation of line 2. It checks the column position of the syntax holding variables (V101 and V102), adjusting the column indicator (V96) to "1" and the syntax variable indicator (V103) to use V102. It then increments V95 to see if all narrative variables have been checked for spaces. If not, it loops back up to line 2. The fourth line locks the Rid and adds the required number of blank lines at the appropriate spot. The fifth line actually writes the data - finally! Note that only part of the WRL is in ticks. This is required so that XQT will interpret V101 and V102 and execute the syntax contained therein. This code is able to write any combination of up to 23 lines of narrative with a single WRL statement, suppressing all blank lines in the process. Writing narrative with multiple write-line syntax is easy because all the variables being written are the same length and go into the same column positions. This technique can also be used to write lines that do not share common field positions, such as in a data paragraph. Since you can't state the columns separately for each line being written, simply load string variables with the data required and write entire lines. Even if only a few lines are being written, it is cheaper and faster to load some variables and do a multiple-line write rather than to write each line individually. My suggestion to Unisys for a future enhancement: Allow WRL to write more than 23 lines at one time. Sure, I could code a loop to do it in blocks of 23, but... Also, allow WRL to write from another rid, as well as from variables. Under certain circumstances, this might be more desirable than an ADD/REP combination, since the intermediate step of creating a result would be avoided. As you can see, there is much to consider before coding even the simplest of runs. Remember, if you code to minimize I/O and LLP, your runs will run faster, the machine will work less, and everyone will be happier! Booga, booga! ------------------------------------------------------------------- Rob Haeuser has 20 years of Data Processing experience. He was MAPPER Coordinator and Run Designer for the Texas Department of Human Services for ten years, and is now an independent contractor working in the Austin area. He also authored and is marketing a set of MAPPER run utilities called GURU. Covering MAPPER topics ranging from technical to tacky, his never-ending quest is for truth, justice, and the MAPPER way. Write to Rob at the following address: GURU Enterprises Attn Rob Haeuser 3212 Great Valley Drive Cedar Park, Tx. 78613 Fax him at 512-335-3862 or call 512-331-0498 and leave a message.