我目前正在开展一个项目,该项目涉及将大型数据集过滤到可以在图表上绘制的可管理数量的数据点。
我在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;
}
}
Codepen of Javascript with Mini Sample Dataset
但是,我们所有的数据都以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代码有什么明显低效的吗?我希望这个问题不要太模糊!
非常感谢,Adam
答案 0 :(得分:4)
请参阅代码的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
答案 1 :(得分:2)
我从cyobashu那里获取灵感,但是通过避免重复调用Redim Preserve
和Trasnpose
方法来获得一些表现。
当我针对1m行运行cyboashu的代码时,大约需要16秒。当我在下面运行1m行代码时,大约需要1秒钟。
我还修复了我认为错误的问题step = 0.1
{@ 1}}
stepsize = 0.1
答案 2 :(得分:0)
许多性能问题来自不良的访问方法。在VBA与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
vs
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
P.S. stdArray is an object oriented replacement for JS array I've created。显然,我自己的库的性能有了很多改进!
编辑
我现在意识到,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