Article ID:qGEN006
Date Revised:November 14, 1998
Keywords:DoEvents, OOP, FastDoEvents

Question: After adding a DoEvents command in my loop the performance has degraded. How can I speed it back up?

Answer: Calling DoEvents is just plain slow.

lnStart = seconds()

for i = 1 to 100
   DoEvents
endfor

? "Time to execute 100 DoEvents:", seconds() - lnStart

On a P166 running Win95 the above code takes an average of 26 seconds to execute, so each call to DoEvents takes a whopping 0.26 seconds!

There isn't too big a need to execute a DoEvents for every iteration of a tight loop, once every 100 or 1000 iterations is typically fast enough. A second counter can be used to control a conditional call to DoEvents(). Wrapping all of this into an object is trivial:

define class DoEventsControl as Line
   mnIterationCount = 0
   mnIterationLimit = 1000

procedure Init( pnIterationLimit )
   if ( pcount() = 1 )
      this.mnIterationLimit = m.pnIterationLimit
   endif
endproc

procedure Execute( plForced )
   with this
      .mnIterationCount = ( .mnIterationCount + 1 ) % .mnIterationLimit
      if ( .mnIterationCount = 0 ) or m.plForced
         DoEvents
      endif
   endwith
endproc
enddefine

So the above loop can be rewritten as:

lnStart = seconds()

oDoEvents = createobject( "DoEventsControl" )

for i = 1 to 10000
   oDoEvents.Execute()
endfor

oDoEvents.Release()

? "Time to execute 10,000 calls", seconds() - lnStart

The above code which only makes 10 DoEvents calls executes in 2.9 seconds which means the oDoEvents only adds 0.3 seconds for 10,000 iterations.

Update 14-Nov-98

Recently while working on an application I noticed an oddity in the speed of DoEvents under VFP6. While reporting the problem one of the Microsoft engineers noticed that DoEvents runs orders of magnitude faster if the mouse was moving. So that revelation spawned this alternative to DoEvents:

* FastDoEvents.prg 30-Sep-98

* 07-Nov-98 added keyboard for when mouse is out of VFP

* this workaround speeds up a DoEvent call

local lnRow, lnCol, lcWindow

lcWindow = wontop()
lnRow = mrow( lcWindow )
lnCol = mcol( lcWindow )
if ( lnRow > 0 ) and ( lnCol > 0 )
   * mouse still within VFP window, so it's ok to mouse
   mouse at mrow(), mcol() window (lcWindow)
else
   keyboard " "
   =inkey()
endif

DoEvents

FastDoEvents() executes on the order of 0.6 milliseconds per call making it 433 times faster!

The keyboard addition came after a thread of discussion with George Tasker over on the UniversalThread. The DoEventsControl class above can be modified to take further advantage of the workaround:

procedure Execute( plForced )
   with this
      .mnIterationCount = ( .mnIterationCount + 1 ) % .mnIterationLimit
      if ( .mnIterationCount = 0 ) or m.plForced
         FastDoEvents()
      endif
   endwith
endproc


1