Saturday, February 17, 2007
Unlocking Hidden Optimizations
The C compiler for System i provides a packed decimal data type, but not a zoned decimal data type. When working with a data base or exchanging data with RPG or COBOL, it is sometimes necessary to convert between the zoned representation of decimal data and other data types such as packed decimal, floating point, and integer.
The common technique for converting between zoned and another data type in a C or C++ program is to use the cpynv function described in "ILE C/C++ for AS/400 MI Library Reference". For example, the following code snippet shows how to convert from packed(30,9) to zoned(7,0):
#include <mih/cpynv.h>
: : :
char znd[7];
_Decimal pkd(30, 9);
cpynv( NUM_DESCR(_T_ZONED, 7, 0), znd,
NUM_DESCR(_T_PACKED, 30, 9), (char*)&pkd );
There is an important usage note in the description of cpynv in the "ILE C/C++ for AS/400 MI Library Reference". The note reads as follows:
- zoned to packed, float, signed integer, or unsigned integer
- packed to zoned
- signed integer to zoned
- unsigned integer to zoned
The built-in function _CPYNV also provides access to the equivalent MI instruction. When using the _CPYNV instruction, you must specify literal constants for the receiver and source descriptor values. _CPYNV is considerably faster than _LBCPYNV (and cpynv) for the following conversions:
The code snippet above uses the NUM_DESCR macro to build the first and third argument values, which describe the attributes (data type and length) of the destination and source locations. The usage note tells us that if both attribute arguments are literal values (rather than variables whose values are not determined until runtime), the compiler will attempt to optimize the conversion by decoding the attribute values and generating code to perform the conversion more directly, which may perform significantly faster. Unfortunately, however, when the NUM_DESCR macro is used to generate the attribute value, the resulting argument value is a complicated expression that the compiler cannot evaluate at compile time. As a result, the macro inhibits the optimization to perform the conversion more directly.
To enable the optimization, NUM_DESCR must be replaced by a constant attribute value that is pre-calculated (by you). The attribute is a 4 byte value with this encoding:
TTFFLL00
where:
TT- is a 1-byte code for the data type
FF- is a 1-byte code for the fractional digits (the 3rd argument to NUM_DESCR for packed and zoned data types)
LL- is a 1-byte code for the total number of digits (the 2nd argument to NUM_DESCR for packed and zoned data types)
00- the last byte must be zero
The data type values are defined in <mih/micptcom.h> (use the command "strseu qsysinc/mih micptcom" to browse). Some example attribute values are shown below:
NUM_DESCR(_T_PACKED, 7, 0) is equivalent to0x03000700
NUM_DESCR(_T_ZONED, 7, 0) is equivalent to0x02000700
NUM_DESCR(_T_PACKED, 30, 9) is equivalent to0x03091E00
Using this information, the original call to cpynv should be replaced with this call to _CPYNV, which will generate the most efficient code to convert from packed to zoned:
_CPYNV( 0x02000700, znd,
0x03091E00, (char*)&pkd );