...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