2012-08-31 5 views
10

내 프로젝트에는 서로 다른 개체에 대해 동적으로 크기 조정할 수있는 배열이 필요합니다. 배열은 단일 클래스의 객체 수를 제한없이 포함 할 수 있지만 여러 클래스의 객체는 포함 할 수 없습니다.VBA : 변형 배열 대 반복 배열의 형식화 된 배열 대 키가없는 배열

주로 배열을 반복하므로 열쇠가있는 컬렉션을 사용하는 것이 이상적이지 않습니다. 두 가지 옵션이 있다고 생각합니다 :

첫 번째 옵션은 각 개체 유형에 대해 '목록'클래스를 개발하고 개체 추가 및 배열 확장 방법, 첫 번째 및 마지막 인덱스 및 개체 수 가져 오기, 인덱스로 객체를 검색하는 것입니다 (후자의 4는 배열이 비어있는 경우 오류 처리를 포함합니다).

두 번째 옵션은 Variant 데이터 형식을 사용하여 동일한 방법으로 단일 'List'클래스를 개발하는 것입니다. 분명히 이것은 훨씬 적은 작업이지만 속도에 대해 우려하고 있습니다. 타입이 지정된 객체보다 변형을 사용하는 것이 얼마나 느린가요?

Dim myObject As MyClass 
Set myObject = variantList.Get(i) 
가 속도를 향상 캐스팅 하는가, 또는 VBA를 아직 모든 유형 검사와 관련된 수행 할 필요가 않습니다 항상 직접 라 검색시 입력 변수에 배열의 변형 개체를 캐스팅 할 않습니다 변종?

또한이 두 번째 옵션은 키가 아닌 컬렉션을 사용하는 것보다 빠르지 않습니까? 나는 Collection iteration이 느리다는 것을 알기 때문에 lookup을 위해 고안되었다. 키가 아닌 컬렉션 또는 키 값 매핑 된 컬렉션에만 적용됩니까?

조언을 제공 할 수있는 사람 덕분입니다.

+0

얼마나 자주 크기를 조정합니까? VBA는 이것이 자주 발생하지 않거나 새 배열의 크기를 알 수 있고 배열 (콜렉션이 아닌)을 계속 사용할 수있게하면 'redim preserve'를 통해 동적 배열 크기 조정을 허용합니다. – enderland

+1

차이가 중요한지 여부는 정확한 사용 사례에 따라 다를 수 있습니다. 그래도 테스트하는 것이 매우 쉬울 것 같습니다 : 어떤 비교를 해 보았습니까? –

답변

14

나는 Tim Williams의 충고를 따르고 몇 가지 속도 테스트를 수행했습니다.

컬렉션/배열의 각 유형에 대해 먼저 클래스 "SpeedTester"의 객체를 추가했습니다.이 객체는 get/set 속성을 사용하여 긴 변수가있는 쉘 객체였습니다. 변수의 값은 루프 인덱스 (1에서 100,000 사이)의 값입니다.

그런 다음 컬렉션/배열의 각 개체에 액세스하고 개체의 긴 속성 값을 새 변수에 할당하는 두 번째 루프를 수행했습니다. long 타입. 나는 방법 당 3 라운드를 수행하고, And와 get 루프의 시간을 평균했다.

결과는 다음과 같다 :

Method      Avg Add Time Avg Get Time Total Time 
Collection Indexed    0.305   25.498   25.803 
Collection Mapped    1.021   0.320   1.342 
Collection Indexed For Each 0.334   0.033   0.367 
Collection Mapped For Each  1.084   0.039   1.123 
Dynamic Array Typed   0.303   0.039   0.342 
Static Array Typed    0.251   0.016   0.266 

방법 컬렉션 인덱스 및 매핑 컬렉션 컬렉션의 개체를 유지하고있었습니다. 첫 번째 키에 키가 추가되지 않았고 두 번째 키에 객체의 긴 속성이 문자열로 변환 된 키가 추가되었습니다. 그런 다음 이러한 개체는 1에서 c.Count의 인덱스를 사용하여 for 루프에서 액세스되었습니다 .Count

다음 두 메서드는 변수가 컬렉션에 추가되는 방식에서 처음 두 메서드와 동일합니다. 그러나 Get 루프의 경우 for 루프를 인덱스와 함께 사용하는 대신 for-each 루프를 사용했습니다.

동적 배열 형식은 SpeedTester 형식의 배열을 포함하는 사용자 지정 클래스입니다. 변수가 추가 될 때마다 배열의 크기가 1 슬롯만큼 확장되었습니다 (ReDim Preserve 사용). get-loop는 일반적으로 배열 에서처럼 1에서 100,000까지의 인덱스를 사용하는 for-loop였습니다.

마지막으로 정적 배열 형식은 단순히 SpeedTester 유형의 배열이며 100,000 개의 슬롯으로 초기화되었습니다. 분명히 이것은 가장 빠른 방법입니다. 이상하게도 속도 향상의 상당 부분은 추가가 아닌 얻기에있었습니다.각 개체를 가져 오는 것이 동적 배열보다 빠르지 만 크기를 조정할 필요가 있기 때문에 추가하는 것이 다른 메서드에서는 더 느릴 것이라고 생각했을 것입니다.

인덱스 된 컬렉션의 개체에 액세스하기 위해 for 루프와 for 루프를 사용하는 것의 차이점에 놀랐습니다. 매핑 된 컬렉션의 주요 조회 속도가 놀랍습니다. 인덱싱보다 훨씬 빠르고 정적 배열을 제외한 다른 모든 메서드와 비슷합니다.

간단히 말해서, 그것들은 내 프로젝트에 대한 모든 실행 가능한 대안입니다 (첫 번째와 마지막 방법을 제외하고, 처음에는 속도가 느리기 때문에 마지막으로 동적으로 크기 조정 가능한 배열이 필요하기 때문입니다). 컬렉션이 실제로 구현되는 방법이나 동적 배열과 정적 배열 간의 구현 차이에 대해서는 전혀 알지 못합니다. 더 이상의 통찰력을 많이 주시면 감사하겠습니다.

EDIT : (동적 배열을 사용하여) 시험 자체 코드

Public Sub TestSpeed() 
    Dim ts As Double 
    ts = Timer() 

    Dim c As TesterList 
    Set c = New TesterList 

    Dim aTester As SpeedTester 

    Dim i As Long 
    For i = 1 To 100000 
     Set aTester = New SpeedTester 
     aTester.Number = i 

     Call c.Add(aTester) 
    Next i 

    Dim taa As Double 
    taa = Timer() 

    For i = c.FirstIndex To c.LastIndex 
     Set aTester = c.Item(i) 

     Dim n As Long 
     n = aTester.Number 
    Next i 

    Dim tag As Double 
    tag = Timer() 

    MsgBox "Time to add: " & (taa - ts) & vbNewLine & "Time to get: " & (tag - taa) 
End Sub 

그리고 동적 배열 클래스 TesterList위한

:

Private fTesters() As SpeedTester 

Public Property Get FirstIndex() As Long 
    On Error GoTo Leave 

    FirstIndex = LBound(fTesters) 

Leave: 
    On Error GoTo 0 
End Property 

Public Property Get LastIndex() As Long 
    On Error GoTo Leave 

    LastIndex = UBound(fTesters) 

Leave: 
    On Error GoTo 0 
End Property 

Public Sub Add(pTester As SpeedTester) 
    On Error Resume Next 

    ReDim Preserve fTesters(1 To UBound(fTesters) + 1) As SpeedTester 
    If Err.Number <> 0 Then 
     ReDim fTesters(1 To 1) As SpeedTester 
    End If 

    Set fTesters(UBound(fTesters)) = pTester 

    On Error GoTo 0 
End Sub 

Public Function Item(i As Long) As SpeedTester 
    On Error GoTo Leave 

    Set Item = fTesters(i) 

Leave: 
    On Error GoTo 0 
End Function 

그리고 마지막으로, 매우 간단한 SpeedTester 객체 클래스 :

Private fNumber As Long 

Public Property Get Number() As Long 
    Number = fNumber 
End Property 

Public Property Let Number(pNumber As Long) 
    fNumber = pNumber 
End Property 
+0

이 코드를 게시 할 수 있습니까? 정적 배열 크기 대 동적 배열 크기 조정이 각각의 반복 크기를 줄이는 데 얼마나 적은 손실이 있는지 정말 놀랍습니다. 나는 이것을 하나의'long' 데이터 유형보다 상당히 큰 객체 유형으로 시도 할 수 있습니다. – enderland

+0

+1 다른 접근 방식에 대한 철저한 테스트를 할 시간이 필요합니다. 수천 개의 객체로 작업 할 것이라는 인상을 받았기 때문에 모든 메소드 (색인 된 콜렉션 제외)가 프로젝트에서 괜찮은 것 같습니다. –

+0

Tim, 네, 제 프로젝트는 수천 개의 객체 만 필요합니다. 그러나 철저히 생각하는 것이 가장 좋았습니다. – Swiftslide