# ...a little graphical 'benchmark'.

In case you hadn't noticed from my previous 'ramblings', I'm a big fan of David's 'resource binder'. I like to think that distributing an executable which contains all it's 'static' data, instead of using external files is neat !

Quite some time ago, before David modified res.e to allow the use of bitmaps, I 'stole' a routine from win32lib, itself, which allowed me to do this.

When David finally added rReadBmp() , I wrote a version of this benchmark to compare the two methods.

It should be pointed out that 'benchmarking' under Windows is not particularly accurate, because Windows *is* a multi-tasking system, and may be doing things in the background, that will influence another programs 'run-time'. Also, if you have a newer-faster computer, you might have to increase the loopCount variable to get meaningful results.

This program basically creates a pair of bitmaps from a single resource file 2000 times, and discards all but the last pair, which it finally writes out to the screen. The program creates two different windows, the first displays the results using loadBmpResource(), and the second window uses rReadBmp() and createDIB().

-- testresource.exw

    without type_check
    include win32lib.ew
    include res.e
    -- the two bitmaps, in a real .res file, this time. ( testres.res )
    rFile( "undercon.bmp" )
    rFile( "underco2.bmp" )

    integer loopCount
    -- increase this, if you have a super-puter.
    loopCount=2000

    constant
    MyWin = create( Window, "Test  loadBmpResource", 0, 0, 0, 300, 100, 0 ),
    Bitmap1 = create(  Bitmap, "", MyWin, 5, 5, 40, 38, 0 ),
    Bitmap2 = create(  Bitmap, "", MyWin, 55, 5, 40, 38, 0 ),
    Labely  = create( LText, "", MyWin, 10, 50, 230, 20, 0 ),

    MyWin2 = create( Window,
                   "Test  rReadBmp/createDIB", 0, 0, 100, 300, 100, {WS_CAPTION} ),
    Bitmap3 = create(  Bitmap, "", MyWin2, 5, 5, 40, 38, 0 ),
    Bitmap4 = create(  Bitmap, "", MyWin2, 55, 5, 40, 38, 0 ),
    Labelz  = create( LText, "", MyWin2, 10, 50, 230, 20, 0 )

    --pokes resource file to memory.
    function pokeResource(  sequence fileName,  atom fSize )
      atom        image
      integer     hFile,  byte

      hFile = rOpen(  fileName,  "rb"  )
      image = allocate(  fSize  )
      for i = 0 to fSize-1 do
      byte = getc ( hFile )
      poke(  image+i,  byte  )
      end for
      close( hFile )
      return image
    end function

    --slightly modified loadBitmapFromFile2()
    --reads BMP from resource file to memory.
    --you MUST specify the bitmap size !
    function loadBmpResource(  sequence fileName,  atom fSize )
      atom image,  bmInfoHeader,  bmBits,  bmColors,  hdc,  hDib

      image = pokeResource( fileName, fSize )

      bmInfoHeader = image + SIZEOF_BITMAPFILEHEADER
      bmBits = image + fetch(  image,  bfOffBits  )
      bmColors = bmInfoHeader + SIZEOF_BITMAPINFOHEADER
      hdc = getDC(  Screen  )

      hDib = w32Func(  xCreateDIBitmap,  {
      hdc, address(  bmInfoHeader,  bmiHeader  ),
      CBM_INIT,
      bmBits,
      bmInfoHeader,
      DIB_RGB_COLORS}  )
      releaseDC(  Screen  )
      free(  image  )
      return hDib
    end function

    atom ti,  map,  map2,  map5,  map6,  jk
    sequence map3,  map4

    procedure Start_up(  integer self,  integer event,  sequence params  )
    setVisible( MyWin, 1 )
    setVisible( MyWin2, 0 )
    ti=time(  )
    for i= 1 to loopCount do
      map=loadBmpResource(  "undercon.bmp",  892  )
      map2=loadBmpResource(  "underco2.bmp",  892  )
        if i < loopCount then
          jk = w32Func(  xDeleteObject, {map} )
          jk = w32Func(  xDeleteObject, {map2} )
        end if
    end for
    setBitmap(  Bitmap1,  map  )
    setBitmap(  Bitmap2,  map2  )
    repaintWindow( Bitmap1 )
    repaintWindow( Bitmap2 )
    setText( Labely,
    "bitmaps drawn in " & sprintf( "%3.2f", time(  )-ti ) & " seconds, " )
    -- now show second window
    setVisible( MyWin2, 1 )
    ti=time(  )
    for i= 1 to loopCount do
      map3=rReadBmp(  "undercon.bmp"  )
      map4=rReadBmp(  "underco2.bmp"  )
      map5=createDIB( map3 )
      map6=createDIB( map4 )
        if i < loopCount then
          deleteObject( map5 )
          deleteObject( map6 )
        end if
    end for
    setBitmap(  Bitmap3,  map5  )
    setBitmap(  Bitmap4,  map6  )
    repaintWindow( Bitmap3 )
    repaintWindow( Bitmap4 )
    setText( Labelz,
    "bitmaps drawn in " & sprintf( "%3.2f", time(  )-ti ) & " seconds, " )
    end procedure

    procedure kill_objects(  integer self,  integer event,  sequence params  )
       jk = w32Func(  xDeleteObject, {map} )
       jk = w32Func(  xDeleteObject, {map2} )
    end procedure

    setHandler(  MyWin,  w32HOpen,  routine_id(  "Start_up"  )  )
    setHandler(  MyWin,  w32HDestroy,  routine_id( "kill_objects" )  )

    WinMain( MyWin,  Normal )
    --end--

The main reason, of course, that the second block of code is so much slower, is that it converts the bitmap into a Euphoria ( DOS ) compatible sequence, which isn't really required in a Windows program.

--

I should point out that if you create a bitmap using createDIB(), you can destroy it with deleteObject().

You can't use this procedure with a bitmap created using loadBmpResource(). You have to call the function xDeleteObject() yourself, because win32lib will not destroy an object that it hasn't created internally.
... and you *should* , at least, destroy these objects on program exit, or they will STAY in memory !

--

On a historical note: these demos run at 32 and 100 seconds on my ancient 486,
...and at 8 and 24 seconds on a P166.

Benchmarking addendum !

Here's a little code snippet you can try at the beginning of any Windows 'benchmark' you write, which might improve the consistency of your tests. It basically just gives your program a higher priority, which usually defers any other program's ( background ) activity, until yours is finished. It's not recommended that you use this for normal programs as a whole.

    --
    without type_check
    -- define requirements, constants, and 'hooks' to DLL and functions.
    include dll.e
    object process_id,class_priority,thread_id,thread_priority
    constant
    --NORMAL_PRIORITY_CLASS = 32,
    HIGH_PRIORITY_CLASS = 128,
    --THREAD_PRIORITY_NORMAL = 0,
    THREAD_PRIORITY_TIME_CRITICAL = 15,
    kern32=open_dll("kernel32.dll"),
    current=define_c_func(kern32,"GetCurrentProcess",{},C_INT),
    zSetPriorityClass=define_c_func( kern32,"SetPriorityClass",{C_INT,C_INT},C_INT),
    zGetCurrentThread=define_c_func( kern32,"GetCurrentThread",{},C_INT),
    zSetThreadPriority=define_c_func( kern32,"SetThreadPriority",{C_INT,C_INT},C_INT)
    -- do it !
    process_id=c_func(current,{})
    class_priority=c_func(zSetPriorityClass,{process_id,HIGH_PRIORITY_CLASS})
    thread_id=c_func(zGetCurrentThread,{})
    thread_priority=c_func(zSetThreadPriority,{thread_id,THREAD_PRIORITY_TIME_CRITICAL})
    --

...end...

CONTENTS