Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

.NET

Optimizing With Microsoft C 6.0


The OPT benchmark program is shown in Listing One. Each compiler was directed to produce an assembly language listing of its output, based on a compile using the optimized compiler switches just shown. Listing Two shows the output from Watcom C 7.0; Listing Three shows the output from Microsoft C 5.1; Listing Four shows the output from Microsoft C 6.0 without enregistered parameters; Listing Five shows the output from Microsoft C 6.0 with enregistered parameters. These listings should give you a feel for the quality of code generation supported by the test compilers.

Listing One

***********************
*** Microsoft C 6.0 ***
***********************

#include "stdio.h"

/* prototypes */

void doit(int i);

void (* func_ptr)(int i) = doit;

void doit(int i)
    {
    --> push bp
    --> mov  bp,sp
    --> push di
    --> push si
    --> mov  di,WORD PTR [bp+4]

    int loop;

    for (; i > 0; --i)

    --> or  di,di
    --> jle $EX225

        {
        for (loop = 0; loop < 26; ++loop)

        --> $F227:
        --> sub si,si
        --> mov WORD PTR [bp+4],di

            {
            printf("loop character = %c\n", 0x41 + loop);

            --> $F230:
            --> lea  ax,WORD PTR [si+65]
            --> push ax
            --> mov  ax,OFFSET DGROUP:$SG233
            --> push ax
            --> call _printf
            --> add  sp,4
            --> inc  si
            --> cmp  si,26
            --> jl   $F230
            }

        printf("i / 16 = %d\n\n",i / 16);

        --> mov  ax,di
        --> cwd
        --> xor  ax,dx
        --> sub  ax,dx
        --> mov  cx,4
        --> sar  ax,cl
        --> xor  ax,dx
        --> sub  ax,dx
        --> push ax
        --> mov  ax,OFFSET DGROUP:$SG234
        --> push ax
        --> call _printf
        --> add  sp,4
        --> dec  di
        --> jne  $F227

        }

    --> $EX225:
    --> pop si
    --> pop di
    --> mov sp,bp
    --> pop bp
    --> ret
    --> nop
    }

int main(void)
    {
    func_ptr(100);

    --> mov  ax,100
    --> push ax
    --> call WORD PTR _func_ptr
    --> add  sp,2

    return 0;

    --> sub ax,ax
    --> ret
    }

Listing Two

*****************************************
*** Microsoft C 6.0 (using _fastcall) ***
*****************************************

#include "stdio.h"

/* prototypes */

void doit(int i);

void (* func_ptr)(int i) = doit;

void doit(int i)
    {

    --> push bp
    --> mov  bp,sp
    --> sub  sp,2
    --> push ax
    --> push si

    int loop;

    for (; i > 0; --i)

    --> or  ax,ax
    --> jle $EX225

        {
        for (loop = 0; loop < 26; ++loop)

        --> $F227:
        --> sub si,si

            {
            printf("loop character = %c\n", 0x41 + loop);

            --> $F230:
            --> lea  ax,WORD PTR [si+65]
            --> push ax
            --> mov  ax,OFFSET DGROUP:$SG233
            --> push ax
            --> call _printf
            --> add  sp,4
            --> inc  si
            --> cmp  si,26
            --> jl   $F230

            }

        printf("i / 16 = %d\n\n",i / 16);

        --> mov  ax,WORD PTR [bp-4]
        --> cwd
        --> xor  ax,dx
        --> sub  ax,dx
        --> mov  cx,4
        --> sar  ax,cl
        --> xor  ax,dx
        --> sub  ax,dx
        --> push ax
        --> mov  ax,OFFSET DGROUP:$SG234
        --> push ax
        --> call _printf
        --> add  sp,4
        --> dec  WORD PTR [bp-4]
        --> jne  $F227

        }

        --> $EX225:
        --> pop si
        --> mov sp,bp
        --> pop bp
        --> ret
    }

int main(void)
    {
    func_ptr(100);

    --> mov  ax,100
    --> call WORD PTR _func_ptr

    return 0;

    --> sub ax,ax
    --> ret

    }


Listing Three

************************
*** Microsoft C 5.10 ***
************************

#include "stdio.h"

/* prototypes */

void doit(int i);

void (* func_ptr)(int i) = doit;

void doit(int i)
    {
    --> push    bp
    --> mov     bp,sp
    --> sub     sp,2
    --> push    di
    --> push    si

    int loop;

    for (; i > 0; --i)

    --> cmp WORD PTR [bp+4],0
    --> jle $FB202
    --> mov di,WORD PTR [bp+4]

        {
        for (loop = 0; loop < 26; ++loop)

        --> $L20002:
        --> sub si,si

            {
            printf("loop character = %c\n", 0x41 + loop);

            --> $L20000:
            --> lea  ax,WORD PTR [si+65]
            --> push ax
            --> mov  ax,OFFSET DGROUP:$SG206
            --> push ax
            --> call _printf
            --> add  sp,4
        }

        --> inc si
        --> cmp si,26
        --> jl  $L20000
        --> mov WORD PTR [bp-2],si  ;loop

        printf("i / 16 = %d\n\n",i / 16);

        --> mov  ax,di
        --> cwd
        --> xor  ax,dx
        --> sub  ax,dx
        --> mov  cx,4
        --> sar  ax,cl
        --> xor  ax,dx
        --> sub  ax,dx
        --> push ax
        --> mov  ax,OFFSET DGROUP:$SG207
        --> push ax
        --> call _printf
        --> add  sp,4

        }

        --> dec di
        --> jne $L20002
        --> mov WORD PTR [bp+4],di

        --> $FB202:
        --> pop si
        --> pop di
        --> mov sp,bp
        --> pop bp
        --> ret
        --> nop

    }

int main(void)
    {
    func_ptr(100);

    --> mov  ax,100
    --> push ax
    --> call WORD PTR _func_ptr
    --> add  sp,2

    return 0;

    --> sub  ax,ax
    --> ret
    }

Listing Four

********************
*** Watcom C 7.0 ***
********************

#include "stdio.h"

/* prototypes */

void doit(int i);

void (* func_ptr)(int i) = doit;

void doit(int i)
    {
    int loop;

    --> push    bx
    --> push    cx
    --> push    dx
    --> mov     cx,ax
    --> jmp     short L3

    for (; i > 0; --i)
        {
        for (loop = 0; loop < 26; ++loop)
            {
            --> L1:
            --> mov     bx,0041H

            printf("loop character = %c\n", 0x41 + loop);

            --> L2:
            --> push    bx
            --> mov     ax,offset DGROUP:L4
            --> push    ax
            --> call    near ptr printf_
            --> add     sp,0004H
            --> inc     bx
            --> cmp     bx,005bH
            --> jne     L2

            }

        printf("i / 16 = %d\n\n",i / 16);

        --> mov     bx,0010H
        --> mov     ax,cx
        --> cwd
        --> idiv    bx
        --> push    ax
        --> mov     ax,offset DGROUP:L5
        --> push    ax
        --> call    near ptr printf_
        --> add     sp,0004H
        --> dec     cx

        }

    --> L3:
    --> test    cx,cx
    --> jg      L1

    --> pop     dx
    --> pop     cx
    --> pop     bx
    --> ret

    }

int main(void)
    {

    func_ptr(100);

    --> mov     ax,0064H
    --> call    word ptr _func_ptr

    return 0;

    --> xor     ax,ax
    --> ret

    }
Listing Five
/* Skeleton Program demonstrating the use of based pointers */

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>

#define MAX_TAG 2000
unsigned long get_size(void);
_segment segvar;           /* name a segment for use with based pointers */

/* set up structures and tags within segment segvar */

typedef mytag {
char filename[14];
unsigned long size;
mytag _based(segvar) *next;
} _based(segvar) *PTAG, TAG;

main() {

   PTAG head, curptr;

/* Allocate a based heap of MAX_TAG structs. Put segment address in segvar. */

   if((segvar = _bheapseg(sizeof(TAG) * MAX_TAG))) == NULLSEG){
      printf("error allocating based heap \n");
      exit(-1);
   }

/* Allocate memory within segvar for first structure in linked list */

   if((head = _bmalloc(segvar, sizeof(TAG)) == _NULLOFF) {
   printf("error allocating TAG \n");
   exit(-1);
   }
   head->size = get_size();
   _fstrcpy((char far *) head->filename, get_name()); /* get a
   filename and copy it to segvar */

   if((head->next = _bmalloc(segvar, sizeof(TAG)) == _NULLOFF) {
   printf("error allocating TAG \n");
   exit(-1);
   }
.
.
.
}
unsigned long get_size(void) {
return 1;
}

char *get_name(void) {
return("foo");
}

At the Workbench

Usually, I don't care much for integrated programming environments -- the ones I've worked with have limited capabilities locked into a simplistic editor. Working from the DOS prompt has always been faster and more powerful. That is, until now.

The Programmer's Workbench is actually an enhanced version of the Microsoft Editor. The Microsoft Editor was easily as powerful as other professional program editors. It allowed separately compiled "add-ons" to be linked with it at run time. The Programmer's Workbench takes the idea of add-ons a step further by using them to add support for compilers, help systems, and utilities to its menus and environments.

The Programmer's Workbench editor is powerful and fast, incorporating all of the features programmers expect from a professional editor -- recordable and compilable macros, powerful block operations, multiple file editing, and mouse support. It also allows you to give editor commands using menus or function keys. I haven't felt restricted by the Programmer's Workbench editor, and that's something I cannot say for other integrated editors.

You can do all of your program development work from within the Programmer's Workbench. The environment integrates the compiler, make utility, linker, help system, and CodeView relatively seamlessly. It's easy to "live" within the Programmer's WorkBench, leaving it only when you're ready to finish up for the day.

Programmer's WorkBench includes menus that allow you to set options for compiles. Options can be set for C, Microsoft Macro Assembler (MASM), and the linker. Every option available from the command line can be selected via the menus. Two sets of program construction options can be set, for debug and production compiles. Combined with a list of files that are part of the same program, the options are used to construct a make file for a project. When you build a project, the project's make file is then passed as a parameter to nmake. Compilation results appear in a window, and errors can be tracked from the compiler's output directly into your source code. It's a slick, powerful, and uninhibited environment for constructing software. Microsoft has told me that the interface to the Programmer's WorkBench will be fully and publicly documented so that third parties can integrate their products into WorkBench. With luck, your favorite editor or make utility will become a part of the environment.

Programmers WorkBench has one drawback that may limit its acceptance by many programmers: It does not run efficiently on anything less than a 386-or fast 286-based PC. The environment is simply too slow to be useful when run on a 10-MHz or slower 286-based computer, according to my correspondents.

Other problems in the Programmer's WorkBench have surfaced. It requires nearly 3 Mbytes of disk space, room many developers need for other software and data. If you don't install WorkBench and its help files, you no longer have access to the primary source of detailed documentation for MSC6. For these reasons many developers may opt to leave Programmer's WorkBench off their system.

Conclusion

Microsoft has made a valiant effort at producing the definitive C compiler for MS-DOS and OS/2. For developers who have high-end PCs with available disk space, Microsoft C 6.0 is a solid professional product that offers some advantages over its competitors. Improvements in code optimization, compiler speed, and ANSI compatibility are major pluses.

Unfortunately, Microsoft missed the mark in some areas. CodeView 3.0 is not what programmers were expecting and the Programmer's WorkBench is a professional environment that is too bulky and too slow for many developers. Finally, the lack of complete paper documentation is simply inexcusable in a product priced at $450. Microsoft still has some work to do before they can accurately claim to have the best C compiler on the market.


Scott is a full-time freelance computer journalist. You can reach him at 705 West Virginia, Gunnison, CO 81230.


Based Pointers for Optimization

Bruce D. Schatzman

Microsoft C 6.0 provides an important new tool in the battle to keep code small and fast -- the based pointer. Virtually all Microsoft C programmers are familiar with near and far pointers, and how they are used within standard or mixed memory models. The 2-byte near pointers provide both size and speed advantages over the larger and slower 4-byte far pointers. As such, programmers tend to use near pointers within small memory models whenever possible.

Even the most carefully designed small model programs, however, sometimes hit the memory wall of DGROUP's 64K allocation. Programmers are then faced with a tough decision: Find a way to keep the program within the confines of a Small memory model to preserve the speed advantage, or admit defeat and move to larger and less efficient models such as compact or large. The inefficiency of larger models is primarily the result of the use of 4-byte (far) pointers. Each time a far data item is referenced, both the segment and offset address must be loaded into one of the 80x86's segment registers. This double load consumes more CPU cycles than near pointers (which load only an offset address), and increases both code and data sizes.

Microsoft C 6.0 provides a solution to this problem through the use of based pointers. This new data type gives you the reach of far pointers while retaining the size and speed advantages of near pointers. You can now increase your program's data space without necessarily moving to a larger memory model or a mixed (near/far) model.

Listing Five presents a small skeleton program that sets up a linked list of file names using a set of structures (TAG), with a maximum number of MAX_TAG structures. Two new keywords are used: _segment and _based. The statement _segment segvar; declares a variable that will hold a segment's memory address. segvar will become a segment in which a set of data is based -- thus the name "based pointers."

This basing is illustrated in the statement _based(segvar) *PTAG, TAG;, which defines a 2-byte structure pointer and a structure that are both based within the segment segvar.

The compiler generates code that automatically looks at the segvar variable each time a reference is made to *PTAG and TAG, necessitating only the use of a 2-byte offset address. Technically, *PTAG and TAG are both in a far heap, yet they act as if they reside in a near heap.

If MAX_TAG were smaller, this program could fit comfortably within a small memory model. However, with MAX_TAG - 2000, the tag list itself could fill up a 64-Kbyte data segment. Basing our structures within segvar means that as the program runs, successive references to these structures involve loading only the 2-byte segment offset. Because the structures are all within one segment, the segment address is preloaded into one of the segment registers and does not need to be reloaded every time a new segvar structure is referenced.

However, referencing another named segment (or far pointer) will requires loading a new value within a segment register. Therefore, based pointers are most effective for use with successive references to data items within the same segment, and offer performance equivalent to that of near pointers.

Note the program's use of the new C 6.0 library routines _bheapseg and _bmalloc, which allocate a based-heap segment and memory within that segment.

Although I've focused here or keeping small programs small based pointers can also be used to reduce the size (and increase the speed) of large programs. This is accomplished by converting some or all of a large program's far pointers to based pointers.

Based pointers are an important tool, but they have their limitations. Perhaps the most significant is the fact that their actual addressing capability is ?6 bits -- the size of a single segment. They do not have the unlimited 32-bit scope of far pointers, and thus cannot be used for very large data structures, such as arrays that exceed 64K, far pointers are the only real choice in these circumstances. Nevertheless, based pointers are a tool that will undoubtedly be used by a large percentage of Microsoft C programmers to optimize code for smaller size and greater speed.


Bruce Schatzman has worked in the computer industry for over 10 years, holding a variety of technical and marketing positions at corporations including General Dynamics, Tektronix, and Xerox. He is currently an independent consultant in Bellevue, Wash., specializing in systems consulting and technical communications.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.