Newsgroups: comp.lang.c++,comp.lang.misc,comp.lang.lisp,comp.arch
Path: cantaloupe.srv.cs.cmu.edu!europa.chnt.gtegsc.com!usenet.eel.ufl.edu!hookup!news.moneng.mei.com!news.ecn.bgu.edu!vixen.cso.uiuc.edu!howland.reston.ans.net!xlink.net!news.ppp.de!news.Hanse.DE!wavehh.hanse.de!cracauer
From: cracauer@wavehh.hanse.de (Martin Cracauer)
Subject: Re: allocator and GC locality (was Re: cost of malloc)
Message-ID: <1995Aug7.152640.27088@wavehh.hanse.de>
Organization: The Internet
References: <jyvgwh@bmtech.demon.co.uk> <807474453snz@wildcard.demon.co.uk> <3vssnd$hd0@news.cs.tu-berlin.de> <3vtcud$u4@wcap.centerline.com> <hbaker-0408951324110001@192.0.2.1> <jywahs@bmtech.demon.co.uk>
Date: Mon, 7 Aug 95 15:26:40 GMT
Lines: 223
Xref: glinda.oz.cs.cmu.edu comp.lang.c++:142602 comp.lang.misc:22529 comp.lang.lisp:18581 comp.arch:60163

Scott Wheeler <scottw@bmtech.demon.co.uk> writes:

>In Article <hbaker-0408951324110001@192.0.2.1> Henry Baker writes:
>>The Stroustrup/Coplien school advocates using 2 mallocated objects in 
>>C++ when one will do in C, and this thread already described many of 
>>the horrors of malloc inefficiencies.

>Only one allocation of heap memory (not *malloc*, please - that's C) 
>unless you are using "new" to generate the string, because the top 
>level of the object will go on the stack. Since temporaries and 
>automatic variables go on the stack, this makes a substantial 
>difference.

>>So the point of the posting was that the Stroustrup/Coplien style
>>_guarantees_ bad performance, 

>I've yet to see any hard data posted. The 3x quoted repeatedly here has 
>not yet been shown to be more than a guess.

Maybe this is too mimple-simded, but at least it shows something.

The following c++ fragment compares the performance of two strings
classes. One with a length integer and a pointer to a malloc'ed string
and the other useing an array of chars and useing the fist byte to
store the length. (Note that the code is benchmark-only, no care is
taken that the length element of the second class is not returned as a
member of the string).

The timing is done for a number of accesses to the strings while every
access includes checking the index to be greater than the length,
therefore touching the length field. This is an access test to see the
effect of seperating length and char field in memory, not an
allocation test. Time for object creation is not counted.

The code is customizeable with 'g++|CC -D...=x' in the following ways:
- length of the string
- number of strings
- number of iterations (to reach significant run time)
- whether to use pointers for the test objects or not

Note that for tiny strings the direct class will be faster because the
objects are smaller.

You have to either insert your own timing routines or use the lib
included in a seperate uuencoded .tar.gz file at the end of this
posting.

The result of this 'benchmark' was that on some machine/stringlength -
combinations the "direct" class is 20-25 % faster, but that there are
cases where the indirect one is faster. Exact results are to be posted
when this code is gone through revision by usenet, to avoid speaking
about several versions of this code.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// This is C++ !
#include <stdio.h>
// for some machines, use malloc.h instead of unistd.h
// #include <malloc.h>
#include <unistd.h>
#include <timing.h>  // see end of posting

#ifndef TEST_STRINGLENGTH
#define TEST_STRINGLENGTH 5
#endif

#ifndef TEST_NSTRINGS
#define TEST_NSTRINGS 1
#endif

#ifndef TEST_N_ITERATIONS
#define TEST_N_ITERATIONS 1000000
#endif

// #define USE_POINTERS

struct Test1 {
  unsigned char length;
  unsigned char *string;

  Test1() {
    int i;
    if (! (string = (unsigned char *)malloc(TEST_STRINGLENGTH+1))) {
      fprintf(stderr,"Malloc failed\n");
      exit(2);
    }
    for (i=0;i<TEST_STRINGLENGTH;i++)
      string[i]='%';
    string[TEST_STRINGLENGTH]=0;
    length = TEST_STRINGLENGTH;
  }
  char aref(int where) {
    if ( where > (int)length) {
      fprintf(stderr,"Failed\n");
      exit(2);
    }
    return string[where];
  }
};

struct Test2 {
  unsigned char string[TEST_STRINGLENGTH+1]; // + 1 for terminal NULL

  Test2() {
    int i;
    for (i=0;i<TEST_STRINGLENGTH;i++)
      string[i]='&';
    string[TEST_STRINGLENGTH]=0;
    string[0] = TEST_STRINGLENGTH;
  }
  char aref(int where) {
    if ( where > (int)string[0]) {
      fprintf(stderr,"Failed\n");
      exit(2);
    }
    return string[where];
  }
};

double test1()
{
  int i,j,k;
  char c='_';
  double cputime,dummy;

#ifndef USE_POINTERS 
  Test1 test[TEST_NSTRINGS]; 
#else
  Test1 *test[TEST_NSTRINGS]; 

  for (i=0;i<TEST_NSTRINGS;i++)
    test[i] = new Test1;
#endif
  
  timestamp(); // I want real macros...
  for (k=0;k<TEST_N_ITERATIONS;k++)
    for (i=0;i<TEST_NSTRINGS;i++)
      for (j=0;j<TEST_STRINGLENGTH;j++) {
	c = test[i]
#ifdef USE_POINTERS
	  ->
#else
	  .
#endif
	  aref(j);
      }
  return ( return_time(&cputime,&dummy));
}

double test2()
{
  int i,j,k;
  char c='_';
  double cputime,dummy;
#ifndef USE_POINTERS 
  Test2 test[TEST_NSTRINGS]; 
#else
  Test2 *test[TEST_NSTRINGS]; 

  for (i=0;i<TEST_NSTRINGS;i++)
    test[i] = new Test2;
#endif
  
  timestamp(); // I want real macros...
  for (k=0;k<TEST_N_ITERATIONS;k++)
    for (i=0;i<TEST_NSTRINGS;i++)
      for (j=0;j<TEST_STRINGLENGTH;j++) {
	c = test[i]
#ifdef USE_POINTERS
	  ->
#else
	  .
#endif
	  aref(j);
      }
  return ( return_time(&cputime,&dummy));
}

main()
{

  double t1,t2;

  t1 = test1();
  t2 = test2();
  printf("%f %f\n",t1,t2);

  t1 = test1();
  t2 = test2();
  printf("%f %f\n",t1,t2);

  return 0;
}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
begin 644 timing.tar.gz
M'XL( '8M)C   ^U66V_;-A3.Z_0K#A2XDS19EEHO!N9YP! D75"W&)IV3P,T
M1:)B ;(DD%3:K.U_WR$I6_1%2_J070!^@"#IW'A(GO.1C*837JR+ZC9(3YX(
M41B>3:=P @#1+-IY"[R832. LS *IV'T_6R*VA=1.#N!\*D2TM$RGE" DY0F
M:=(2.F3WD/Y_BM.B2LLV(_ CXUE1!ZN?+$N3W3-1'42(3XL<,I(7%<F<ZWOV
MFPN?/V\%<<SNZ#2.7<VYQ++B3+EN9'VP_2&4(2D9V5-2PNJ6ILJ-5%F1:QG:
M7>VN;,O"C>1%"EG=WI0$RH3Q.&W$]JX;6$ 8A/-C)G)LW69C5%1<5.=-PHBP
M085U5Q<9%'5,**VIDZZP<#Q*$E97KO7) L@;BEZY@TN))KY]258EH7!#"ABQ
MWRO;[XSG:$L^%MR)\/-+%W>;B*."'60GOF-6QWE"'1EB?XKXJ>DQ;C=/78ZA
M'[F1 (S3-N7 UTP\N   Z.K(G)QG*')AL8!Q)&RA7Q=;&M@R14IX2RMP')6*
M*P(%^,2M,')AHJG.EZ_B=^>O7/A.QCOT8<,^F)RJG6W6M&7)+>E>,O=;PM6?
M\_;]]<\O+^+KB^6E#\^44$MW;WBE#VBKD@[X7<Q(NDE36/Z=:2MM)X D*!"$
MO>/Q0=C1008'8L,#S3?]TI?"3@U]32UTW2!PEY3SX>IP4-T5:U<D#U7)L3+!
M((_=:!%'C,F;;0ZXTT):YUERCSDT/CB:\9]U1<!SX<W[Y7(@-SW 0"$[O.DV
M"<9;DG#[XNVKMWGT[JA!9--W[N!A\_H;PA)D4RIV$/*#E@=E<<@5O8-\C7>I
M0W=4[_$>^V@K( +TI"4)3R6LZ/!#PGRXO%I>@,>92E7]Y45)>@IABP4V@I M
M%%G.0>RJDG!531LV%3+?'@7/<V#@C((H'XU<V_H&P->G/]Z9DU0[PWIWXNRL
MT7AWPJX7B4T24=QMUC@W52B[F?T ?XS8MX+>A8'D=E6@^X9HXAY;NE@M0<?[
MVI*&OM(,>M4M'_!"C?)ZX/SO#M+-'7#U%'>,!^Y_, O#[?WO+ K%_6]F[G__
M#/  J)#O(8[?7;V^>O,R_L4Z5?ROB\0YH:S2IFR9>"SRD1/D _O<AD_;BQG 
MQ(.W!-F020ZBL"9(#%7!UN!-4+U_U1'M<N2:HHD/J4P.(<E(D%E292!)2QBJ
M0;Z*3C$D!OQ5](_*&3X4? 4)9L[D%8+7^",Z& ]&EM*BX375)K-'@=[&SQ>\
MY\F9#/7[@$YV]?SHHG_IK\#J_6_7CX&!@8&!@8&!@8&!@8&!@8&!@8&!@8&!
.@<%_"W\!-7!\B  H  #I
 
end
-- 
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Martin Cracauer <cracauer@wavehh.hanse.de>. No NeXTMail, please.
 Norderstedt/Hamburg, Germany. Fax +49 40 522 85 36. This is a 
 private address. At (netless) work programming in data analysis.
