簡體   English   中英

Excel VBA 與 Javascript 迭代大型數據集的性能

[英]Excel VBA vs Javascript Performance for Iterating Large Datasets

我目前正在做一個項目,該項目涉及將大型數據集過濾到可以繪制在圖表上的可管理數量的數據點。

我在 Javascript 中編寫了以下代碼,它遍歷一系列數據並以給定的步長選取第一個值,在本例中,起始值 = 0,步長 = 0.1。 這很好用並且執行得非常快; 我還沒有量化它,但對於 >10000 個數據點來說肯定 <1 秒。

var data = [ ... ];

var filteredData = [];
var index = 0;
var step = 0.1;

for (var i=0; i < data.length; i++) {
  if(data[i] >= index) {
    filteredData.push(data[i]);
    index+=step;
  }
}

帶有迷你樣本數據集的 Javascript Codepen

然而,我們所有的數據都是作為 Excel 工作簿輸入的,所以我使用 VBA 將代碼重寫為 Excel 宏,如下所示,將數據點輸出到相鄰的列。 與等效的 JS 相比,處理相同數量的數據點需要很長時間,對於 10000 個數據點大約需要 20 秒。

Dim dataRange As Range
Set dataRange = Range(Range("A8"), Range("A8").End(xlDown))
Dim index As Double
Dim stepsize As Double
Dim outputRow As Integer
index = 0
step = 0.1
outputRow = 8

For Each cell In dataRange
    If cell.Value >= index Then
        ActiveSheet.Cells(outputRow, 2).Value = cell.Value
        index = index + stepsize
        outputRow = outputRow + 1
    End If
Next cell

為什么這兩種方法之間會有如此巨大的差異? 我的 VB 代碼有什么明顯低效的地方嗎? 我希望這個問題不是太模糊!

非常感謝,亞當

看看你的代碼的 Array 實現,它幾乎和 JS 一樣快。

10,000 個數據點需要(至少在我的機器上)幾分之一秒。

Sub test()

    Dim dataRange As Range
    Set dataRange = Range(Range("A8"), Range("A8").End(xlDown))
    Dim index As Double
    Dim stepsize As Double
    Dim outputRow As Long

    index = 0
    step = 0.1
    outputRow = 8


    '/ Array implementation in VBA
    '/ Its almost at the same speed.
    '----------------------------------------------------
    Dim lctr        As Long
    Dim oRow        As Long
    Dim arrOut()
    Dim arr
    arr = dataRange

    For lctr = LBound(arr) To UBound(arr)
        If arr(lctr, 1) >= index Then
            index = index + stepsize
            oRow = oRow + 1
            ReDim Preserve arrOut(1 To 1, 1 To oRow)
            arrOut(1, oRow) = arr(lctr, 1)
        End If
    Next

    arrOut = Application.Transpose(arrOut)

    ActiveSheet.Cells(8, 2).Resize(UBound(arrOut)) = arrOut

    '------------------------------------------------------------



'    For Each cell In dataRange
'        If cell.Value >= index Then
'            ActiveSheet.Cells(outputRow, 2).Value = cell.Value
'            index = index + stepsize
'            outputRow = outputRow + 1
'        End If
'Next cell

End Sub

我從 cyobashu 中獲得靈感,但通過避免重復調用Redim PreserveTranspose方法來提高性能。

當我對 1m 行運行 cyboashu 的代碼時,大約需要 16 秒。 當我對 1m 行運行下面的代碼時,大約需要 1 秒。

我還修復了我認為的錯字step = 0.1應該是stepsize = 0.1

Sub test()
    Dim dataRange As Range
    Set dataRange = Range(Range("A8"), Range("A8").End(xlDown))
    Dim index As Double
    Dim stepsize As Double
    Dim outputRow As Long

    index = 0
    stepsize = 0.1
    outputRow = 8


    '/ Array implementation in VBA
    '/ Its almost at the same speed.
    '----------------------------------------------------
    Dim lctr        As Long
    Dim oRow        As Long
    Dim arrOut()
    Dim arr
    arr = dataRange

    ReDim arrOut(LBound(arr) To UBound(arr), LBound(arr, 2) To UBound(arr, 2)) As Variant

    For lctr = LBound(arr) To UBound(arr)
        If arr(lctr, 1) >= index Then
            index = index + stepsize
            oRow = oRow + 1
            arrOut(oRow, 1) = arr(lctr, 1)
        End If
    Next

    ActiveSheet.Cells(8, 2).Resize(oRow) = arrOut

End Sub

許多性能問題來自錯誤的訪問方法。 在VBA vs JS的RAW性能方面,如果我們做同樣的2個將數據映射到數組的代碼示例:

Const max As Long = 50000000
Sub test()
    Dim i As Long, arr(1 To max) As Long
    DEV.Timer_Start
    For i = 1 To max
         arr(i) = i
    Next
    DEV.Timer_Stop
End Sub

對比

cons max = 50000000
var arr=[]
x=performance.now()
for(var i=0;i<max;i++) arr.push(i)
console.log((performance.now()-x)/1000)

次數:

VBA Average time: 1.01371899999867 seconds
JS  Average time: 1.81799999999930 seconds

因此,就原始計算時間而言,JS 在這里似乎比 VBA 慢,但這主要是因為這兩個操作在內部完全不同。

VBA 定義了一個 200000000 字節長的內存塊,然后填充每個字節。 JavaScript 的執行更加動態,並逐漸擴大其內存占用,如下所示:

Const max As Long = 50000000
Sub test()
    Dim i As Long, arr As Collection
    Set arr = New Collection

    DEV.Timer_Start
    For i = 1 To max
         Call arr.Add(i)
    Next
    DEV.Timer_Stop
End Sub

這會大大減慢 VBA 代碼的速度。

在 500,000 行測試中,我得到以下結果:

VBA fixed size long array:      0.00933219999933 seconds
VBA fixed size variant array:   0.01075579999815 seconds
JS dynamic size variant array:  0.03299999999953 seconds
VBA Collection:                 0.10702589999709 seconds
VBA dynamic size long array:    0.60271329999886 seconds
VBA stdArray:                   7.95831580000231 seconds
VBA dynamic size variant array: 8.36757760000182 seconds

PS stdArray 是我創建的 JS 數組的面向對象替代品 顯然,我自己的庫的性能有很多改進!

編輯

我現在意識到 stdArray 令人難以置信的緩慢得分的原因與內部使用的變量類型直接相關。 在這里,我對一個變體數組進行了循環,結果循環花費了 8.368 秒。

Sub test5()
    Dim i As Long, arr() As Variant
    ReDim arr(1 To 1) As Variant

    DEV.Timer_Start
    For i = 1 To max
         ReDim Preserve arr(1 To UBound(arr) + 1) As Variant
         arr(i) = i
    Next
    DEV.Timer_Stop
End Sub

這需要8.368秒才能運行。 這確實表明在創建數組時選擇正確的變量類型是多么重要。 在 VBA 中使用變體數據類型時,一切都非常慢。

編輯

包含測試用例和性能細節的鏈接 GIST: https : //gist.github.com/sancarn/1f92164f1b53fcd940640f680a06b426

暫無
暫無

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

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