Chapter 11

Writing Efficient and Portable Macros

Writing Efficient and Portable Macros

The macro facility is a powerful tool for making your SAS code development more efficient. But macros are only as efficient as you make them. There are several techniques and considerations for writing efficient macros. You can extend the power of the macro facility by creating macros that can be used on more than one host environment. In order to do this, there are additional considerations for writing portable macros.

Keeping Efficiency in Perspective

Efficiency is an elusive thing, hard to quantify and harder still to define. What works with one application might not work with another, and what is efficient on one host environment might be inefficient on a different system. However, there are some generalities that you should keep in mind.

Usually, efficiency issues are discussed in terms of CPU cycles, elapsed time, I/O hits, memory usage, disk storage, and so on. This section does not give benchmarks in these terms because of all the variables involved. A program that runs only once needs different tuning than a program that runs hundreds of times. An application running on a mainframe has different hardware parameters than an application developed on a desktop PC. You must keep efficiency in perspective with your environment.

There are different approaches to efficiency, depending on what resources you want to conserve. Are CPU cycles more critical than I/O hits? Do you have lots of memory but no disk space? Taking stock of your situation before deciding how to tune your programs is a good idea.

The area of efficiency most affected by the SAS macro facility is human efficiency — how much time is required to both develop and maintain a program. Autocall macros are particularly important in this area because the autocall facility provides code reusability. Once you develop a macro that performs a task, you can save it and use it for the following:

• in the application that you developed it for

• in future applications without any further work

A library of reusable, immediately callable macros is a boon to any application development team.

The stored compiled macro facility (described in “Storing and Reusing Macros” on page 117) might reduce execution time by enabling previously compiled macros to be accessed during different SAS jobs and sessions. But it is a tool that is efficient only for production applications, not during application development. So the efficiency techniques that you choose depend not only on your hardware and personnel situation, but also on the stage that you have reached in your application development process.

Also, remember that incorporating macro code into a SAS application does not automatically make the application more efficient. When designing a SAS application, concentrate on making the basic SAS code that macros generate more efficient. There are many sources for information about efficient SAS code, including SAS Programming Tips: A Guide to Efficient SAS Processing.

Writing Efficient Macros

Use Macros Wisely

An application that uses a macro to generate only constant text can be inefficient. In general, for these situations consider using a %INCLUDE statement. The %INCLUDE statement does not have to compile the code first (it is executed immediately). Therefore, it might be more efficient than using a macro (especially if the code is executed only once). For more information, see “%INCLUDE Statement” in SAS Statements: Reference.

If you use the same code repeatedly, it might be more efficient to use a macro. A macro is compiled only once during a SAS job, no matter how many times it is called.

Use Name Style Macros

Macros come in three invocation types: name style, command style, and statement style. Of the three, name style is the most efficient because name style macros always begin with a %, which immediately tells the word scanner to pass the token to the macro processor. With the other two types, the word scanner does not know immediately whether the token should be sent to the macro processor. Therefore, time is wasted while the word scanner determines whether the token should be sent.

Avoid Nested Macro Definitions

Nesting macro definitions inside other macros is usually unnecessary and inefficient. When you call a macro that contains a nested macro definition, the macro processor generates the nested macro definition as text and places it on the input stack. The word scanner then scans the definition and the macro processor compiles it. Do not nest the definition of a macro that does not change. You will cause the macro processor to compile the same macro each time that section of the outer macro is executed.

As a rule, you should define macros separately. If you want to nest a macro’s scope, simply nest the macro call, not the macro definition.

For example, the macro STATS1 contains a nested macro definition for the macro TITLE:

/* Nesting a Macro Definition--INEFFICIENT */
%macro stats1(product,year);
   %macro title;
      title "Statistics for &product in &year";
      %if &year>1929 and &year<1935 %then
         %do;
            title2 "Some Data Might Be Missing";
         %end;
   %mend title;

   proc means data=products;
      where product="&product" and year=&year;
      %title
   run;
%mend stats1;

%stats1(steel,2002)
%stats1(beef,2000)
%stats1(fiberglass,2001)

Each time the macro STATS1 is called, the macro processor generates the definition of the macro TITLE as text, recognizes a macro definition, and compiles the macro TITLE. In this case, STATS1 was called three times, which means the TITLE macro was compiled three times. With only a few statements, this task takes only micro-seconds; but in large macros with hundreds of statements, the wasted time could be significant.

The values of PRODUCT and YEAR are available to TITLE because its call is within the definition of STATS1. Therefore, it is unnecessary to nest the definition of TITLE to make values available to TITLE’s scope. Nesting definitions are also unnecessary because no values in the definition of the TITLE statement are dependent on values that change during the execution of STATS1. (Even if the definition of the TITLE statement depended on such values, you could use a global macro variable to effect the changes, rather than nest the definition.)

The following program shows the macros defined separately:

/* Separating Macro Definitions--EFFICIENT */
%macro stats2(product,year);
   proc means data=products;
      where product="&product" and year=&year;
      %title
   run;
%mend stats2;

%macro title;
   title "Statistics for &product in &year";
   %if &year>1929 and &year<1935 %then
      %do;
         title2 "Some Data Might Be Missing";
      %end;
%mend title;

%stats2(cotton,1999)
%stats2(brick,2002)
%stats2(lamb,2001)

Here, because the definition of the macro TITLE is outside the definition of the macro STATS2, TITLE is compiled only once, even though STATS2 is called three times. Again, the values of PRODUCT and YEAR are available to TITLE because its call is within the definition of STATS2.

Note: Another reason to define macros separately is because it makes them easier to maintain, each in a separate file.

Assign Function Results to Macro Variables

It is more efficient to resolve a variable reference than it is to evaluate a function. Therefore, assign the results of frequently used functions to macro variables.

For example, the following macro is inefficient because the length of the macro variable THETEXT must be evaluated at every iteration of the %DO %WHILE statement:

/* INEFFICIENT MACRO */
%macro test(thetext);
   %let x=1;
      %do %while (&x > %length(&thetext));
         .
         .
         .
      %end;
%mend test;

%test(Four Score and Seven Years Ago)

A more efficient method would be to evaluate the length of THETEXT once and assign that value to another macro variable. Then, use that variable in the %DO %WHILE statement, as in the following program:

/* MORE EFFICIENT MACRO */
%macro test2(thetext);   %let x=1;
   %let length=%length(&thetext);
   %do %while (&x > &length);
      .
      .
      .
   %end;
%mend test2;

%test(Four Score and Seven Years Ago)

As another example, suppose you want to use the %SUBSTR function to pull the year out of the value of SYSDATE. Instead of using %SUBSTR repeatedly in your code, assign the value of the %SUBSTR(&SYSDATE, 6) to a macro variable, and use that variable whenever you need the year.

Turn Off System Options When Appropriate

While the debugging system options, such as MPRINT and MLOGIC, are very helpful at times, it is inefficient to run production (debugged) macros with this type of system option set to on. For production macros, run your job with the following settings: NOMLOGIC, NOMPRINT, NOMRECALL, and NOSYMBOLGEN.

Even if your job has no errors, if you run it with these options turned on you incur the overhead that the options require. By turning them off, your program runs more efficiently.

Note: Another approach to deciding when to use MPRINT versus NOMPRINT is to match this option’s setting with the setting of the SOURCE option. That is, if your program uses the SOURCE option, it should also use MPRINT. If your program uses NOSOURCE, then run it with NOMPRINT as well.

Note: If you do not use autocall macros, use the NOMAUTOSOURCE system option. If you do not use stored compiled macros, use the NOMSTORED system option.

Use the Stored Compiled Macro Facility

The stored compiled macro facility reduces execution time by enabling macros compiled in a previous SAS job or session to be accessed during subsequent SAS jobs and sessions. Therefore, these macros do not need to be recompiled. Use the stored compiled macro facility only for production (debugged) macros. It is not efficient to use this facility when developing a macro application.

CAUTION:

Save the source code. You cannot re-create the source code for a macro from the compiled code. You should keep a copy of the source code in a safe place, in case the compiled code becomes corrupted for some reason. Having a copy of the source is also necessary if you intend to modify the macro at a later time.

See “Storing and Reusing Macros” on page 117 for more information about the stored compiled macro facility.

Note: The compiled code generated by the stored compiled macro facility is not portable. If you need to transfer macros to another host environment, you must move the source code and recompile and store it on the new host.

Centrally Store Autocall Macros

When using the autocall facility, it is most efficient in terms of I/O to store all your autocall macros in one library and append that library name to the beginning of the SASAUTOS system option specification. Of course, you could store the autocall macros in as many libraries as you want. But each time you call a macro, each library is searched sequentially until the macro is found. Opening and searching only one library reduces the time SAS spends looking for macros.

However, it might make more sense, if you have hundreds of autocall macros, to have them separated into logical divisions according to the following:

• purpose

• levels of production

• who supports them

• and so on

As usual, you must balance reduced I/O against ease-of-use and ease-of-maintenance.

All autocall libraries in the concatenated list are opened and left open during a SAS job or session. The first time you call an autocall macro, any library that did not open the first time is tested again each time an autocall macro is used. Therefore, it is extremely inefficient to have invalid pathnames in your SASAUTOS system option specification. You see no warnings about this wasted effort on the part of SAS, unless no libraries at all open.

There are two efficiency tips involving the autocall facility:

• Do not store nonmacro code in autocall library files.

• Do not store more than one macro in each autocall library file.

Although these two practices are used by SAS and do work, they contribute significantly to code-maintenance effort and therefore are less efficient.

Other Useful Efficiency Tips

Here are some other efficiency techniques that you can try:

• Reset macro variables to null if the variables are no longer going to be referenced.

• Use triple ampersands to force an additional scan of macro variables with long values, when appropriate. See “Storing Only One Copy of a Long Macro Variable Value” on page 151 for more information.

• Adjust the values of “MSYMTABMAX= System Option” on page 365 and “MVARSIZE= System Option” on page 366 to fit your situation. In general, increase the values if disk space is in short supply; decrease the values if memory is in short supply. MSYMTABMAX affects the space available for storing macro variable symbol tables; MVARSIZE affects the space available for storing values of individual macro variables.

Storing Only One Copy of a Long Macro Variable Value

Because macro variables can have very long values, the way you store macro variables can affect the efficiency of a program. Indirect references using three ampersands enable you to store fewer copies of a long value.

For example, suppose your program contains long macro variable values that represent sections of SAS programs:

%let pgm=%str(data flights;
   set schedule;
   totmiles=sum(of miles1-miles20);
   proc print;
   var flightid totmiles;);

You want the SAS program to end with a RUN statement:

%macro check(val);
         /* first version */  &val
   %if %index(&val,%str(run;))=0 %then %str(run;);
%mend check;

First, the macro CHECK generates the program statements contained in the parameter VAL (a macro variable that is defined in the %MACRO statement and passed in from the macro call). Then, the %INDEX function searches the value of VAL for the characters run;. (The %STR function causes the semicolon to be treated as text.) If the characters are not present, the %INDEX function returns 0. The %IF condition becomes true, and the macro processor generates a RUN statement.

To use the macro CHECK with the variable PGM, assign the parameter VAL the value of PGM in the macro call:

%check(&pgm)

As a result, SAS sees the following statements:

data flights;
  set schedule;
  totmiles=sum(of miles1-miles20);

proc print;
  var flightid totmiles;
run;

The macro CHECK works properly. However, the macro processor assigns the value of PGM as the value of VAL during the execution of CHECK. Thus, the macro processor must store two long values (the value of PGM and the value of VAL) while CHECK is executing.

To make the program more efficient, write the macro so that it uses the value of PGM rather than copying the value into VAL:

%macro check2(val);  /* more efficient macro */  &&&val
   %if %index(&&&val,%str(run;))=0 %then %str(run;);
%mend check2;

%check2(pgm)

The macro CHECK2 produces the same result as the macro CHECK:

data flights;

   set schedule;
   totmiles=sum(of miles1-miles20);

proc print;
   var flightid totmiles;
run;

However, in the macro CHECK2, the value assigned to VAL is simply the name PGM, not the value of PGM. The macro processor resolves &&&VAL into &PGM and then into the SAS statements contained in the macro variable PGM. Thus, the long value is stored only once.

Writing Portable Macros

Using Portable SAS Language Functions with %SYSFUNC

If your code runs in two different environments, you have essentially doubled the worth of your development effort. But portable applications require some planning ahead. For more details about any host-specific feature of SAS, see the SAS documentation for your host environment.

You can use the %SYSFUNC macro function to access SAS language functions to perform most host-specific operations, such as opening or deleting a file. For more information, see “%SYSFUNC and %QSYSFUNC Functions” on page 278.

Using %SYSFUNC to access portable SAS language functions can save you a lot of macro coding (and is therefore not only portable but also more efficient). The following table lists some common host-specific tasks and the functions that perform those tasks.

Table 11.1 Portable SAS Language Functions and Their Uses

Task

SAS Language Function or Functions

Assign and verify existence of fileref and physical file

FILENAME,

FILEREF,

PATHNAME

Open a file

FOPEN, MOPEN

Verify existence of a file

FEXIST, FILEEXIST

Get information about a file

FINFO, FOPTNAME, FOPTNUM

Write data to a file

FAPPEND, FWRITE

Read from a file

FPOINT, FREAD,

FREWIND, FRLEN

Close a file

FCLOSE

Delete a file

FDELETE

Open a directory

DOPEN

Return information about a directory

DINFO, DNUM, DOPTNAME, DOPTNUM, DREAD

Close a directory

DCLOSE

Read a host-specific option

GETOPTION

Interact with the File Data Buffer (FDB)

FCOL, FGET, FNOTE, FPOS, FPUT, FSEP

Assign and verify librefs

LIBNAME, LIBREF, PATHNAME

Get information about executed host environment commands

SYSRC

Note: Of course, you can also use other functions, such as ABS, MAX, and TRANWRD, with %SYSFUNC. A few SAS language functions are not available with %SYSFUNC. See “%SYSFUNC and %QSYSFUNC Functions” on page 278 for more details.

Example Using %SYSFUNC

The following program deletes the file identified by the fileref MyFile:

%macro testfile(filrf);
   %let
rc=%sysfunc(filename(filrf,physical-filename));
   %if &rc = 0 and %sysfunc(fexist(&filrf)) %then
      %let rc=%sysfunc(fdelete(&filrf));
   %let rc=%sysfunc(filename(filrf));
%mend testfile;

%testfile(myfile)

Using Automatic Variables with Host-Specific Values

Macro Variables by Task

The automatic macro variables are available under all host environments, but the values are determined by each host. The following table lists the macro variables by task. The “Type” column tells you if the variable can be changed (Read and Write) or can be inspected (Read Only).

Table 11.2 Automatic Macro Variables with Host-Specific Results

Task

Automatic Macro Variable

Type

List the name of the current graphics device on DEVICE=.

SYSDEVIC

Read and write

List of the mode of execution (values are FORE or BACK). Some host environments allow only one mode, FORE.

SYSENV

Read-only

List the name of the currently executing batch job, user ID, or process. For example, on UNIX, SYSJOBID is the PID.

SYSJOBID

Read-only

List the last return code generated by your host environment, based on commands executed using the X statement in open code. The X command in the SAS windowing environment, or the %SYSEXEC (or %TSO or %CMS) macro statements.

The default value is 0.

SYSRC

Read and write

List the abbreviation of the host environment that you are using.

SYSSCP

Read-only

List a more detailed abbreviation of the host environment that you are using.

SYSSCPL

Read-only

Retrieve a character string that was passed to SAS by the SYSPARM= system option.

SYSPARM

Read and write

Time zone name based on TIMEZONE option.

SYSTIMEZONE

Read-only

Time zone ID based on TIMEZONE option.

SYSTIMEZONEIDENT

Read-only

Current time zone offset based on TIMEZONE option.

SYSTIMEZONEOFFSET

Read-only

Examples Using SYSSCP and SYSSCPL

The macro DELFILE uses the value of SYSSCP to determine the platform that is running SAS and deletes a TMP file. FILEREF is a macro parameter that contains a filename. Because the filename is host-specific, making it a macro parameter enables the macro to use whatever filename syntax is necessary for the host environment.

%macro delfile(fileref);
    /* Unix */
    %if &sysscp=HP 800 or &sysscp=HP 300 %then %do;
          X "rm &fileref..TMP";
    %end;

        /* DOS-LIKE platforms */
    %else %if &sysscp=OS2 or &sysscp=WIN %then %do;

             X "DEL &fileref..TMP";
   %end;
    /* CMS */
   %else %if &sysscp=CMS %then %do;
             X "ERASE &fileref  TMP A";
   %end;
%mend delfile;

Here is a call to the macro DELFILE in a PC environment that deletes a file named C: SASSasuserDoc1.Tmp:

%delfile(c:sasSasuserDoc1)

In this program, note the use of the portable %SYSEXEC statement to carry out the host-specific operating system commands.

Now, suppose you know your macro application is going to run on some version of Microsoft Windows. The SYSSCPL automatic macro variable provides information about the name of the host environment, similar to the SYSSCP automatic macro variable. However, SYSSCPL provides more information and enables you to further modify your macro code.

Example Using SYSPARM

Suppose the SYSPARM= system option is set to the name of a city. That means the SYSPARM automatic variable is set to the name of that city. You can use that value to subset a data set and generate code specific to that value. Simply by making a small change to the command that invokes SAS (or to the configuration SAS file), your SAS job will perform different tasks.

/* Create a data set, based on the value of the */
/* SYSPARM automatic variable. */
/* An example data set name could be MYLIB.BOSTON. */
data mylib.&sysparm;
   set mylib.alltowns;
      /* Use the SYSPARM SAS language function to */
      /* compare the value (city name) */
      /* of SYSPARM to a data set variable. */
   if town=sysparm();
run;

When this program executes, you end up with a data set that contains data for only the town that you are interested in. You can change what data set is generated before you start your SAS job.

Now suppose you want to further use the value of SYSPARM to control what procedures your job uses. The following macro does just that:

%macro select;
   %if %upcase(&sysparm) eq BOSTON %then
      %do;
         proc report ... more SAS code;
            title "Report on &sysparm";
         run;
      %end;

   %if %upcase(&sysparm) eq CHICAGO %then
      %do;
         proc chart ... more SAS code;

           title "Growth Values for &sysparm";
         run;
      %end;
   .
   .  /* more macro code */
   .
%mend select;

SYSPARM Details

The value of the SYSPARM automatic macro variable is the same as the value of the SYSPARM= system option, which is equivalent to the return value of the SAS language function SYSPARM. The default value is null. Because you can use the SYSPARM= system option at SAS invocation, you can set the value of the SYSPARM automatic macro variable before your SAS session begins.

SYSRC Details

The value of the SYSRC automatic macro variable contains the last return code generated by your host environment. The code returned is based on the following commands that you execute:

• the X statement in open code

• the X command a windowing environment

• the %SYSEXEC macro statement (as well as the nonportable %TSO and %CMS macro statements)

Use the SYSRC automatic macro variable to test the success or failure of a host environment command.

Note: Since it does not generate an error message in the SAS log, the SYSRC automatic macro variable is not useful under all host environments. For example, under some host environments, the value of this variable is always 99, regardless of the success or failure of the host environment command. Check the SAS companion for your host environment to determine whether the SYSRC automatic macro variable is useful for your host environment.

Macro Language Elements with System Dependencies

Several macro language elements are host-specific, including the following:

any language element that relies on the sort sequence

Examples of such expressions include %DO, %DO %UNTIL, %DO %WHILE, %IF-%THEN, and %EVAL.

For example, consider the following program:

%macro testsort(var);
    %if &var < a %then %put *** &var is less than a ***;
    %else %put *** &var is greater than a ***;
%mend testsort;
%testsort(1)
      /* Invoke the macro with the number 1 as the parameter. */

On EBCDIC systems, such as z/OS, and VSE, this program causes the following to be written to the SAS log:

*** 1 is greater than a ***

But on ASCII systems (such as UNIX or Windows), the following is written to the SAS log:

*** 1 is less than a ***

MSYMTABMAX=

The MSYMTABMAX system option specifies the maximum amount of memory available to the macro variable symbol tables. If this value is exceeded, the symbol tables are stored in a Work file on disk.

MVARSIZE=

The MVARSIZE system option specifies the maximum number of bytes for any macro variable stored in memory. If this value is exceeded, the macro variable is stored in a Work file on disk.

%SCAN and %QSCAN

The default delimiters that the %SCAN and %QSCAN functions use to search for words in a string are different on ASCII and EBCDIC systems. The default delimiters are

ASCII systems

blank . < ( + & ! $ * ) ; ^ − / , % |

EBCDIC systems

blank . < ( + | & ! $ * ) ; ¬ − / , % ¦ ¢

%SYSEXEC, %TSO, and %CMS

The %SYSEXEC, %TSO, and %CMS macro statements enable you to issue a host environment command.

%SYSGET

On some host environments, the %SYSGET function returns the value of host environment variables and symbols.

SYSPARM=

The SYSPARM= system option can supply a value for the SYSPARM automatic macro variable at SAS invocation. It is useful in customizing a production job. For example, to create a title based on a city as part of noninteractive execution, the production program might contain the SYSPARM= system option. It can be in the SAS configuration file or the command that invokes SAS. See “SYSPARM Details” on page 156 for an example using the SYSPARM= system option in conjunction with the SYSPARM automatic macro variable.

SASMSTORE=

The SASMSTORE= system option specifies the location of stored compiled macros.

SASAUTOS=

The SASAUTOS= system option specifies the location of autocall macros.

Host-Specific Macro Variables

Some host environments create unique macro variables. These macro variables are not automatic macro variables. The following tables list some commonly used host-specific macro variables. Additional host-specific macro variables might be available in future releases. See your SAS companion for more details.

Table 11.3 Host-Specific Macro Variables for z/OS

Variable Name

Description

SYS99ERR

SVC99 error reason code

SYS99INF

SVC99 info reason code

SYS99MSG

YSC99 text message corresponding to the SVC error or info reason code

SYS99R15

SVC99 return code

SYSJCTID

Value of the JCTUSER field in the JCT control block

SYSJMRID

Value of the JMRUSEID field in the JCT control block

SYSUID

TSO user ID associated with the SAS session

Naming Macros and External Files for Use with the Autocall Facility

When naming macros that will be stored in an autocall library, there are restrictions depending on your host environment. Here is a list of some of the restrictions:

• Every host environment has file naming conventions. If the host environment uses file extensions, use .sas as the extension of your macro files.

• Although SAS names can contain underscores, some host environments do not use them in the names of external files. Some host environments that do not use underscores do use the pound sign (#) and might automatically replace the # with _ when the macro is used.

• Some host environments have reserved words, such as CON and NULL. Do not use reserved words when naming autocall macros or external files.

• Some hosts have host-specific autocall macros. Do not define a macro with the same name as these autocall macros.

• Macro catalogs are not portable. Remember to always save your macro source code in a safe place.

• On UNIX systems the filename that contains the autocall macro must be all lowercase letters.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.9.169