Lecture Notes for Advanced Programming II

13 March 2001 - Virtual Call Costs: A case study


Typical Virtual Classes

class shape {
  public:
    virtual double area(void) const = 0;
  };

class rectangle : public shape {
  public:
    virtual double area(void) const {
      return 0.0;
      }
  };

class square : public rectangle {
  public:
    virtual double area(void) const {
      return 0.0;
      }
  };


Why Do Virtual Calls Cost More?


For Example

const unsigned loop_maximum = 100000000;

int main(int argc, char * argv[]) {

  int i;
  double f;
  time_t s, oh;

  timeit(oh, );
  printf("Loop overhead is %g usec/iteration.\n",
	 ((double) oh)/((double) loop_maximum));

  square * sqp = new square;
  timeit(s, sqp->area());
  printf("Time per call for %d calls: %g usec.\n", loop_maximum,
	 ((double) (s - oh))/((double) loop_maximum));

  shape * shp = new square;
  timeit(s, shp->area());
  printf("Time per call for %d calls: %g usec.\n", loop_maximum,
	 ((double) (s - oh))/((double) loop_maximum));

  return EXIT_SUCCESS;
  }


Well, Do They?

cl uname -a
SunOS clayton 5.7 Generic_106541-11 sun4u sparc SUNW,Ultra-5_10

cl date
Tue Mar 13 16:51:11 EST 2001

cl g++ -o t0 t0.cc

cl ./t0
Loop overhead is 0.0393402 usec/iteration.
Time per square call for 100000000 calls: 0.109822 usec.
Time per shape call for 100000000 calls: 0.105269 usec.

cl CC -o t0 t0.cc

cl ./t0
Loop overhead is 0.0120949 usec/iteration.
Time per square call for 100000000 calls: 0.0998956 usec.
Time per shape call for 100000000 calls: 0.10231 usec.

cl 


Maybe It's Inlining

cl cat shape.h
class shape {
  public: virtual double area(void) const = 0;
  };

class rectangle : public shape {
  public: virtual double area(void) const;
  };

class square : public rectangle {
  public: virtual double area(void) const;
  };

cl cat shape.cc
#include "shape.h"

double rectangle::area(void) const {
  return 0.0;
  }

double square::area(void) const {
  return 0.0;
  }

cl g++ -o t1 t1.cc shape.cc

cl ./t1
Loop overhead is 0.0397327 usec/iteration.
Time per square call for 100000000 calls: 0.105852 usec.
Time per shape call for 100000000 calls: 0.105827 usec.

cl CC -c shape.cc t1.cc
shape.cc:
t1.cc:

cl CC -o t1 t1.o shape.o

cl ./t1
Loop overhead is 0.0120932 usec/iteration.
Time per square call for 100000000 calls: 0.0993437 usec.
Time per shape call for 100000000 calls: 0.0993558 usec.

cl 


Other Tries Elsewhere

ro uname -a
Linux rockhopper.monmouth.edu 2.2.16-22smp #1 SMP 
Tue Aug 22 16:39:21 EDT 2000 i686 unknown

ro g++ -c t1.cc shape.cc

ro g++ -o t1 t1.o shape.o

ro ./t1
Loop overhead is 0.00995604 usec/iteration.
Time per square call for 100000000 calls: 0.0117943 usec.
Time per shape call for 100000000 calls: 0.0117474 usec.

ro 

***

mo uname -a
OSF1 moncol.MONMOUTH.EDU V5.1 732 alpha

mo cxx -c t1.cc shape.cc
t1.cc:
shape.cc:

mo cxx -o t1 t1.o shape.o

mo ./t1
Loop overhead is 0 usec/iteration.
Time per square call for 100000000 calls: 0.115789 usec.
Time per shape call for 100000000 calls: 0.113572 usec.

mo


Maybe It's the Pointer


What Was That Timing Thing?

#define timeit(et, stmts) \
  do { \
    struct timeval _ts, _te; \
    unsigned _i; \
    (void) gettimeofday(&_ts, NULL); \
    for (_i = 0; _i < loop_maximum; _i++) { stmts ; } \
    (void) gettimeofday(&_te, NULL); \
    if (_ts.tv_sec < _te.tv_sec) { \
      _te.tv_sec--; \
      _te.tv_usec += 1000000; } \
    et = (_te.tv_sec - _ts.tv_sec)*1000000 + (_te.tv_usec - _ts.tv_usec); \
    } while (false)


This page last modified on 27 February 2001.