2012-08-31 26 views
10

Dự án của tôi yêu cầu một loạt mảng có thể thay đổi kích thước động cho các đối tượng khác nhau. Một mảng có thể chứa bất kỳ số lượng các đối tượng, có khả năng hàng ngàn, của một lớp duy nhất, nhưng không phải là đối tượng của nhiều lớp.VBA: Tốc độ lặp của mảng biến thể so với mảng đã nhập so với bộ sưu tập không có khóa

Chủ yếu tôi sẽ lặp qua mảng, do đó việc sử dụng bộ sưu tập có khóa không phải là lý tưởng. Tôi nghĩ rằng tôi có hai lựa chọn:

Tùy chọn đầu tiên là phát triển một lớp 'Danh sách' cho từng loại đối tượng, với phương pháp thêm đối tượng (và mở rộng mảng), nhận chỉ mục Đầu tiên và Cuối cùng và đếm đối tượng, và lấy một đối tượng theo chỉ mục (sau 4 sẽ bao gồm xử lý lỗi trong trường hợp mảng trống).

Tùy chọn thứ hai là phát triển một lớp 'Danh sách' duy nhất, với cùng phương pháp, sử dụng loại dữ liệu Biến thể. Rõ ràng đây là công việc ít hơn nhiều, nhưng tôi lo ngại về tốc độ. Sử dụng các biến thể nhiều hơn các đối tượng đã nhập chậm hơn bao nhiêu? Lưu ý rằng tôi sẽ luôn luôn được đúc các đối tượng biến trong mảng trực tiếp đến một biến gõ khi thu hồi, a la:

Dim myObject As MyClass 
Set myObject = variantList.Get(i) 

Liệu đúc cải thiện tốc độ, hoặc không VBA vẫn phải thực hiện tất cả các loại kiểm tra liên quan đến biến thể?

Ngoài ra, tùy chọn thứ hai này có nhanh hơn sử dụng Bộ sưu tập không có khóa không? Tôi đã đọc rằng Bộ sưu tập lặp lại chậm, rằng chúng được thiết kế để tra cứu. Điều này có áp dụng cho các bộ sưu tập không có khóa hay chỉ các bộ sưu tập có khóa-giá trị được ánh xạ không?

Nhờ bất kỳ ai có thể đưa ra lời khuyên.

+0

tần suất bạn thay đổi kích thước chúng? VBA cho phép thay đổi kích thước mảng động thông qua 'redim preserve' nếu điều này không xảy ra thường xuyên và/hoặc bạn biết kích thước của mảng mới và sẽ cho phép bạn tiếp tục sử dụng mảng (thay vì tập hợp). – enderland

+1

Có hay không có sự khác biệt nào đáng kể phụ thuộc vào trường hợp sử dụng chính xác của bạn. Dường như nó sẽ rất dễ dàng để thử nghiệm mặc dù: bạn có chạy bất kỳ so sánh? –

Trả lời

14

Tôi đã làm theo lời khuyên của Tim Williams và đã thực hiện một số kiểm tra tốc độ.

Đối với mỗi loại bộ sưu tập/mảng, lần đầu tiên tôi thêm 100.000 đối tượng của lớp "SpeedTester", đơn giản chỉ là đối tượng vỏ chứa một biến dài (với thuộc tính get/set). Giá trị của biến là giá trị của chỉ số vòng lặp (từ 1 đến 100.000)

Sau đó, tôi đã thực hiện vòng lặp thứ hai liên quan đến từng đối tượng trong bộ sưu tập/mảng và gán giá trị thuộc tính dài của đối tượng cho biến mới loại dài. Tôi thực hiện 3 vòng cho mỗi phương pháp, và tính trung bình thời gian cho And và nhận được vòng lặp.

Kết quả như sau:

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 

Các phương pháp sưu tập được lập chỉ mục và Bộ sưu tập ánh xạ tham gia giữ các đối tượng trong một bộ sưu tập. Đầu tiên được thêm vào không có khóa, phần thứ hai được thêm vào với một khóa là thuộc tính dài của đối tượng được chuyển đổi thành một chuỗi. Các đối tượng này sau đó được truy cập trong một vòng lặp bằng cách sử dụng một chỉ mục từ 1 đến c.Count

Hai phương thức tiếp theo giống hệt với hai phương thức đầu tiên được thêm vào bộ sưu tập. Tuy nhiên, đối với vòng lặp Get, thay vì sử dụng vòng lặp for có chỉ mục, tôi đã sử dụng vòng lặp for-each.

Nhập mảng động là một lớp tùy chỉnh có chứa một mảng kiểu SpeedTester. Mỗi lần một biến được thêm vào, kích thước của mảng được mở rộng thêm 1 khe (sử dụng ReDim Preserve). Vòng lặp get là một for-loop sử dụng một chỉ mục từ 1 đến 100.000, như là điển hình cho một mảng.

Cuối cùng, mảng tĩnh được nhập chỉ đơn giản là một mảng kiểu SpeedTester, được khởi tạo với 100.000 vị trí. Rõ ràng đây là phương pháp nhanh nhất. Thật kỳ lạ, phần lớn tốc độ tăng của nó là ở Bắt hơn là Thêm.Tôi đã giả định rằng việc thêm sẽ chậm hơn đối với các phương thức khác, do nhu cầu thay đổi kích thước, trong khi việc Bắt từng đối tượng sẽ không nhanh hơn một mảng động.

Tôi đã kinh ngạc bởi sự khác biệt giữa việc sử dụng vòng lặp for-loop và for-each để truy cập các đối tượng của bộ sưu tập được lập chỉ mục. Tôi cũng ngạc nhiên bởi tốc độ tra cứu chính của bộ sưu tập được ánh xạ - nhiều, nhanh hơn nhiều so với lập chỉ mục và so sánh với tất cả các phương thức khác ngoại trừ mảng tĩnh.

Tóm lại, chúng là tất cả các lựa chọn thay thế khả thi cho dự án của tôi (trừ phương pháp thứ nhất và cuối cùng, trước hết vì chậm trễ, cuối cùng vì tôi cần mảng có thể thay đổi kích thước động). Tôi biết hoàn toàn không có gì về cách các bộ sưu tập được thực sự thực hiện, hoặc sự khác biệt thực hiện giữa một mảng động và tĩnh. Bất kỳ thông tin chi tiết nào khác sẽ được đánh giá cao.

EDIT: Mã cho thử nghiệm bản thân (bằng cách sử dụng mảng động)

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 

Và đối với những động lớp mảng 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 

Và cuối cùng, các lớp đối tượng SpeedTester rất đơn giản :

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

Bạn có thể đăng mã này không? Tôi thực sự ngạc nhiên làm thế nào ít mất mát có trên một mảng năng động thay đổi kích thước mỗi iteration vs một mảng có kích thước tĩnh! Tôi có thể thử điều này với một kiểu đối tượng lớn hơn đáng kể so với các kiểu dữ liệu 'long' duy nhất. – enderland

+0

+1 dành thời gian để thực hiện kiểm tra kỹ lưỡng các phương pháp khác nhau. Có vẻ như tất cả các phương pháp đó (có thể ngoại trừ bộ sưu tập được lập chỉ mục) sẽ ổn cho dự án của bạn, vì tôi có ấn tượng rằng bạn sẽ làm việc với hàng nghìn đối tượng thấp? –

+0

Tim, vâng, dự án của tôi chỉ cần hàng nghìn đối tượng, tuy nhiên tôi nghĩ tốt nhất là nên triệt để. – Swiftslide