[英]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;
}
}
然而,我們所有的數據都是作為 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 Preserve
和Transpose
方法來提高性能。
當我對 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.