我经常看到与 vba 相关的 Overflow
错误的问题。
我的问题是为什么要使用 integer
变量声明,而不是将所有数字变量(除了 double
等)定义为 long
?
除非你正在执行像 for 循环这样的操作,在这种情况下你可以保证值不会超过 32,767 的限制,否则是否使用 long
会影响性能或其他方面的因素吗?
我经常看到与 vba 相关的 Overflow
错误的问题。
我的问题是为什么要使用 integer
变量声明,而不是将所有数字变量(除了 double
等)定义为 long
?
除非你正在执行像 for 循环这样的操作,在这种情况下你可以保证值不会超过 32,767 的限制,否则是否使用 long
会影响性能或其他方面的因素吗?
BUT 在32位系统上,减半的内存使用量会带来性能成本。当处理器实际使用16位整数进行计算(例如增加循环计数器)时,值将被静默地转换为临时Long,而没有更大范围的数字可供使用。溢出仍然会发生,处理器用于存储计算值的寄存器将以相同的方式占用相同的内存(32位)。性能甚至可能受到损害,因为数据类型必须在非常低的级别上进行转换。
虽然这不是我要找的参考资料,但是...
这是我正在寻找的参考资料。我的理解是,即使声明为整数,底层的VB引擎也会将整数转换为长整数。因此,可能会注意到轻微的速度下降。我已经相信这一点有一段时间了,也许这也是为什么上面的语句被提出的原因,我没有询问原因。
显然,-1不等于65535,但计算机返回了正确的答案,即“FFFF”=“FFFF”。 然而,如果我们首先强制将-1转换为long,我们就会得到正确答案(65535大于32k,自动成为long)。MsgBox Hex(-1) = Hex(65535) ' = True
一般来说,在现代系统中,除了一些需要接收整数的遗留API之外,VBA中声明“As Integer”没有意义。 pcreview论坛 最终我找到了我真正想要的msdn文档。MsgBox Hex(-1&) = Hex(65535) ' = False
"FFFFFFFF" = "FFFF"
根据评论澄清:整数仍然需要更少的内存来存储 - 一个大的整数数组将需要比具有相同维度的Long数组少得多的RAM(几乎正好是一半,您可以在任务管理器中自行检查)。但由于处理器需要使用32位内存块,VBA在执行计算时会临时将整数转换为长整型。
Integer
类型的好理由。除非你需要与期望16位int的旧API调用进行Interop,或者你正在处理大量的小整数数组且内存非常紧张。尽管这篇文章已经四年了,但我对此很感兴趣,所以进行了一些测试。最重要的是,程序员应该始终将变量声明为某个类型。未声明的变量明显表现最差(未声明的变量在技术上是Variant
)。
Long
的表现最快,因此我认为微软建议始终使用 Long
而不是 Integer
是有道理的。 我猜测同样适用于 Byte
,但大多数程序员不使用它。
64位Windows 10笔记本电脑上的结果
使用的代码:
Sub VariableOlymics()
'Run this macro as many times as you'd like, with an activesheet ready for data
'in cells B2 to D6
Dim beginTIME As Double, trials As Long, i As Long, p As Long
trials = 1000000000
p = 0
beginTIME = Now
For i = 1 To trials
Call boomBYTE
Next i
Call Finished(p, Now - beginTIME, CDbl(trials))
p = p + 1
beginTIME = Now
For i = 1 To trials
Call boomINTEGER
Next i
Call Finished(p, Now - beginTIME, CDbl(trials))
p = p + 1
beginTIME = Now
For i = 1 To trials
Call boomLONG
Next i
Call Finished(p, Now - beginTIME, CDbl(trials))
p = p + 1
beginTIME = Now
For i = 1 To trials
Call boomDOUBLE
Next i
Call Finished(p, Now - beginTIME, CDbl(trials))
p = p + 1
beginTIME = Now
For i = 1 To trials
Call boomUNDECLARED
Next i
Call Finished(p, Now - beginTIME, CDbl(trials))
p = p + 1
End Sub
Private Sub boomBYTE()
Dim a As Byte, b As Byte, c As Byte
a = 1
b = 1 + a
c = 1 + b
c = c + 1
End Sub
Private Sub boomINTEGER()
Dim a As Integer, b As Integer, c As Integer
a = 1
b = 1 + a
c = 1 + b
c = c + 1
End Sub
Private Sub boomLONG()
Dim a As Long, b As Long, c As Long
a = 1
b = 1 + a
c = 1 + b
c = c + 1
End Sub
Private Sub boomDOUBLE()
Dim a As Double, b As Double, c As Double
a = 1
b = 1 + a
c = 1 + b
c = c + 1
End Sub
Private Sub boomUNDECLARED()
a = 1
b = 1 + a
c = 1 + b
c = c + 1
End Sub
Private Sub Finished(i As Long, timeUSED As Double, trials As Double)
With Range("B2").Offset(i, 0)
.Value = .Value + trials
.Offset(0, 1).Value = .Offset(0, 1).Value + timeUSED
.Offset(0, 2).FormulaR1C1 = "=ROUND(RC[-1]*3600*24,0)"
End With
End Sub
Dim a as Variant, b as Variant, c As Variant
应该与根本不进行“dimming”(即不声明)的结果相同。 - Vityata正如其他回答所提到的,int和long之间的真正区别在于其内存空间大小,因此它可以容纳的数字大小不同。
这里是有关这些数据类型的完整文档 http://msdn.microsoft.com/en-us/library/office/ms474284(v=office.14).aspx
一个整数是16位,可以表示-32,768到32,767之间的值
长整型为32位,可以表示-2,147,483,648到2,147,483,647之间的值
还有一个长长整型,为64位,可以处理像9百万亿这样大的数字
记住的最重要的事情之一是,数据类型因语言和操作系统/平台而异。在您的VBA世界中,long是32位,但在64位处理器上的c#中,long是64位。这可能会导致显着的混淆。
虽然VBA不支持,但当您转移到任何其他语言中,如.NET或Java等,我更喜欢使用系统数据类型 int16 , int32 和 int64 ,这使我能够更透明地了解这些数据类型可以容纳的值。
VBA有很多历史包袱。
Integer
是16位宽,当16位架构/字长普遍存在时,它是一个很好的默认数字类型。
Long
是32位宽,(在我看来)应该尽可能使用。
使用的代码:
Option Explicit
Sub VariableOlympics()
'Run this macro as many times as you'd like, with an activesheet ready for data
'in cells B2 to D6
Dim beginTIME As Double, trials As Long, i As Long, p As Long
Dim chosenWorksheet As Worksheet
Set chosenWorksheet = ThisWorkbook.Sheets("TimeTrialInfo")
Application.EnableEvents = False
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
trials = 1000000000 ' 1,000,000,000 - not 10,000,000,000 as used by @PGSystemTester
p = 0
beginTIME = Now
boomBYTE trials
Finished p, Now - beginTIME, CDbl(trials), chosenWorksheet.Range("B2")
p = p + 1
beginTIME = Now
boomINTEGER trials
Finished p, Now - beginTIME, CDbl(trials), chosenWorksheet.Range("B2")
p = p + 1
beginTIME = Now
boomLONG trials
Finished p, Now - beginTIME, CDbl(trials), chosenWorksheet.Range("B2")
p = p + 1
beginTIME = Now
boomDOUBLE trials
Finished p, Now - beginTIME, CDbl(trials), chosenWorksheet.Range("B2")
p = p + 1
beginTIME = Now
boomUNDECLARED trials
Finished p, Now - beginTIME, CDbl(trials), chosenWorksheet.Range("B2")
p = p + 1
Application.EnableEvents = True
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
chosenWorksheet.Calculate
End Sub
Private Sub boomBYTE(numTrials As Long)
Dim a As Byte, b As Byte, c As Byte
Dim i As Long
For i = 1 To numTrials
a = 1
b = 1 + a
c = 1 + b
c = c + 1
Next i
End Sub
Private Sub boomINTEGER(numTrials As Long)
Dim a As Integer, b As Integer, c As Integer
Dim i As Long
For i = 1 To numTrials
a = 1
b = 1 + a
c = 1 + b
c = c + 1
Next i
End Sub
Private Sub boomLONG(numTrials As Long)
Dim a As Long, b As Long, c As Long
Dim i As Long
For i = 1 To numTrials
a = 1
b = 1 + a
c = 1 + b
c = c + 1
Next i
End Sub
Private Sub boomDOUBLE(numTrials As Long)
Dim a As Double, b As Double, c As Double
Dim i As Long
For i = 1 To numTrials
a = 1
b = 1 + a
c = 1 + b
c = c + 1
Next i
End Sub
Private Sub boomUNDECLARED(numTrials As Long)
Dim a As Variant, b As Variant, c As Variant
Dim i As Long
For i = 1 To numTrials
a = 1
b = 1 + a
c = 1 + b
c = c + 1
Next i
End Sub
Private Sub Finished(i As Long, timeUSED As Double, trials As Double, initialCell As Range)
With initialCell.Offset(i, 0)
.Value = trials
.Offset(0, 1).Value = timeUSED
.Offset(0, 2).FormulaR1C1 = "=ROUND(RC[-1]*3600*24,2)"
End With
End Sub
感谢 AJD 和 pgSystemTester。我确认了对于整数33和长整型20的AJD结果。然而,长整型要快得多,只是因为您的测试程序足够小,可以完全适合处理器缓存内存。最快的RAM是L1缓存,其中数据和程序各有32kB。对于一个或几个数组,它们几乎适合于32kB的L1数据缓存,测试Byte、Integer和Long将会给出完全不同或相反的速度结果。
在我的情况下,对于相同的数组,整数总共120kB,长整型总共240kB,我得到了与整数相同的长整型结果。这是因为将相同的数组更改为长整型,总大小增加了一倍,与整数数组相比,更多的数据由于更改为长整型而落在L1缓存之外。要访问L1缓存之外的数据需要更多的时钟或时间。
因此,您的测试只是作为测试而言很好,但在实际生活中却会误导人们,因为msdn.microsoft建议使用Long。此外,那些强调Long内存大小加倍的人并没有强调处理器等待时间达到L1缓存之外甚至更糟的L2缓存或L3缓存之外的数据的后果。对于每个L1、L2和L3之外的数据,到达数据的时间将急剧增加,这对速度最为重要。Dim a() As Byte, b() As Byte, c() As Byte
ReDim a(7, 24, 60), b(24, 7, 60), c(24, 60, 7)
Dim h As Long, loops As Long: Dim i As Long, j As Long, k As Long ' these i, j, k always As Long
loops=1
For h = 1 To loops
For i = 1 To 6: For j = 0 To 23: For k = 1 To 58
a(i, j, k) = a(i + 1, j, k): b(j, i, k) = b(j, i - 1, k)
c(j, k, i) = a(i - 1, j, k + 1) + b(j, i - 1, k - 1)
Next k: Next j: Next i
For i = 6 To 1 Step -1: For j = 23 To 0 Step -1: For k = 58 To 1 Step -1
a(i, j, k) = a(i + 1, j, k): b(j, i, k) = b(j, i - 1, k)
c(j, k, i) = a(i - 1, j, k + 1) + b(j, i - 1, k - 1)
Next k: Next j: Next i
Next h
首先将“循环”设置为1,以查看所需时间。然后逐渐增加它,以达到几秒钟的字节大小。对于As Integer,需要更长的时间,对于As Long则需要更长的时间...
每个3个数组的大小为8x25x61 = 12200,这是
12200 kB乘以3 = 36600 kB用于As Byte,
24400 kB乘以3 = 73200 kB用于As Integer,
48800 kB乘以3 = 146400 kB用于As Long。
使用Dim a() As Integer,b() As Integer,c() As Integer运行相同的代码,然后使用Dim a() As Long,b() As Long,c() As Long等。
现在,如果您将一个维度增加20倍,则预计持续时间将增加20倍,但实际上会更多,因为现在数据将超出L2缓存(4个核心共享1MB)。
如果您将一个维度增加200倍,则预计持续时间将增加200倍,但实际上会再次更多,因为现在数据将超出L3缓存(4个核心和8个核心共享6-8MB,而ryzen 5800则为8MB或16MB...)。
我不明白为什么经过20年或更长时间后,L1缓存仅为64kB,而它至少可以是16x16=256kB。使用16位行地址和16位列地址,您只需要读取32位,这是32位处理器的一次读取。我怀疑这是因为核心仍在使用16位(8位行+8位列地址,8x8=64kB)或更糟糕的是仅使用8位。
测试后请发布您的结果。
Type
时,有另一种情况必须使用Integer
,这是因为类型的布局和大小很重要,无论是因为您要将该类型传递给API,还是因为您要对文件进行序列化/反序列化,或者使用LSet
/Rset
复制字节。 - ThunderFrame