Topic : C++ Performance
Author : Glen McCluskey & Associates LLC
Page : << Previous 3  Next >>
Go to page :


       {
                const double f = fact(25);

                if (a <= 0 || b <= 0)
                        return -1;

                /* use f in calculations */

                return 0;
        }


This approach does an expensive computation each time, even under error conditions. A way to avoid this would be to say:


        /* return -1 on error, else 0 */
        int f(int a, int b)
        {
                const double f = (a <= 0 || b <= 0 ? 0.0 : fact(25));

                if (a <= 0 || b <= 0)
                        return -1;

                /* use f in calculations */

                return 0;
        }


but the logic is a bit torturous. In C++, using declaration statements (see above), this problem can be avoided entirely, by saying:


        /* return -1 on error, else 0 */
        int f(int a, int b)
        {
                if (a <= 0 || b <= 0)
                        return -1;

                const double f = fact(25);

                /* use f in calculations */

                return 0;
        }





Part V
Stream I/O Performance


Is stream I/O slower than C-style standard I/O? This question is a bit hard to answer. For a simple case like:


        #ifdef CPPIO
        #include <iostream.h>
        #else
        #include <stdio.h>
        #endif

        main()
        {
                long cnt = 1000000L;

                while (cnt-- > 0)
        #ifdef CPPIO
                        cout << 'x';
        #else
                        putchar('x');
        #endif
                return 0;
        }


the C++ stream I/O approach is about 50% slower for a couple of popular C++ compilers. But putchar() is a macro (equivalent to an inline function) that has been tuned, whereas the C++ functions in iostream.h are less tuned, and in the 50% slower case not all the internal little helper functions are actually inlined. We will say more about C++ function inlining some other time, but one of the issues with it is trading space for speed, that is, doing a lot of inlining can drive up code size.


And 50% may be irrelevant unless I/O is a bottleneck in your program in the first place.




Part VI
Stream I/O Output


In issue #006 we talked about stream I/O, and an example like this was shown:


        cout << x << "\n";


A couple of people wrote. One said that:


        cout << x << endl;

was preferable, while another said:


        cout << x << '\n';

would be a better choice on performance grounds, that is, output a single character instead of a C string containing a single character.


Using one popular C++ compiler (Borland C++ 4.52), and outputting 100K lines using these three methods, the running times in seconds are:


        "\n"            1.9

        '\n'            1.3

        endl            13.2

Outputting a single character is a little simpler than outputting a string of characters, so it's a bit faster.


Why is endl much slower? It turns out that it has different semantics. Besides adding a newline character like the other two forms do, it also flushes the output buffer. On a UNIX-like system, this means that ultimately a write() system call is done for each line, an expensive operation. Normally, output directed to a file is buffered in chunks of size 512 or 1024 or similar.


The Borland compiler has a #define called _BIG_INLINE_ in iostream.h that was enabled to do more inlining and achieve the times listed here.


Does this sort of consideration matter very much? Most of the time, no. If you're doing interactive I/O, it is best to write in the style that is plainest to you and others. If, however, you're writing millions of characters to files, then you ought to pay attention to an issue like this.


Note also that there's no guarantee that performance characteristics of stream I/O operations will be uniform across different compilers. It's probably true in most cases that outputting a single character is cheaper than outputting a C string containing a single character, but it doesn't have to be that way.




Part VII
Per-class New/Delete


Some types of applications tend to use many small blocks of space for allocating nodes for particular types of data structures, small strings, and so on. In issue #002 we talked about a technique for efficiently allocating many small strings.


Another way of tackling this problem is to overload the new/delete operators on a per-class basis. That is, take over responsibility for allocating and deallocating the storage required by class objects. Here is an example of what this would look like for a class A:


        #include <stddef.h>
        #include <stdlib.h>

        class A {
                int data;
                A* next;
        #ifdef USE_ND
                static A* freelist;             // pointer to free list
        #endif
        public:
                A();
                ~A();
        #ifdef USE_ND
                void* operator new(size_t);     // overloaded new()
                void operator delete(void*);    // overloaded delete()
  


Page : << Previous 3  Next >>