提问人:Gareth 提问时间:10/17/2014 最后编辑:Gareth 更新时间:2/28/2023 访问量:93317
为什么使用整数而不是长整型?
Why Use Integer Instead of Long?
问:
我经常看到与 vba 错误相关的问题。Overflow
我的问题是为什么使用变量声明,而不仅仅是将所有数值变量(不包括等)定义为?integer
double
long
除非您正在执行像 for 循环中的操作,您可以保证该值不会超过 32,767 限制,否则是否会对性能产生影响或其他要求不使用 ?long
答:
VBA有很多历史包袱。
An 的宽度为 16 位,在 16 位体系结构/字大小盛行时,它是一个很好的默认数字类型。Integer
A 的宽度为 32 位,应尽可能使用 (IMO)。Long
整数变量存储为 16 位(2 字节)数字
长(长整型)变量存储为有符号的 32 位(4 字节)数字
因此,好处是减少了内存空间。Integer 占用的内存是 Long 占用内存的一半。现在,我们谈论的是 2 个字节,所以它不会对单个整数产生真正的影响,它只是在处理大量整数(例如大型数组)并且内存使用至关重要时才是一个问题。
但是在 32 位系统上,内存使用量减半是以性能为代价的。当处理器实际使用 16 位整数执行一些计算时(例如,递增循环计数器),该值会静默地转换为临时的 Long,而没有更大的数字范围可供使用。溢出仍然会发生,处理器用于存储计算值的寄存器将采用相同数量的内存(32 位)。性能甚至可能受到损害,因为必须转换数据类型(在非常低的级别)。
不是我要找的参考资料,而是......
我的理解是,底层 VB 引擎将整数转换为长整数,即使它被声明为整数。因此,可以注意到速度略有下降。我相信这一点已经有一段时间了,也许这也是做出上述声明的原因,我没有要求推理。
这是我一直在寻找的参考。
简答,在 32 位系统中,将 2 字节整数转换为 4 字节 渴望。真的没有其他方法可以使各个位正确对齐 进行任何形式的处理。请考虑以下几点
MsgBox Hex(-1) = Hex(65535) ' = True
显然 -1 不等于 65535,但计算机返回了正确的 答案,即 “FFFF” = “FFFF”
然而,如果我们把-1胁迫到一个长头,我们就会得到正确的结果 答案(大于 32k 的 65535 自动为多头)
MsgBox Hex(-1&) = Hex(65535) ' = False
“FFFFFFFF” = “FFFF”
通常,在 VBA 中没有必要在现代中声明“As Integer” 系统,除了一些期望接收 整数。
终于,我找到了我真正想要的 msdn 文档。
传统上,VBA 程序员使用整数来保持小 数字,因为它们需要更少的内存。在最近的版本中, 但是,VBA 会将所有整数值转换为 Long 类型,即使它们是 声明为 Integer 类型。因此,不再有性能优势 使用整数变量;事实上,Long 变量可能略有 更快,因为 VBA 不必转换它们。
根据注释澄清一下:整数仍然需要更少的内存来存储 - 大型整数数组需要的 RAM 比具有相同维度的 Long 数组要少得多(几乎正好是一半,您可以在任务管理器中自行检查)。但是,由于处理器需要使用 32 位内存块,因此 VBA 在执行计算时会暂时将整数转换为多头
因此,总而言之,现在几乎没有充分的理由使用类型。除非您需要与需要 16 位 int 的旧 API 调用进行互操作,或者您正在使用小整数的大型数组并且内存非常宝贵。Integer
值得指出的一件事是,一些旧的 API 函数可能需要 16 位(2 字节)整数的参数,如果您使用的是 32 位并尝试通过引用传递 Integer(已经是 4 个字节长),由于字节长度的差异,它将无法工作。
感谢 Vba4All 指出这一点。
评论
Integer
Type
LSet
Rset
Integer
Long
Integer
the msdn documentation I was really truly looking for.
正如其他答案所指出的,int 和 long 之间的真正区别在于其内存空间的大小,因此也在于它可以容纳的数字的大小。
以下是有关这些数据类型的完整文档 http://msdn.microsoft.com/en-us/library/office/ms474284(v=office.14).aspx
整数为 16 位,可以表示介于 -32,768 和 32,767 之间的值
a Long 为 32 位,可以表示 -2,147,483,648 到 2,147,483,647
还有一个 LongLong,它是 64 位,可以处理 9 个五连
要记住的最重要的事情之一是数据类型因语言和操作系统/平台而异。在 VBA 世界中,long 是 32 位,但在 c# 中,在 64 位处理器上,long 是 64 位。这可能会带来严重的混淆。
尽管 VBA 不支持它,但当您在 .net 或 java 或其他语言中迁移到任何其他语言时,我更喜欢使用 int16、int32 和 int64 的系统数据类型,这使我能够更加透明地了解这些数据类型中可以保存的值。
这是一个空间与必要性的问题。
在某些情况下,有必要使用长。如果要遍历大型 Excel 文件中的行,则保存行号的变量应为 long。
但是,有时您会知道整数可以处理您的问题,而使用长整数会浪费空间(内存)。单个变量确实没有太大区别,但是当您开始处理数组时,它可能会产生很大的不同。
在 VBA7 中,整数为 2 个字节,longs 为 4 个字节
如果您有一个介于 1 到 10 之间的 100 万个数字的数组,则使用 Integer 数组将占用大约 2MB 的 RAM,而长数组的 RAM 约为 4MB。
评论
尽管这篇文章已经有四年的历史了,但我对此感到好奇并进行了一些测试。需要注意的最重要的一点是,编码人员应该始终将变量声明为 SOMETHING。未声明的变量显然表现最差(未声明的变量在技术上是Variant
)
Long
确实执行得最快,所以我不得不认为 Microsoft 建议始终使用而不是有意义。我猜和 一样,但大多数编码人员不使用它。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
我采用了@PGSystemTester的方法并对其进行了更新,以消除一些潜在的可变性。通过将循环放在例程中,可以消除调用例程所花费的时间(这是很多时间)。我还关闭了屏幕更新,以消除这可能导致的任何延迟。
Long
仍然表现最好,并且由于这些结果更紧密地局限于变量类型的影响,因此变化的幅度值得注意。
我的结果(桌面、Windows 7、Excel 2010):
使用的代码:
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
评论
正如其他人已经提到的,Long 占用的空间可能是 Integer 的两倍。正如其他人已经提到的,当前计算机的高容量意味着您不会看到任何性能差异,除非您正在处理额外的额外超大量数据:
记忆
考虑到 100 万个值,使用整数与长整型之间的差异是每个值的 2 个字节,因此 2 * 1 000 000 / 1,024 / 1024 = RAM 中的差异小于 2 MB,这可能远小于 RAM 容量的 1% 甚至 0.1%。
加工
考虑到 PGSystemTester 所做的基准测试,当处理 100 亿个批次(每个批次 4 个操作)时,您可以看到 Longs 和 Integer 之间的差异为 811 - 745 = 66 秒。将操作数量减少到 100 万次,我们可以预期 66 / 10 000 / 4 = 执行时间差小于 2ms。
我个人使用 Integers 和 Longs 来帮助我的代码的可读性,尤其是在循环中,其中 Integer 表示循环应该很小(少于 1000 次迭代),而 Long 告诉我循环应该相当大(超过 1000 次)。
请注意,这个主观阈值远低于整数上限,我使用多头只是为了区分我自己对小和大的定义。
Ty AJD 和 pgSystemTester。我确认整数 33 和长整数 20 的 AJD 结果。然而,Long 的速度要快得多,只是因为您的测试程序足够小,可以完全放入处理器缓存中。最快的 ram 是 L1 缓存,只有 32kB 用于数据,32kB 用于程序。测试一个或几个勉强适合 32kB L1 数据缓存的数组的 Byte、Integer 和 Long,将为您提供完全不同或相反的速度结果。
就我而言,对于整数总计为 120kB 和 Long 总计 240kB 的相同数组,Long 的结果与 Integer 的结果相同。 这是因为与整数数组相比,更改为 Long 相同的数组的总大小增加了一倍,因此,由于更改为 Long,越来越多的数据落在 L1 缓存之外。要访问 L1 缓存之外的数据,需要更多的时钟或时间。
因此,您的测试仅作为测试而有效,但在现实生活中具有误导性,因为 msdn.microsoft 建议无论如何都要使用 Long。此外,那些强调 Long 的 ram 大小是 long 的两倍的人没有强调处理器等待时间到达 L1 缓存之外的数据的后果,甚至在 L2 缓存之外或 L3 缓存之外最糟糕的情况。对于每个外部 L1、L2 和 L3,访问数据的时间将急剧增加,这对速度最为重要。
总结一下:
如果您的数据适合 L1 缓存,那么 Long 是最快的,但 只有 4k 的数据乘以 Long = 16kB 的 4Bytes(因为其他程序和操作系统将填充 32k 的 L1 缓存的其余部分),
Byte 和 Integer 将大大提高数组大小的速度 至少 16kB,因为更改为 Long 会增加大小,而这 将强制更多数据驻留在最快的 L1 缓存 RAM 之外。
尝试相同的测试,但不要使用 Dim a() As Byte ,而是使用 Dim a() As Byte ,例如:
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 Bytes 几秒钟。As Integer 需要更长的时间,As Long 甚至需要更长的时间......
3 个数组中每个数组的大小为 8x25x61 = 12200,这是
12200 kB 乘以 3 = 36600 kB 对于 As Byte ,
24400 kB 乘以 3 = 73200 kB(As 整数) ,
48800 kB 乘以 3 = 146400 kB(如 Long )。
使用 Dim a() As Integer、b() As Integer、c() As Integer 运行相同的代码,然后使用 Dim a() As Long、b() As Long、c() As Long 等运行相同的代码。
现在,如果将一个维度增加 20 倍,则持续时间将增加 20 倍,但持续时间会增加 20 倍,因为现在数据将超出 L2 缓存(所有 4 个内核共享 1MB)。
如果您将一个维度增加 200 倍,您将期望持续时间增加 200 倍,但它会再次增加,因为现在数据将落在 L3 缓存之外(所有 4 个内核共享 6-8MB,8 个内核共享相同的 8MB,如果 ryzen 5800 为 16MB......
我不明白为什么 20 年或更长时间后,L1 缓存只有 64kB,而它至少可以是 16x16=256kB 。行地址为 16 位,列地址为 16 位,您只需要读取 32 位,即 32 位处理器的一次读取。我怀疑这是因为内核可能仍然可以在 16 位上工作(行地址为 8 位 + 列地址为 8 位,8x8=64kB)或最差的仅在 8 位上工作。
测试后,请发布您的结果。
我知道这是一篇旧文章,但我在浏览时发现了它,我也想分享我的发现: 在@PGSystemTester的方法之后,我得到了那些
在 AJD 之后
英特尔 i5-8500T 处理器 8gigs 内存 64位系统和Win10Enterprise 21H1操作系统内部版本19043.2006 而 excel 位于版本 2108 和内部版本 14326.20852 上
我也不知道它是否有影响,但我也得到了 Rubberduck Vers。2.5.2.5906
但似乎我的 Integer 在这两种情况下都更快
评论