Using C++ constructs in performance-critical code

Note: The discussion in this information applies to high-level language constructs that might seriously degrade the performance of C++ programs. All other coding discussions in this information apply to both C and C++ programs.

Be aware that in C++, more than in C, certain coding constructs can lead to n-to-1, m-to-1 or even z-to-1 code expansion. You can create well-performing code with these constructs, but you must use them carefully and appropriately, especially when you are writing critical-path or high-frequency code.

When writing performance-critical C++ programs, ensure that you understand why problems might occur and what you can do about them if you use any of the following high-level language constructs:

Virtual
The virtual construct is an important part of object-oriented coding and can be very useful in removing the if and switch logic from an application. Programmers often use virtual and neglect to remove the switch logic. Note the following:
  • The use of a virtual construct (like the use of a pointer and unlike the use of if statements) prevents the compiler from knowing how that construct is defined, which would provide the compiler with an optimization opportunity. In other words, when you use a virtual construct instead of if or switch statements, you limit optimization opportunities.
  • In a non-XPLINK module, because of function overhead, virtual functions are costlier to execute than straight-line code with if or switch statements.
Exception handling
When exception handling is available (that is, when you are using the EXH compiler option), opportunities for both normal optimizations and for inlining are limited. This is because the compiler must generate extra code to keep track of execution events and to ensure that all required objects are caught by the correct routines.
When you use the C++ try and catch blocks, the compiler creates obstacles to optimization. The compiler cannot pull common code out of a try block because it might trigger an exception that would need to be caught. Similarly, code cannot be pulled out of a catch block because:
  • The code in a catch block is triggered far down the call chain, after the exception has occurred
  • After a catch has occurred, the compiler must ensure that all requested tasks have been executed
You might improve compiler performance by:
  • Removing dependencies on C++ exception handling from your code
  • Compiling with the NOEXH compiler option
Dynamic casts/Runtime type identification (RTTI)
A dynamic cast (also known as RTTI) is a coding construct that delays, until run time, the determination of which code is to be executed. This limits the potential for optimization. In addition, the process of actually doing the dynamic cast involves multiple function calls and large amounts of code.
Note: We strongly recommend that RTTI/dynamic casts not be used in performance-critical code. You can often avoid the use of RTTI through careful application design.
iostream
As discussed in Using the Standard C++ Library I/O Stream Classes and in Using C and C++ standard streams and redirection, iostream is often built upon the standard C I/O library (fprintf, fopen, fclose, fread, fwrite). For I/O performance-critical portions of your application, it is often faster to use the C I/O functions explicitly instead of iostream.
Note: You must be careful if you are mixing the C++ stream classes with the C library. For more information, see Using the Standard C++ Library I/O Stream Classes.
Standard Template Library and other class libraries
These libraries are very convenient and are often well coded, but you must remember that each use of a class can involve one or more function calls. If you keep this in mind when coding, you can design applications that use these libraries efficiently. For example, you would not initialize all local string variables to the NULL string and then redefine the string on first reference.
new/delete
New C++ applications on z/OS® often depend heavily on new and delete operators because they are commonly one of the first things taught in a C++ introductory course, and many courses never explicitly teach that classes can also be automatic (default for local) or global variables.
You should be aware that the new and delete operators are costlier to use than variables. Also, before using new, you should carefully consider:
  • The scope/usage pattern of the variable
  • Whether an automatic (local) or global variable is more appropriate
Note: You can ensure that all memory and storage requests are properly optimized by following the instructions given in Improving performance with compiler options and Optimizing memory and storage.