为什么使用整数而不是长整型?

Why Use Integer Instead of Long?

提问人:Gareth 提问时间:10/17/2014 最后编辑:Gareth 更新时间:2/28/2023 访问量:93317

问:

我经常看到与 错误相关的问题。Overflow

我的问题是为什么使用变量声明,而不仅仅是将所有数值变量(不包括等)定义为?integerdoublelong

除非您正在执行像 for 循环中的操作,您可以保证该值不会超过 32,767 限制,否则是否会对性能产生影响或其他要求不使用 ?long

VBA long-integer 整数溢出

评论


答:

8赞 Alex K. 10/17/2014 #1

VBA有很多历史包袱。

An 的宽度为 16 位,在 16 位体系结构/字大小盛行时,它是一个很好的默认数字类型。Integer

A 的宽度为 32 位,应尽可能使用 (IMO)。Long

158赞 RubberDuck 10/17/2014 #2

整数变量存储为 16 位(2 字节)数字

Office VBA 参考

长(长整型)变量存储为有符号的 32 位(4 字节)数字

Office VBA 参考

因此,好处是减少了内存空间。Integer 占用的内存是 Long 占用内存的一半。现在,我们谈论的是 2 个字节,所以它不会对单个整数产生真正的影响,它只是在处理大量整数(例如大型数组)并且内存使用至关重要时才是一个问题。

但是32 位系统上,内存使用量减半是以性能为代价的。当处理器实际使用 16 位整数执行一些计算时(例如,递增循环计数器),该值会静默地转换为临时的 Long,而没有更大的数字范围可供使用。溢出仍然会发生,处理器用于存储计算值的寄存器将采用相同数量的内存(32 位)。性能甚至可能受到损害,因为必须转换数据类型(在非常低的级别)。

不是我要找的参考资料,而是......

我的理解是,底层 VB 引擎将整数转换为长整数,即使它被声明为整数。因此,可以注意到速度略有下降。我相信这一点已经有一段时间了,也许这也是做出上述声明的原因,我没有要求推理。

OzGrid 论坛

这是我一直在寻找的参考。

简答,在 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” 系统,除了一些期望接收 整数。

PCPreview 论坛

终于,我找到了我真正想要的 msdn 文档

传统上,VBA 程序员使用整数来保持小 数字,因为它们需要更少的内存。在最近的版本中, 但是,VBA 会将所有整数值转换为 Long 类型,即使它们是 声明为 Integer 类型。因此,不再有性能优势 使用整数变量;事实上,Long 变量可能略有 更快,因为 VBA 不必转换它们。

根据注释澄清一下:整数仍然需要更少的内存来存储 - 大型整数数组需要的 RAM 比具有相同维度的 Long 数组要少得多(几乎正好是一半,您可以在任务管理器中自行检查)。但是,由于处理器需要使用 32 位内存块,因此 VBA 在执行计算时会暂时将整数转换为多头


因此,总而言之,现在几乎没有充分的理由使用类型。除非您需要与需要 16 位 int 的旧 API 调用进行互操作,或者您正在使用小整数的大型数组并且内存非常宝贵。Integer

值得指出的一件事是,一些旧的 API 函数可能需要 16 位(2 字节)整数的参数,如果您使用的是 32 位并尝试通过引用传递 Integer(已经是 4 个字节长),由于字节长度的差异,它将无法工作。

感谢 Vba4All 指出这一点。

评论

6赞 11/4/2014
值得指出的一件事是,一些旧的 API 函数可能需要 16 位(2 字节)整数的参数,如果您使用的是 32 位并尝试通过引用传递 Integer(已经是 4 个字节长),由于字节长度的差异,它将无法工作。
3赞 4/26/2015
@It'sbeenapleasure 我不明白。如果将 Long by ref 传递给需要 Integer by ref 的函数,它将使用前两个字节,并且由于数字存储在 little-endian 中,因此它将起作用(前提是只有两个较低的字节有意义,但如果这个 Long 只是一个以 32 位存储的 Integer),则很可能)。
3赞 ThunderFrame 4/10/2017
您必须使用的另一个时间是在声明 时,其中类型的布局和大小很重要,因为您要将类型传递给 API,或者您正在序列化/反序列化文件,或者您正在使用 / 复制字节。IntegerTypeLSetRset
1赞 GSerg 9/3/2017
MSDN 文章是错误的和/或误导性的。例如,它可能是指在处理器内部作为 s 进行内部处理,但出于所有可观察的目的,仍然需要两个字节。请参阅 stackoverflow.com/q/26717148/11683 下的答案和评论,以进行扩展讨论。IntegerLongInteger
1赞 GSerg 9/3/2017
你似乎没有在你的答案中说明 msdn.microsoft.com/en-us/library/office/......可能是错误的或误导性的,相反,你把它呈现为 然而,我的评论主要是针对未来的读者而不是你自己,因为你参与了另一个问题,即使你的答案在我看来不应该被接受(我认为这个应该被接受)。the msdn documentation I was really truly looking for.
13赞 Patrick 10/17/2014 #3

正如其他答案所指出的,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 或其他语言中迁移到任何其他语言时,我更喜欢使用 int16int32int64 的系统数据类型,这使我能够更加透明地了解这些数据类型中可以保存的值。

5赞 Alter 10/17/2014 #4

这是一个空间必要性的问题。

在某些情况下,有必要使用长。如果要遍历大型 Excel 文件中的行,则保存行号的变量应为 long。

但是,有时您会知道整数可以处理您的问题,而使用长整数会浪费空间(内存)。单个变量确实没有太大区别,但是当您开始处理数组时,它可能会产生很大的不同。

  • 在 VBA7 中,整数为 2 个字节,longs 为 4 个字节

  • 如果您有一个介于 1 到 10 之间的 100 万个数字的数组,则使用 Integer 数组将占用大约 2MB 的 RAM,而长数组的 RAM 约为 4MB。

评论

1赞 ChrisB 9/1/2017
对于 1 到 10 之间的数字数组,您可以改用 BYTE 数组,但我理解您的观点。
0赞 Vityata 9/5/2018
这在大约15年前可能是正确的,但现在答案真的已经过时了。
0赞 Alter 9/5/2018
这怎么过时了?在拥有数百万行的 pandas 中(大数据/机器学习)或传输大量数据时的网络问题(例如物理实验)
0赞 Vityata 9/7/2018
@Alter - 我猜你错过了原始问题的重点。看看公认的答案。
1赞 Alter 9/7/2018
看来我明白了。此外,公认的答案指出,当它说没有区别时,它谈论的是 32 位系统。
15赞 pgSystemTester 8/5/2018 #5

尽管这篇文章已经有四年的历史了,但我对此感到好奇并进行了一些测试。需要注意的最重要的一点是,编码人员应该始终将变量声明为 SOMETHING。未声明的变量显然表现最差(未声明的变量在技术上是Variant)

Long确实执行得最快,所以我不得不认为 Microsoft 建议始终使用而不是有意义。我猜和 一样,但大多数编码人员不使用它。LongIntegerByte

在 64 位 WINDOWS 10 笔记本电脑上的结果

Variable Olympics

使用的代码:

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

评论

1赞 Vityata 9/5/2018
未声明的变量是变体。 应该给出与完全不“调暗”相同的结果。Dim a as Variant, b as Variant, c As Variant
0赞 pgSystemTester 9/7/2018
@Vityata,我知道“未声明”应该意味着变体,但请记住,整个讨论的背景故事是微软自动将整数转换为长整数,尽管被定义了!所以我不确定在没有定义的情况下对整数的期望是什么。看到结果,它似乎最有可能真的是变体,但这就是为什么我给“未声明”和“变体”这个名字。另外,这对低级编码人员来说可能更有用。但是,我现在将继续添加它。感谢您的反馈。
0赞 Vityata 9/7/2018
@PGCodeRider - 使用 c++ 创建一个 dll 库并将其添加到 VBA 并在那里对其进行基准测试可能会很有趣。结果可能会很有趣。我在这里做了类似的事情——vitoshacademy.com/......
6赞 AJD 6/27/2019 #6

我采用了@PGSystemTester的方法并对其进行了更新,以消除一些潜在的可变性。通过将循环放在例程中,可以消除调用例程所花费的时间(这是很多时间)。我还关闭了屏幕更新,以消除这可能导致的任何延迟。

Long仍然表现最好,并且由于这些结果更紧密地局限于变量类型的影响,因此变化的幅度值得注意。

我的结果(桌面、Windows 7、Excel 2010):

enter image description here

使用的代码:

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

评论

0赞 pgSystemTester 6/7/2021
实际上,我比我更喜欢这种方法。
0赞 Oleg Ushakov 4/3/2023
有兴趣,您有 Postgresql 云实例的性能测试结果吗?您使用的Postgresql版本是什么?您能解释一下 Long 与 Integer 的最佳结果机制吗?
0赞 AJD 4/5/2023
@OlegUshakov:请参阅公认的机制答案。您可以轻松地在自己喜欢的环境中进行测试。
0赞 Oleg Ushakov 4/5/2023
@AJD 或者,我很抱歉,我以为你为 postgresql 环境做了测试。我没有抓住有关 excel 和 vba 脚本的详细信息。请原谅我的错过
2赞 Ama 8/24/2019 #7

正如其他人已经提到的,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 次)。

请注意,这个主观阈值远低于整数上限,我使用多头只是为了区分我自己对小和大的定义。

0赞 vio 8/29/2022 #8

Ty AJDpgSystemTester。我确认整数 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,访问数据的时间将急剧增加,这对速度最为重要。

总结一下:

  1. 如果您的数据适合 L1 缓存,那么 Long 是最快的,但 只有 4k 的数据乘以 Long = 16kB 的 4Bytes(因为其他程序和操作系统将填充 32k 的 L1 缓存的其余部分),

  2. 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 位上工作。

测试后,请发布您的结果。

0赞 Brugon 9/15/2022 #9

我知道这是一篇旧文章,但我在浏览时发现了它,我也想分享我的发现: 在@PGSystemTester的方法之后,我得到了那些enter image description here

在 AJD 之后

enter image description here

英特尔 i5-8500T 处理器 8gigs 内存 64位系统和Win10Enterprise 21H1操作系统内部版本19043.2006 而 excel 位于版本 2108 和内部版本 14326.20852 上

我也不知道它是否有影响,但我也得到了 Rubberduck Vers。2.5.2.5906

但似乎我的 Integer 在这两种情况下都更快