Thursday, December 12, 2013

Running C++ Applications in CICS

Running object oriented languages on the mainframe presents a lot of "opportunities." It's a new world for most of us old timers but, with the right care and feeding, they can fit nicely side by side with the more traditional applications. This article contains some of my recent experiences running C++ on CICS.
Have plenty of virtual storage available: The Language Environment (LE) C++ compiler creates some load modules of unprecedented size, especially if you use compiler options meant to optimize CPU. This can be a problem for systems like CICS that like to keep programs in storage as much as possible. We ended up increasing our EDSA up to our private limit of 2G. Unfortunately, I'm not sure how soon help will be on the way. While zOS created storage above the 4G bar, as of this writing you cannot execute code up there. The C++ compiler says it is able to use data above 4G but it is cumbersome and your programmers have to understand what's going on.

Tune your LE options: C++, like other object oriented languages, is very dynamic and likes to create and destroy objects. This means the storage management routines get a thorough workout. You should also remember that in CICS the LE memory manager runs on top of CICS'. But, although there's not a lot you can do about an application's memory requests, you can tune your LE options to reduce the number of C++ storage requests that turn in CICS getmains.
Note that this is not as easy as setting HEAP to a large value and going out for some coffee. Remember that the LE allocates storage every time it creates an enclave. For instance, if you set the initial HEAP size to 5M, every transaction will get that much storage at a minimum. After that each EXEC CICS LINK creates a new enclave and sucks up 5M more of storage. This limits the region's throughput and can cause additional real storage strain to the entire LPAR. It's best to find a happy medium. You should also remember to account for CICS' crumple zone overhead (16 bytes) and LE's storage accounting (about 54 bytes).
Call, don't link: As stated above, EXEC CICS LINK commands and some LE calls create new enclaves for the target program. C++ enclave creation can be particularly expensive depending on how the class constructors work for the called program. We've had a lot of luck using dllload() (which returns the address of a DLL) and dllqueryfn() (to get the entry point to a function within the DLL) to avoid this overhead even if it is a little clunky.
You also need to aware that C++ on the mainframe also supports implicit DLL loads. It works the same as it does on Windows, where bringing in one program creates a potential cascading load of other programs. You just better pray they all fit.
Tune the object oriented application: Object Oriented Programming (OOP) is dynamic and makes great reuse of classes. But, as in all things in life, there is a price to pay. As mentioned before, the dynamic nature of C++ object construction and destruction can be expensive. Programmers who capitalize on class reuse tend to cause other problems. For instance, someone could instantiate a class to use a piddling date manipulation method and be blissfully unaware that the constructor for that class instantiates dozens of additional objects and writes to a database.
My advice is to break with the pure OOP design every now and then where performance becomes a concern. Applications should also be designed so objects can be passed from program to program instead of recreating them for each DLL. Finally, application programmers may look into caching of data or objects to avoid accessing databases.
Debugging output in production: C and C++ make it very easy to write debugging messages to STDOUT with printf() and the cout class. Most C and C++ programmers enthusiastically embrace this tool as a good way to keep track of what the program is doing.
This is usually not a problem until you move the program to production. All of the sudden you may see messages like, "I am in badRoutineA()" and "The value of lifePointer is ." Even worse, sometimes you may even see confidential customer data rolling majestically into SYSOUT for everyone to see.
STDOUT and STDERR messages end up going to CEEOUT and CEEMSG respectively. In CICS these turn into expensive extrapartition data queue writes. Given the expense and potential legal implications you should aggressively persuade your programmers to turn off these messages before a module reaches production. If persuasion doesn't work, you could always write a CICS global user exit (GLUE) to kill the messages before they see daylight.
Spread out the workload: Even well tuned C++ applications will use several times the CPU consumed by traditional COBOL programs. Couple that with C++'s appetite for storage and you quickly run into throughput problems trying to fit so many large transactions through a single region. The classic answer is to spread the application horizontally into a CICSPlex. If you get involved early enough make sure to advise the developers how to build a CICSPlex enabled application.
Beware the null-terminal string: C++ carries on C's dubious tradition of null-terminated strings. If you're new to the language, this means the end of a string is denoted by a null. If the null is missing a simple string copy (strcpy())can cause an overlay of biblical proportions. When one of these overlays occurs you may find your CICS region is absolutely ruined and your only option is to bounce. These types of overlays are difficult to debug because most of the environment is gone by the time you get the dump. However, patience and a good debugger usually pay off. <>good news is that C++ comes with string classes and most programmers will come up with their own from time to time. These classes afford some protection from overlays and are careful about string boundaries. However, even with these classes the application programmers should know what they're doing.
I think it's okay to admit that object oriented languages probably don't perform as well on the mainframe as they do on other platforms. IBM admitted as much when they came out with the Java zAAP processors. However, mainframe object oriented use will increase because of its other advantages such as productivity and portability. I should also note that the college kids nowadays are more likely to know C++ and Java than ANSI FORTRAN. Therefore, we need to make our peace and hope IBM has some tricks up its corporate sleeve.

0 comments:

Post a Comment