How do I? - Part 17
by Eric Slaats
Hi. I had some free days this month and decided (amongst firing up my studio) to look deeper at some of the more difficult OS/2 stuff. I covered EAs and am looking at hooks right now. Intriguing matter. Although I really don't understand why the EA stuff has to be so complex. I used EAs in Smalled, but that was coded over a year ago and I've found that I've lost most of my knowledge about it. Hooks are another matter. Hooks allow you to interfere with the way the system operates. It enables you to intercept messages from programs, capture keyboard input before it is sent to a program etc. I've decided to do something more with hooks. (They are used by SmallHtm to work with MLE editors.) I'll keep you all posted on the projects that may evolve from this.
Last month when we rounded up the calculator sample application I promised to take a look at smaller code. One of the things I hate are bloated programs. Large program blobs are becoming more and more like a normal thing. Simply take a look at a run of the mill word processor: It's eating megabytes of your hard drive and can be tediously slow on a 486 to a Pentium 133. But even the shareware and freeware programs are becoming larger and larger too. Where is this large code coming from? Well there are some causes:
- Object Technology. If your using Object Oriented Programming then there is a slight overhead. The OOP extensions to a programming language, like exception handlers, also need memory space. Don't get me wrong, I love the OOP principle, but it often causes fat programs. If you use an OOP add on, for example ObjectWindows, then a very simple program that just displays a window and writes something in it will become very large.
- In the slipstream of OOP, visual programming has emerged. Right now most of the main culprits of bloat are written in visual programming tools like Delphi, Visual Age etc. I really like the way you can program in these tools, but I hate the code bloating. It gives me the feeling that I simply don't know a very large part of the code that is placed in the .EXE I create.
- Code that isn't designed very well. It's amazing how much code can be reduced by designing your program right. A lot of programs can be reduced in code load if reviewed thoroughly. More about that later on.
Well most of this is obvious. (I left out obvious causes as leaving debugging code or debuggers info in your exe.) What can we do to make code as small and effective? Well here are some pointers I always use. In my experience these points usually pay off. Since this column isn't intended to be very technical I try to keep things as simple as possible.
- Design, Design, Design. This is the most important point. A badly designed program won't perform well and will usually end up bloated. Design your functions as clever as possible and use decomposition to do this. In most of the programs of students I review, there is a lot of redundant code. Create functions so that they may be reused. If a function (or an event handler) has more that 25 lines of code (of course this is subjective and not a rule of thumb) it is way too large. Chances are you're redoing something you've already done.
- Object Oriented Programming is arranged around the concept of reusability. One of the problems is that you've got to know or find an object to reuse it. So before creating a new object, check if an object exists that does the work for you. This also goes for the use of non-OO functions. Check out the standard C-functions and the load of functions that the OS/2 PM has on-board. The next points address more technical ways to write optimal code. Compilers and linkers have a number of switches on board that make them create smaller code. Besides that, code load may vary from compiler to compiler. So if you've got the time, try more compilers. At least one of them is free (as far as I know). The EMX compiler. (check out Hobbes for this package).
- Use a base address. This is a very simple though very effective way to create smaller code. (There is no downside to this. It will make your code faster and smaller, so DO IT!) In a multitasking environment like OS/2, you don't know were a program will be loaded. In most cases there will be more programs active and all can be loaded on different places. Now OS/2 guarantees a load address at 1000H. If you tell your linker this, all internal relocations will be removed. This will result in a smaller, faster loading and faster running executable. (Also to this for the DLLs if you're creating any.)
- Use your compiler and linker switches. Most compilers these days make it fairly easy to select the switches to use. For example The Borland C++ 2.0 compiler let's you choose between fastest code, smallest code, or minimal options. Besides that, all the compiler settings can of course be hand tuned. (Look at the -O (optimize) parameters for the compiler in most cases.) We'll take a look at one set of switches to underline the point that size can be a trade-off with speed. Most compilers let you choose if your code has to be optimized for 386/486 or Pentium. Since the Pentium is optimized for code that is built in a RISC like manner (simple one word instructions), the code for a Pentium will, in general, be bulkier. The 386/486 code will use a lot of instructions that do more work per-instruction. So this code will be smaller, but not as fast. In a lot of cases we are not really interested in speed of a program. For example the Calculator. I doubt it very much if anyone would notice the speed change between these two compiler settings.
- Compress your code. Everybody uses utilities like Zip, ARJ and RAR and we're fairly used to them. Now why not simply compress an exe file and run it straight from the compressed form? The advantage in this is that the load time to fetch a file from disk is much longer than the time the computer needs to decompress the file. This means faster startup as well as smaller code. (Note that the faster startup goes for all the smaller code methods.) OS/2 has the capability to run compressed executables. Well, that statement is not entirely true - it can run executables with compressed resources (the resources have to reside in the executable). So the more resources like bitmaps, dialogs, fonts, etc. the executable has, the more effect the compression has. Compression is fairly simple to use. You can bind the resource file to the executable by using the -x switch in the resource compiler, like this:
RC -x prog.rc prog.exe
Warp (since version 3.0) has even better compression. A "x2" has been added since then. The drawback for this is that the executables can only run on an OS/2 Warp system. OS/2 2.x can't execute exepack 2 programs. But compression is really something you should try. The results are rather dramatic (as we will see in a minute).
There are more techniques like using Wrapper functions and realigning code pages. We'll look at those some other time because they need a lot more explaining. Right now we'll take a look at the effects of some of the settings. As an example program I used the calculator we build last months.
First we compile the Calculator code with minimal settings and slowly build it up to the smallest possible version that is compressed, based etc. This testing is done with Borland C++ 2.0. So if you're using another compiler this might vary.
1) Normal 38416 bytes 2) Based 34832 bytes 3) Based and x1 compression 33296 bytes 4) Based and x2 compression 27664 bytes
Now one last tip, and this is a great one. There is a tool out there written by Mike Ruddy. (check also EDM/2 3.6) It's called LXOPT. This tool contains the x1 and x2 compression as well as something this tool is unique in: code page aligning and elimination of dead code. This is really a great tool, besides that it's free. If LXOPT is used with the calculator is makes the executable even smaller:
5) LXOPT 25340 bytes.
As you can see the total reduction is rather dramatic. The code shrank one third and with hardly any effort on our side. For larger executables this effect is even more dramatic. So at least use Based and "x" compression. Although I would advise everybody to check on LXOPT and, of course, use good programming techniques.
That's it for this month.