|
|
|
|
About Common and Named Common By Tom Packert Senior Programmer Analyst Avanti-Case Hoyt After writing a few programs in Pick, it does not take long for a programmer to come across situations when it would be convenient to be able to have variables shared between programs and subroutines, without passing long lists of parameters. The way to implement these global variables is by using variables passed through a common block of variables called COMMON. In this article I will address methods of implementing common including the underutilized ‘named’ common. Common variables are extremely powerful features of any Pick application. As with anything powerful, it is often misunderstood and misused. Debugging problems in the COMMON areas of programs can be extremely time consuming so it is best to set out a methodology for implementing common first. Hopefully, this will prevent time from being wasted with common areas that are out of synch. Style 1 The formal term for describing the visibility of a variable across programs is called the ‘scope’. Common is a method to implement variables with ‘global scope’ and all other variables are local to the program or subroutine where they are declared. The advantage of global variables stored in common variable blocks is that they can be initialized once and remain visible throughout all the subroutines that need to use the variable. In Basic, setting up common variables is a simple matter of declaring them as common by placing the word common in front of the name of the variable. There are three styles you can use to declare common variables (example 1). I prefer the third style for reasons I will explain below. Style 1 lists explicitly the names of the common variables. This method is the most simple, but when large numbers of variables are listed it can quickly become difficult to maintain.. Style 2 is where you declare each element on its own common line. Style three is where you declare dimensioned arrays in common and then equate the variables to the common arrays. The third style is the easiest to maintain and give the programmer a lot of flexibility. Declaring COMMON dimensioned arrays allows the programmer to very quickly to see all the common variable space laid out. It is very clear from the declaration of the array how many variables are there. In order to make room for unforeseen modifications later, the programmer can allocate more space than needed by changing the declaration from COMMON SOME.ARR( (3) to COMMON SOME.ARR( (10) and leave room for seven additional elements later. There is little harm in declaring a few extra variables as long as a few does not reach into the thousands. An advantage of leaving room for a few extra variables this way is that the all the programs that use the same common will not need to be recompiled unless they need to access the additional variable. Using style 1 or style 2 would require that all programs that are called by or call the program in question will need to be recompiled. If they are not recompiled then bugs will appear at runtime because the same variable space will be updated incorrectly. *MAIN PROGRAM
In order to implement common in the easiest, most maintainable form, it is useful use the $INCLUDE directive to reference a single item containing the COMMON declarations. It is useful to create two variations of the COMMON declarations. One of the declarations will be used by $INCLUDES in main programs and the other is for subroutines. The only difference between the two is the declarations for a subroutine will not initialize any common variables. The included declarations for the main programs may initialize the common arrays to null with MAT SOME.ARR="". You would not want this initialization to appear inside of a subroutine because it would clear any values in the common arrays. COM VAR.ARR(10) Retrofitting programs with a single COMMON declaration may cause difficulty because of different variable names in different programs. It is not a problem to equate two or more variations of a variable to a single COMMON array element (example 3). As long as you do not try to use the variable names for different values they can be used interchangeably. The result is that you can accommodate various versions of programs that used different naming conventions for the same elements without having to modify all the instances of the different variable names in the source code. When common variables are declared, the compiler and run time engine work together to instruct the system to start allocating variable space from the beginning of the space reserved for variables. As more arrays or variables are declared, the amount of space pre-allocated for variables grows. Every variable declared in a program is either in common or it is local. If the variable is in common then the space for the variable is allocated when the main program starts. All subsequent references to the variable reference the global location of this variable. Consider example 5. The space for the SYS.VARS and USER.VARS is allocated when the MAIN program is started. These two arrays combine to reserve the first ten variables for the programs. Only one of these variables is even used, but the space is allocated anyway. T1=SYSTEM(12)
The performance effect of the allocation of variables can be significant. Allocating variables consumes CPU cycles and increases the footprint of the program. The more variables that are declared then the more CPU and memory is required to start a program. If the bulk of the variables are declared in the main program instead of in subroutines, then the effect of calling a subroutine on CPU and memory will be less. Consider the programs in Example 4. The first loop of Subroutine calls will complete in 100th the time of the second loop of subroutine calls. The only difference is the DIM statement which allocates 3000 variables. If the ARR array is moved to common the performance of the two loops is identical because the allocation and de-allocation of 3000 variables is performed once, not 50,000 times. The conclusion here is that you should minimize the number of variables declared in frequently called subroutines, otherwise you introduce a runtime overhead which occurs every time a subroutine is called. Mismatched common and what happens. When common arrays are not included from a global include declaration, then there is opportunity for the declarations of the common to become inconsistent in different programs. When commons become mismatched the effects are felt at runtime. There are few tools in the system to help a programmer trace problems with common blocks, they are best just to be prevented by using INCLUDES carefully. Debugging common space problems can be one of the most frustrating and time consuming activities a programmer can perform. *MAIN PROGRAM Example 5
Look at the program in example 5. Notice that the subroutine can declare the common space as a single 10 element array instead of two declarations of 5 elements. This is perfectly legitimate. There might be a reason to do this, but it will more likely cause a problem if you do this too frequently because of possible typographical errors. One reason you might want to do this is to be able save a copy of ranges of common elements in a single statement with MAT SAVE.VARS = MAT WOW.VARS. Consider what would happen to the variable space if the common declaration of WOW.VARS were mistyped as a 20 instead of a 10. If this were to happen then any assignments to elements 11-20 in the WOW.VARS would in effect be overwriting the first 10 local variables in the MAIN PROGRAM Calling subroutines with less declared common – It is not harmful to call subroutines which have the same, less or no common declarations. It is only harmful to call a subroutine with more common than the main line routine. The problem is caused by the subroutine mapping the additional common variables at the end of the existing common. The local variables in the main line routine were also allocated at the end of the common area declared in the main routine. The result will be that the common area in the subroutine use the same space as the local variables in the main routine. This is a very time consuming bug to locate because it occurs at runtime, based on the execution. It could actually go undetected for a long time until an execution path causes one of the overlapped variables to initialize a variable and return a corrupted value to the calling routine. If the overwritten variable happens to be a file variable, then the result might be a fatal file not opened error although the file was opened successfully by the main routine. *MAIN PROGRAM
Accidentally passing common variables as parameters. There is nothing in the compiler or runtime engine that will prevent you from passing COMMON variables as parameters. Lately, I have not been able to reproduce the problems I ran into years ago with applications that did this. Previously there was a problem with passing common variables to subroutines as parameters. The runtime engine could become confused and release the memory back to available space even though it is still in common. My advice is DON’T PASS COMMON VARIABLES AS PARAMETERS. It is ambiguous and can potentially confuse the run time. Most systems today seem to be forgiving on this programming oversight, but it is not a good idea to deliberately tempt fate by possibly tricking the run time engine.
Using $INCLUDES to control COMMONs As mentioned before it makes a lot of sense to declare your common blocks in a single item for main line programs and a single item for all subroutines (see example 5). Adding additional space to common arrays is as easy as editing two items, finding all the programs that include the items and compiling the programs. It makes sense to declare several arrays in common and to group the arrays by function. I make sure that arrays that will contain open file variables are kept separate from other variables. Variables that contain open file information can be assigned to other variables, but they can not be written to a file Program Includes
About Named Common – If you understand the concept of common, then named common will be also be easy to understand. Non-named common, which is what I have been discussing up to now, is initialized when a program starts and lasts until the program stops. When the program stops, the space for all the variables is released and all the values are thrown away. When the program starts up again, it will reallocate the common variables and re-initialize every one of them. If your system is based on many programs being EXECUTEd or CHAINed then there is a lot of allocation and reinitializing occurring in the system whenever a user enters a new program. Named common is relatively new and was introduced to solve this problem of wasting CPU cycles when programs start. Named common variables retain their values during the entire logon process and are only thrown away when the user logs off or the common is cleared deliberately by the program. Named common allows the program to open certain files once at the startup of the user’s session and not have to allocate or initialize the variables again until the user logs on again. Theoretically you could have the user open all the files in the system once per day and store them in a NAMED common array. I am not convinced this is a good idea, but it could be done. If your system supports named common it can be very beneficial to take advantage of it if you are not already. The only problem with named common is that the implementation of named common is different for the various implementations Pick type environments. For example the variables in named common in Unidata are initialized to zero, while variables in named common in GA’s mvBase are initialized to unassigned. The function of the CLEARCOMMON statement also varies between implementations. This becomes a problem for developers trying to develop functionality across platforms. Note: It is also a problem for technical writers to provide meaningful examples so please consult your system documentation for further help with the details of named common on your system. The examples below work on a Mentor Pro or mvBase system. The concepts are the same, if not the syntax, for Unidata/Universe and AP/D3. Named common is easily declared with the following syntax. COM /SOMENAME/ SOME.ARR(100). There are some restrictions on named common that are implementation dependent. The length of the name of the common is limited to 8 characters in mvBase and Unidata and the number of named commons that can be declared in a program is limited to five in mvBase. ED DICT INVOICE.FILE NON.TAX.AMT Exanple 8
Named common and performance - Named common is especially important to understand if you will be using BASIC routines called from dictionary items. Calling BASIC programs from dictionary items is a powerful option to produce a report quickly. The dictionary item and program in example 8 implements a simple basic summation. I know that this could be done as a "F" correlative but finding programmers who know how to do this as an "F" correlative is harder these days. The example does not need named common. But the modified version in example 9 does implement the named common to maintain performance. Calling BASIC from dictionary items can quickly become a performance robbing option unless used carefully and usually in combination with named common. If you find yourself leaning toward implementing a report with dictionary items that need to call a basic routine, then named common will be useful to implement in order to avoid a great deal of overhead. For example: If a file needs to be opened in the basic routine that is called from a dictionary item, it would benefit the performance of the report for the file to be opened once to named common. Otherwise an open statement is executed for every item listed on the report. When complicated reports run with multiple dictionaries calling multiple basic routines, and opening multiple files, the report will quickly degrade in performance unless named common is used to eliminate wasteful, repetitive file opens. RESET.DICT.COM COM /DICTCOM/ DICT.ARR(10) EQU MY.FILE TO DICT.ARR(1) EQU INITIALIZED TO DICT.ARR(10) INITIALIZED=0
Exanple 9
Named common can quickly get out of hand just like non-named common, if we first do not plan on how to implement named common for reports and other system functions. Remember, once created, named common exists until the user logs off. If a user runs a report that uses named common, then initializing the named common is important to consider. The initialization flag is also stored in named common so it is critical for the process that starts a report correctly initialize the named common. The use of the CLEAR COMMON statement will be required. I suggest using a single named common area for all access reports which invoke dictionary items that call BASIC programs. All reports should be initialized first by a process that clears the named common and initializes it for the current report process. This will ensure that the correct values are set for the current report. If a common initialization routine does not execute for each invocation of a report, then the danger is that named common values may be in use from a report run hours before and the results will be extremely confusing to the users. The other problem with uncontrolled use of named common is that the named common will remain in memory until the use logs off. If each report in the system were to declare unique named common at the end of the day, after running many reports, the user’s workspace would contain many left over named common arrays that are just taking up valuable space. When multiple users have multiple left over named common regions, the effect on system resources can become noticeable. Common and Named common are excellent tools to increase the efficiency of your applications and systems while offering programming efficiencies that lower maintenance costs. If common and named common are implemented by following programming standards then all of the pitfalls of their use are avoided and only the benefits remain. If you are not using common and still don’t understand the issue, don't worry!. From past experience trying to teach this, it tends to be an ‘ah ha’ experience. If you play with them long enough, you will one day say ‘ah ha!’ now I understand it. If you have any questions regarding the use of common, please feel free to email me and perhaps I can fill in any missing information. |