簡體   English   中英

與訪問用戶定義類型的字段相比,VBA類模塊屬性是否較慢?

[英]Are VBA Class module properties slow compared to accessing fields of User Defined Types?

我一直在嘗試使用類模塊開發一個宏,但是與UDT相比,get / let似乎需要很長時間。 我真的很感興趣為什么會這樣,有人可以解釋一下嗎? 我只發現了討論功能/子執行的討論,這似乎同樣快。

目前的問題是設置一個屬性,該類需要大約3000毫秒(200萬次)和120毫秒使用UDT進行相同操作。

我正在嘗試決定是否應該建議宏開發人員在需要獲取或設置大量屬性時避免使用類模塊。 只使用這個作為我應該的數據,但也許你有不同的見解。

我想明白為什么這么慢。 也許我只是做錯了什么。

示例代碼:

Public Type Participant
    Name As String
    Gender As Integer
End Type

Public Declare Function GetTickCount Lib "kernel32.dll" () As Long

Sub TimeUDT()
   Dim i As Long
   Dim startMs As Long
   startMs = GetTickCount
   Dim participants(1 To 1000000) As Participant
   For i = 1 To 1000000
      participants(i).Name = "TestName"
      participants(i).Gender = 1
   Next
   Debug.Print GetTickCount - startMs
End Sub

Sub TimeCls()
   Dim i As Long
   Dim startMs As Long
   Dim participants(1 To 1000000) As New clsParticipant
   startMs = GetTickCount
   For i = 1 To 1000000
      participants(i).Name = "TestName"
      participants(i).Gender = 1
   Next
   Debug.Print GetTickCount - startMs
End Sub

和類模塊(名為clsParticipant):

Private iGender As Integer
Private sName As String

Public Property Let Gender(value As Integer)
   iGender = value
End Property
Public Property Get Gender() As Integer
   Gender = iGender
End Property

Public Property Get Name() As String
   Name = sName
End Property
Public Property Let Name(value As String)
   sName = value
End Property

首先,我強烈建議使用高分辨率計時器,這樣您就不必測試盡可能多的迭代次數。 請參閱使用QueryPerformanceCounter CTimer

這是我的機器上的基線,10K迭代,高精度計時器

Sub TimeUDT()
   Dim i As Long
   Dim timer As New CTimer
   timer.StartCounter
   Dim participants(1 To 10000) As Participant
   For i = 1 To 10000
      participants(i).Name = "TestName"
      participants(i).Gender = 1
   Next
   Debug.Print "Elapsed time: " & timer.TimeElapsed & " ms"
End Sub

Elapsed time: 1.14359022404999 ms

不管你信不信,你實際上在你的循環中獲得對象創建的命中。 在啟動計時器之前,在循環中顯式創建它們並查看差異:

之前

Sub TimeCls()
   Dim i As Long
   Dim timer As New CTimer
   Dim participants(1 To 10000) As New clsParticipant

   timer.StartCounter
   For i = 1 To 10000
      participants(i).Name = "TestName"
      participants(i).Gender = 1
   Next
   Debug.Print "Elapsed time: " & timer.TimeElapsed & " ms"
End Sub

Elapsed time: 24.9600996727434 ms

Sub TimeCls()
   Dim i As Long
   Dim timer As New CTimer
   'Dim participants(1 To 10000) As New clsParticipant
   Dim participants(1 To 10000) As clsParticipant
   For i = 1 To 10000
       Set participants(i) = New clsParticipant
   Next i

   timer.StartCounter
   For i = 1 To 10000
      participants(i).Name = "TestName"
      participants(i).Gender = 1
   Next
   Debug.Print "Elapsed time: " & timer.TimeElapsed & " ms"
End Sub

Elapsed time: 4.66722880515984 ms

這僅比基線慢4倍(在現在從測量中排除對象創建命中之后)。 如果你進一步聲明你的iGendersName公開的並且直接改變它們,那么性能甚至更接近基線,所以其余大部分性能命中來自Let間接。

Sub TimeCls()
   Dim i As Long
   Dim timer As New CTimer
   Dim participants(1 To 10000) As clsParticipant
   For i = 1 To 10000
       Set participants(i) = New clsParticipant
   Next i

   timer.StartCounter
   For i = 1 To 10000
      'participants(i).Name = "TestName"
      'participants(i).Gender = 1
      participants(i).sName = "TestName"
      participants(i).iGender = 1
   Next
   Debug.Print "Elapsed time: " & timer.TimeElapsed & " ms"
End Sub

Elapsed time: 1.71887815565976 ms

他的答案中的Webb正確地強調了這樣一個事實:在您的測試中,您沒有考慮創建對象所需的時間,而是忘記添加對象的破壞。

下面代碼中的時間考慮了創建和銷毀時間,並且表明沒有任何東西是免費的,也就是說你不管怎么做,最后總時間大致相同。

我還添加了最后3個以三種不同方式顯式銷毀對象的函數,而不是將它留給垃圾收集器,時間保持不變。

我添加了這三個測試,因為我期待看到巨大的變化。 我記得很久以前我做過的測試,垃圾收集器只需要改變破壞順序10或100倍。 我無法在這里重現問題,可能是因為沒有那么多的對象或因為對象更簡單。 但是我添加了代碼只是為了顯示你應該做的另一個測試,如果你發現破壞所需的時間突然增加了。

這是我的代碼:

Sub Test()
  Dim T0 As Single

  T0 = timer
  TimeCls_a
  Debug.Print , timer - T0

  T0 = timer
  TimeCls_b
  Debug.Print , timer - T0

  T0 = timer
  TimeCls_c
  Debug.Print , timer - T0

  T0 = timer
  TimeCls_c_up
  Debug.Print , timer - T0

  T0 = timer
  TimeCls_c_dn
  Debug.Print , timer - T0

  T0 = timer
  TimeCls_c_all
  Debug.Print , timer - T0
End Sub

Sub TimeCls_a()
   Dim i As Long
   Dim T0 As Single
   Dim participants(1 To NCYCLES) As New clsParticipant

   T0 = timer
   For i = 1 To NCYCLES
      participants(i).Name = "TestName"
      participants(i).Gender = 1
   Next
   Debug.Print "TimeCls_a:", timer - T0
End Sub

Sub TimeCls_b()
   Dim i As Long
   Dim T0 As Single
   Dim participants(1 To NCYCLES) As clsParticipant
   For i = 1 To NCYCLES
       Set participants(i) = New clsParticipant
   Next i

   T0 = timer
   For i = 1 To NCYCLES
      participants(i).Name = "TestName"
      participants(i).Gender = 1
   Next
   Debug.Print "TimeCls_b:", timer - T0
End Sub

Sub TimeCls_c()
   Dim i As Long
   Dim T0 As Single
   Dim participants(1 To NCYCLES) As clsParticipant
   For i = 1 To NCYCLES
       Set participants(i) = New clsParticipant
   Next i

   T0 = timer
   For i = 1 To NCYCLES
      participants(i).Name = "TestName"
      participants(i).Gender = 1
   Next
   Debug.Print "TimeCls_c:", timer - T0
End Sub

Sub TimeCls_c_up()
   Dim i As Long
   Dim T0 As Single
   Dim participants(1 To NCYCLES) As clsParticipant
   For i = 1 To NCYCLES
       Set participants(i) = New clsParticipant
   Next i

   T0 = timer
   For i = 1 To NCYCLES
      participants(i).Name = "TestName"
      participants(i).Gender = 1
   Next

   For i = 1 To NCYCLES
       Set participants(i) = Nothing
   Next i
   Debug.Print "TimeCls_c_up:", timer - T0
End Sub

Sub TimeCls_c_dn()
   Dim i As Long
   Dim T0 As Single
   Dim participants(1 To NCYCLES) As clsParticipant
   For i = 1 To NCYCLES
       Set participants(i) = New clsParticipant
   Next i

   T0 = timer
   For i = 1 To NCYCLES
      participants(i).Name = "TestName"
      participants(i).Gender = 1
   Next

   For i = NCYCLES To 1 Step -1
       Set participants(i) = Nothing
   Next i
   Debug.Print "TimeCls_c_dn:", timer - T0
End Sub

Sub TimeCls_c_al()
   Dim i As Long
   Dim T0 As Single
   Dim participants() As clsParticipant
   ReDim participants(1 To NCYCLES)
   For i = 1 To NCYCLES
       Set participants(i) = New clsParticipant
   Next i

   T0 = timer
   For i = 1 To NCYCLES
      participants(i).Name = "TestName"
      participants(i).Gender = 1
   Next

   ReDim participants(1 To NCYCLES)
   Debug.Print "TimeCls_c_al:", timer - T0
End Sub

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM