与访问用户定义types的字段相比,VBA类模块属性是否缓慢?

我一直在试图用类模块来开发macros,但是与UDT相比,get / let似乎需要很长时间。 我真的对这是为什么感兴趣,任何人都可以解释这一点? 我只find讨论function/子执行,这似乎是一样快。

目前的问题是设置一个属性,这需要大约3000毫秒的类(200万)和120毫秒使用UDT做同样的事情。

我试图决定是否应该build议macros开发人员在需要获取或设置很多属性时避免使用类模块。 只使用这个数据我应该,但也许你有不同的见解。

我想明白为什么这么慢。 也许我只是做错了什么。

示例代码:

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 

首先,我强烈build议使用高分辨率计时器,这样您就不必testing更多的迭代。 使用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 

现在,不pipe你是否真的在你的循环中创build了对象创build的命中。 在启动计时器之前,在循环中明确地创build它们,并查看其差异:

之前

 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倍(现在从测量中排除了对象创build命中之后)。 如果你进一步声明你的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在他的回答中正确地强调了这样的事实:在你的testing中,你没有考虑创build对象所需的时间,但是忘记添加对象的销毁。

下面代码中的时间既考虑了创build时间,也考虑了销毁时间,并且表明没有任何东西是免费的,也就是说你怎么做都没有关系,最后总的时间也差不多。

我还添加了最后3个函数,它们以三种不同的方式显式销毁对象,而不是将其留给垃圾回收器,时间保持不变。

我添加了这三个testing,因为我期待看到巨大的变化。 我记得我之前做过的一个testing,垃圾收集器需要花10到100倍的时间才能改变破坏的顺序。 这里我不能重现这个问题,也许是因为没有那么多的对象,或者因为对象比较简单。 但是,如果您注意到销毁所需的时间突然增加,则我添加了代码以显示另一个testing。

这是我的代码:

 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