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 Chapter 9, “Storing and Reusing
Macros,” on page 115) 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,
144 Chapter 11 Writing Efficient and Portable Macros
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 Global 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.
Writing Efficient Macros 145
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)
146 Chapter 11 Writing Efficient and Portable Macros
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.
For more information about the stored compiled macro facility, see Chapter 9, “Storing
and Reusing Macros,” on page 115.
Writing Efficient Macros 147
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. For more information, see “Storing Only One Copy of a
Long Macro Variable Value” on page 149.
Adjust the values of “MSYMTABMAX= System Option” on page 380 and
“MVARSIZE= System Option” on page 381 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.
148 Chapter 11 Writing Efficient and Portable Macros
..................Content has been hidden....................

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