Efficiency issues? Function takes 7-10 seconds to complete/refresh

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • Knut Ole
    New Member
    • Mar 2011
    • 70

    Efficiency issues? Function takes 7-10 seconds to complete/refresh

    I have a function to draw some shapes on a form depending on values in queries/tables. The whole procedure takes up to ten seconds however, and as I'm new to coding, I assume I might have a lot to gain on making my code more efficient...?

    Are there any very obvious time-consumers in my code, which can be settled much more efficiently?

    Any guidance would be greatly appreciated.



    (In the db I have a main form "Calendar," which has 67 subforms (frmSub01-67) in which drawings are made. The "finished product" looks more or less like this:
    Code:
    Public Function makeArrows()
    
        Dim RecSetSortAll As Recordset
        Dim RecSetDatePoint As Recordset
        Dim RecSetRooms As Recordset
        
        Dim ctl As Control
        Dim frmSub As Form
        Dim strSubForm As Form
        
        
        startTime = Time()
    '***************************
    '''initializing recordsets
    '***************************
        'Table!DatePointer.Refresh
       ' Tables!Bookings.Refresh
        'Tables!Contacts.Refresh
        'Queries!qSortAll.Refresh
        
        Set RecSetDatePoint = CurrentDb.OpenRecordset("DatePointer")
        Set RecSetSortAll = CurrentDb.OpenRecordset("qSortAll")
        Set RecSetRooms = CurrentDb.OpenRecordset("qAllRoomsList")
        
        
        'Set rsRoomPoint = CurrentDb.OpenRecordset("qRoomPoint")
        'Set rsTotRooms = CurrentDb.OpenRecordset("Rooms")
      
    ''' find rsDatePointer for calendarview start date, formerly variable "c"
        dStart = RecSetDatePoint.Fields("RStartDate").Value
    
    ''' find total no of rooms / rows in calendar
        'totRooms = rsTotRooms.RecordCount
        'MsgBox totRooms
        
        'd = 0
        'intNoRooms = RoomPoint
        'str( = "frmSub1"
        'str1 = "frmSub01"
    
    
    
    '***************************************************
    '''looping thru every subform control to disconnect
    '***************************************************
        For Each ctl In Forms!Calendar
    
            If ctl.ControlType = acSubform Then
             If Left(ctl.Name, 3) = "ctr" Then
                'srcObject = Forms!Calendar!(ctl.Name).SourceObject
                'MsgBox ctl.Name
             ''' disconnecting subform from mainform, opening subform
                Forms!Calendar!(ctl.Name).SourceObject = ""
                'Forms!Calendar!(ctl.Name).Height = 400
                
                
                
             End If
            End If
    
        Next ctl
    'MsgBox "disconnected"
    
    
    
    
    '******************************************
    ''' delete all controls in all subforms ...
    '******************************************
            p = 0
            
            Do While p < 67
                p = p + 1
                    If p < 10 Then
                        strForms = "frmSub0" & p
                    Else
                        strForms = "frmSub" & p
                    End If
                
                DoCmd.OpenForm strForms, acDesign, , , acFormEdit, acHidden
                
                Do While Forms(strForms).Controls.Count > 0
                    DeleteControl strForms, Forms(strForms).Controls(0).Name
                Loop
                'With Forms(strForms).Controls
                '     Do While .Count > 0
                '       Call DeleteControl(strForms, .Item(0).Name)
                '     Loop
                'End With
                
                
                DoCmd.Close acForm, strForms, acSaveYes
            Loop
        'MsgBox p & " subforms massacred..."
           
           
    
         
         
        
    '*****************************
    '  ARROWS LOOP for ALL ROOMS
    '*****************************
        q = 0                                                                              'frmSub number count
       
        Do Until RecSetRooms.EOF
       
            '**************************************************
            'drawing room# box for each room (each RecSetRooms)
            '**************************************************
                q = q + 1
                                                                                            '*******************************************
                If q < 10 Then strForms = "frmSub0" & q Else strForms = "frmSub" & q        'determining which form, counting roomlist!!
                                                                                            '*******************************************
                roomNow = RecSetRooms.Fields("RoomNumber").Value
                'MsgBox "drawing room-number " & roomNow & " in " & strForms & "..."
                
                DoCmd.OpenForm strForms, acDesign, , , acFormEdit, acHidden                 'open frmSub for drawing
                
                '*******************
                'DRAW: room-number box
                '*******************
                Set cRoomBox = CreateControl(strForms, acLabel)
                    With cRoomBox
                        .BackStyle = 1
                        .Width = 580
                        .Height = 375
                        .Left = 50
                        .Top = 25
                        .Caption = roomNow
                        .BorderStyle = 1
                        .FontSize = 14
                        .TextAlign = 2
                        .BorderWidth = 1
                        .FontWeight = 700
                        '.BackColor = RGB(0, 255, 0)
                    End With
                    
                DoCmd.Close acForm, strForms, acSaveYes                        'close frmSub after drawing
        
                '********
                'end draw
                '********
        
        
        
            '************************************
            'start drawing bookings for this room
            '************************************
            
            If Not RecSetSortAll.EOF Then                                       'make sure still bookings to be drawn, if not, return to room-number loop
            
            
                recordNow = RecSetSortAll.Fields("qBase.RoomNumber").Value      'recordset of bookings
                roomNow = RecSetRooms.Fields("RoomNumber").Value                'list of roomnumbers
                'MsgBox "before if recnow = roomnow, values are " & recordNow & " = " & roomNow
                If recordNow = roomNow Then                                     'there are bookings for this room
                Do Until recordNow <> roomNow
                        'MsgBox "inside loop of recnow <> roomnow, w values " & recordNow & " <> " & roomNow
                        '***************************
                        ' open subform (again), draw
                        '***************************
                                                                                                '*******************************************
                        If q < 10 Then strForms = "frmSub0" & q Else strForms = "frmSub" & q   'determining which form, counting roomlist!!
                                                                                                '*******************************************
                                                                                                '(not really necessary, as q only changes with roomlist)
                        'MsgBox "match! starting drawing in " & strForms & "..."
                        
                        DoCmd.OpenForm strForms, acDesign, , , acFormEdit, acHidden   'open frmSub for drawing
                                       
                        
                    '// finding variables
                        slBeg = RecSetSortAll.Fields("SlotBegin").Value
                        slEnd = RecSetSortAll.Fields("SlotEnd").Value
                        rcBookID = RecSetSortAll.Fields("Bookings.ID").Value
                        leftPos = (slBeg - dStart) * 1217 + 850
                        bConf = RecSetSortAll.Fields("Confirmed").Value
                        'MsgBox bConf
                        'widthMsg = ((slEnd - slBeg) * 1217) - 380
                        'MsgBox "starting pos = " & leftPos & ", and width = " & widthMsg
                        
                        
                    '// TAIL IMAGE
                        Set cImg1 = CreateControl(strForms, acImage)
                            If bConf = True Then
                                With cImg1
                                    .BackStyle = 0
                                    .Width = 432
                                    .SizeMode = 0
                                    .Height = 360
                                    .Left = leftPos
                                    .Top = 30
                                    .ControlTipText = rcBookID
                                    .OnClick = ""
                                    .Picture = "C:\Users\Lailita\Documents\arrows\yellowA.wmf"
                                End With
                            Else
                                With cImg1
                                    .BackStyle = 0
                                    .Width = 432
                                    .SizeMode = 0
                                    .Height = 360
                                    .Left = leftPos
                                    .Top = 30
                                    .ControlTipText = rcBookID
                                    .OnClick = ""
                                    .Picture = "C:\Users\Lailita\Documents\arrows\yshadeA.wmf"
                                End With
                            End If
                        
                        
                        
                        '// HEAD IMAGE
                          Set cImg2 = CreateControl(strForms, acImage)
                            If bConf = True Then
                                With cImg2
                                    .BackStyle = 0
                                    .Width = 432
                                    .Height = 360
                                    .SizeMode = 0
                                    .ControlTipText = rcBookID
                                    .Left = leftPos + ((slEnd - slBeg) * 1217) - 380
                                    '.Left = leftPos + 300
                                    .Top = 30
                                    .Picture = "C:\Users\Lailita\Documents\arrows\yellowAh.wmf"
                                End With
                            Else
                                With cImg2
                                    .BackStyle = 0
                                    .Width = 432
                                    .SizeMode = 0
                                    .Height = 360
                                    .ControlTipText = rcBookID
                                    .Left = leftPos + ((slEnd - slBeg) * 1217) - 380
                                    '.Left = leftPos + 300
                                    .Top = 30
                                    .Picture = "C:\Users\Lailita\Documents\arrows\yshadeAh.wmf"
                                End With
                            End If
                                                
                         '// Shaded Box
                        Set cImg3 = CreateControl(strForms, acImage)
                        If bConf = False Then
                           With cImg3
                                .BackStyle = 0
                                .Width = ((slEnd - slBeg) * 1217) - 432 - 40
                                .Height = 360
                                .SizeMode = 0
                                .ControlTipText = rcBookID
                                .Left = leftPos + 250
                                '.Left = leftPos + 300
                                .Top = 30
                                .Picture = "C:\Users\Lailita\Documents\arrows\yshadeB.wmf"
                            End With
                        Else
                            With cImg3
                                .BackStyle = 0
                                .Width = ((slEnd - slBeg) * 1217) - 432 - 40
                                .Height = 360
                                .SizeMode = 0
                                .ControlTipText = rcBookID
                                .Left = leftPos + 250
                                '.Left = leftPos + 300
                                .Top = 30
                                .Picture = "C:\Users\Lailita\Documents\arrows\yellowB.wmf"
                            End With
                            'MsgBox widthMsg
                        End If
                        
                            
                        '// BODY LABEL
                          Set cLbl1 = CreateControl(strForms, acLabel)
                            With cLbl1
                                .Visible = True
                                .BackColor = RGB(255, 194, 14)
                                .ForeColor = RGB(255, 255, 255)
                                .FontWeight = 900
                                .TopMargin = 34
                                .TextAlign = 2
                                .BackStyle = 0
                                .FontWeight = 900
                                .Top = 40
                                .Height = 330
                                .Left = leftPos + 250
                                .Width = ((slEnd - slBeg) * 1217) - 432 - 40
                                '.Width = 200
                                .Caption = rcBookID
                            End With
                         If bConf = False Then
                            With cLbl1
                                .ForeColor = RGB(0, 0, 0)
                            End With
                         End If
                            
                        DoCmd.Close acForm, strForms, acSaveYes                        'close frmSub after drawing of booking
                        
                        
                        
                       ' MsgBox "success, moving to next booking..."
                                            
                        RecSetSortAll.MoveNext                                         'moving to next booking
                        
                        If Not RecSetSortAll.EOF Then
                        recordNow = RecSetSortAll.Fields("qBase.RoomNumber").Value     'roomnumber of next booking
                        Else
                            Exit Do
                        End If
                            
                Loop
                
                Else
                   'MsgBox "no more bookings for this room, moving on to next room.."
                   ' RecSetSortAll.MoveNext
                End If
         
         
         
         
    
         
            Else    'RecSetSortAll.EOF If..Then
                'MsgBox "recsetsortall.eof"
            End If
            RecSetRooms.MoveNext
            
        Loop       'RecSetRooms.EOF loop
         
         
         
         
         
         
         
         
         
         
         
         
         
         
         'MsgBox "reconnecting"
         
    '*************************************************
    '''looping thru every subform control to RECONNECT
    '*************************************************
        
        For Each ctl In Forms!Calendar
    
            If ctl.ControlType = acSubform Then
             If Left(ctl.Name, 3) = "ctr" Then
                'srcObject = Forms!Calendar!(ctl.Name).SourceObject
                'MsgBox ctl.Name
             ''' disconnecting subform from mainform, opening subform
                srcOb = "frmSub" & Right(ctl.Name, 2)
                Forms!Calendar!(ctl.Name).SourceObject = srcOb
               
             End If
            End If
    
        Next ctl
        
        'MsgBox "reconnected"
        
        'DoCmd.Requery
        'MsgBox "refreshing"
        Forms!Calendar.Refresh
        
        'MsgBox "refreshed"
        
        endTime = Time()
        elapsedTime = endTime - startTime
        
        MsgBox "that took " & Second(elapsedTime) & " secs."
        
        
        
        
        
        
        
        
        
        
        
        
               
                
                
                
                
                
          
    
    
        
    
        
        
    
    
    
    
    
    
        
        
       
        
        
    
    
    
    
        
    End Function
    Attached Files
  • TheSmileyCoder
    Recognized Expert Moderator Top Contributor
    • Dec 2009
    • 2322

    #2
    I haven't looked through your code as its quite lengthy. I will maybe give it a go later.
    What I would suggest first is to turn of redrawing while your code runs. Add Docmd.Echo False at the start of your code and Docmd.Echo true at the end.
    You should also add an error handler in which you turn the drawing back on, otherwise you will be stuck with an empty screen if an error occurs.

    Comment

    • Knut Ole
      New Member
      • Mar 2011
      • 70

      #3
      i appreciate that!

      it's especially the two segments at line 47-62 and line 73-93, they eat up most of the time consumed. i might have made them rather chunky...

      Comment

      • ADezii
        Recognized Expert Expert
        • Apr 2006
        • 8834

        #4
        It appears to me that the long processing time has nothing to do with the efficiency of the Code, but rather with the nature of the Task. Among other things, you are dynamically Deleting/Creating Controls on none less than 67 Sub-Forms, a tall order in and of itself. There are a few items that I noticed which may provide small gains in processing time:
        1. Always explicitly refer to the Object type Libraries to which Objects belong.
          Code:
          Dim RecSetSortAll As DAO.Recordset 
          Dim RecSetRooms As DAO.Recordset
        2. If you are only moving Forward within a Recordset, make it a Forward Only Type Recordset:
          Code:
          Set RecSetSortAll = CurrentDb.OpenRecordset("qSortAll", dbOpenSnapshot, dbOpenForwardOnly)
          Set RecSetRooms = CurrentDb.OpenRecordset("qAllRoomsList", ", dbOpenSnapshot, dbOpenForwardOnly)
        3. Not sure if it is worth it to create a Recordset for a Single Field Lookup, try a DLookup() instead:
          Code:
          dStart = DLookup("[RStartDate]", "DatePointer")
                    'instead of
          Dim RecSetDatePoint As Recordset
          Set RecSetDatePoint = CurrentDb.OpenRecordset("DatePointer")
          dStart = RecSetDatePoint.Fields("RStartDate").Value
        4. I see no Clean Up chores being performed, namely:
          Code:
          RecSetSortAll.Cose 
          RecSetRooms.Close
          Set RecSetSortAll = Nothing 
          Set RecSetRooms = Nothing
        5. I didn’t have much time to look over the Code thoroughly, so if I am incorrect in any of my assumptions, please forgive me.

        Comment

        • Rabbit
          Recognized Expert MVP
          • Jan 2007
          • 12517

          #5
          Rather than a subform for each room, why not one form with all the rooms?

          Comment

          • Knut Ole
            New Member
            • Mar 2011
            • 70

            #6
            thanks all,


            rabbit: would that save me a lot?

            Comment

            • TheSmileyCoder
              Recognized Expert Moderator Top Contributor
              • Dec 2009
              • 2322

              #7
              I have some code that will at least help you to isolate which parts take the most time. The code should be placed in a class module.
              At the bottom of teh module is an example of how to use it.
              Code:
              Option Compare Database
              Option Explicit
              Option Base 1
              
              '***********  Class variables  ****************
              Private lngStart As Long
              Private lngLastEvent As Long
              Private strEvent() As String
              Private strLog As String
              'Reference function
              Private Declare Function GetTickCount Lib "kernel32" () As Long
              
              
              
              Public Sub StartTimer()
                  lngStart = GetTickCount
                  lngLastEvent = lngStart
                  ReDim strEvent(2, 1)
                  strEvent(1, UBound(strEvent, 2)) = "Timer Started"
                  strEvent(2, UBound(strEvent, 2)) = EndTimer
              End Sub
              
              Public Function addEvent(strInput As String, Optional bFromStart As Boolean = False)
                  strLog = strLog & strInput
                  
                  
                  ReDim Preserve strEvent(2, UBound(strEvent, 2) + 1)
                  strEvent(1, UBound(strEvent, 2)) = strInput
                  strEvent(2, UBound(strEvent, 2)) = EndTimer(bFromStart)
                  lngLastEvent = GetTickCount
              End Function
              
              Public Function toString() As String
                  Dim intI As Integer
                  Dim lngSpacesEvent As Long
                  Dim lngSpacesTime  As Long
                  lngSpacesEvent = maxLen(strEvent(), 1)
                  lngSpacesTime = maxLen(strEvent(), 2)
                  
                  For intI = 1 To UBound(strEvent, 2)
                      toString = toString & addSpaces(strEvent(1, intI), lngSpacesEvent) & " : " & addSpaces(strEvent(2, intI), lngSpacesTime, False) & vbNewLine
                  
                  Next
              End Function
              
              Public Function EndTimer(Optional bFromStart As Boolean = False) As String
                  Dim EndTime As Long
                  If bFromStart Then
                      EndTime = (GetTickCount - lngStart)
                  Else
                      EndTime = (GetTickCount - lngLastEvent)
                  End If
                  
                  
                  Dim s As Integer
                  s = EndTime / 1000
                  Dim ms As Integer
                  ms = EndTime Mod 1000
                  If s > 1 Then
                      EndTimer = s & "s, " & ms & "ms"
                  Else
                      EndTimer = ms & "ms"
                  End If
              
              End Function
              
              
              Private Function maxLen(myArray() As String, intCol As Integer)
                  Dim lngMax As Long
                  Dim intI As Integer
                  For intI = 1 To UBound(myArray, 2)
                      If Len(myArray(intCol, intI)) > lngMax Then
                          lngMax = Len(myArray(intCol, intI))
                      End If
                  Next
                  maxLen = lngMax
              End Function
              Private Function addSpaces(strInput As String, lngLength As Long, Optional bAfter As Boolean = True) As String
                  'Add spaces
                  If bAfter Then
                      Do While Len(strInput) < lngLength
                          strInput = strInput & " "
                      Loop
                  Else
                      Do While Len(strInput) < lngLength
                          strInput = " " & strInput
                      Loop
                  End If
                  addSpaces = strInput
              End Function
              
              
              
              ' ***********  Example usage  ****************
              
              
              'Public Sub testSW()
              '    Dim mySW As New TSCWatch
              '
              '    mySW.StartTimer
              '
              '    'Run code here
              '
              '    mySW.addEvent "Code 1 finished"
              '
              '
              '    'Run more code here
              '
              '    mySW.addEvent "Code 2 finished"
              '
              '    'Run code here
              '
              '    mySW.addEvent "Total Time", True
              '
              '    Debug.Print mySW.toString
              '
              '    'Cleanup
              '        Set mySW = Nothing
              'End Sub

              Example output:
              Code:
              Timer Started  :       0ms
              First Code bit :     657ms
              2nd code bit   :  2s, 31ms
              Total Time     : 3s, 688ms

              Comment

              • Knut Ole
                New Member
                • Mar 2011
                • 70

                #8
                thanks a lot, smiley..
                what takes time, apparently, is the clearing of 67 subforms (line 69), and drawing arrows (line 103).

                i had the subforms opened/closed for each, while i have now made it so they open all on first use, then not open/closed again before all are closed at end. this saved me approx. 2-3 seconds in average.

                im still at 5 seconds average (4.6 - 6.9, mostly at 4.6). however, there are few drawings on the calendar atm, compared to a full one - which could set me back many seconds. in other words, still very short of acceptable levels.

                rewriting the whole thing to one subform is a formidable job. can anyone confirm this would give substantial gains? anyone with more concrete information as to exactly which lines are responsible for the time consumed?

                the code is in a "module," by the way. any reason it should be in a "class module" or embedded in the calendar form instead?

                thank you so much again!

                Comment

                • TheSmileyCoder
                  Recognized Expert Moderator Top Contributor
                  • Dec 2009
                  • 2322

                  #9
                  Hi Knut

                  I dont think you would gain anything noticeable by placing the code in the calender form. Class modules are something else entirely. (My listed watch for instance is a class).

                  Im sorry to tell you, but if you have used the listed approaches by ADezii and the DoCmd.Echo False I mentioned, then I dont think there is much more to be gained, since both deleting and creating controls takes some time.

                  Why have you choosen the approach of 67 subforms? What reasoning is behind the choice?

                  Comment

                  • Knut Ole
                    New Member
                    • Mar 2011
                    • 70

                    #10
                    ok, thanks smiley.
                    the reason for subforms is first of all it seems orderly. it makes for no relative position information for the drawings, as the subform position on the mainform takes care of that.

                    another reason, discovered later, is that it's not easy to draw things perfectly in access - the pixels/cm/twips are not perfectly aligned, so i get some drawings being one pixel off. (seems insignificant, but creates some major off-positions in calendar).

                    so, im not looking forward to making all drawing positions completely relative to the whole thing - though if anyone has a clear-cut way of approaching this, i'd be very glad for some input. im thinking i'd have to create some off-set position constants to use with each drawing? im just afraid i'll never get it accurate due to the problems of twips/pixels mentioned above.

                    thank you so much so far,
                    more input on efficiency greatly appreciated (tho i might be reaching a roof here)

                    Comment

                    • Knut Ole
                      New Member
                      • Mar 2011
                      • 70

                      #11
                      for the record:

                      using only one subForm in which all arrows are drawn greatly reduces the drawing time. apparently it's the opening and closing of forms that take time.

                      thanks all,

                      Comment

                      • TheSmileyCoder
                        Recognized Expert Moderator Top Contributor
                        • Dec 2009
                        • 2322

                        #12
                        Im glad that you got it sorted.

                        Out of curiosity, how much of an efficiency increase did it give? How long time does your form take to draw now?

                        Comment

                        • Knut Ole
                          New Member
                          • Mar 2011
                          • 70

                          #13
                          i went immediately down to 1.1 s. with one subform.

                          having added some more draw objects (hor/vert lines etc.), im currently back up at around 2.3-3.4 secs. (it's funny, btw, how the drawing time varies by 50% with the exact same job...?)

                          thanks a lot for your help!

                          Comment

                          • TheSmileyCoder
                            Recognized Expert Moderator Top Contributor
                            • Dec 2009
                            • 2322

                            #14
                            Just to make sure, you did use the tip on turning off redrawing (DoCmd.Echo False), until the update is finished?

                            Especially when drawing you should see a good performance boost on that.

                            Comment

                            • Knut Ole
                              New Member
                              • Mar 2011
                              • 70

                              #15
                              i did, but frankly, with or without echos i get exactly the same results... (intermittently 2.3s and 3.4s...)

                              perhaps it's because i disconnect the subform and the drawings are done in the background in the first place, and when reconnecting subform to mainform, all the new changes are there already... so the drawing process is not visible to the user in the first place.

                              last question, btw, you know the right code for refreshing/requiering and especially for the code for the "refresh all" button, so that all tables, queries are refreshed before my code runs? it's not really updated every time i make a change in my user-end inferface, and i have to sometimes manually "refresh all..."

                              thanks again!

                              Comment

                              Working...