Зачем использовать целое число вместо длинного?
Я часто вижу вопросы, связанные с Overflow
ошибки с vba.
Мой вопрос: зачем использовать integer
объявление переменной вместо простого определения всех числовых переменных (исключая double
и т. д.) как long
?
Если вы не выполняете операцию, подобную циклу for, где вы можете гарантировать, что значение не превысит ограничение 32 767, это повлияет на производительность или что-то еще, что будет диктовать неиспользование long
?
10 ответов
Целочисленные переменные хранятся как 16-битные (2-байтовые) числа
Длинные (длинные целые) переменные хранятся как 32-битные (4-байтовые) числа со знаком
Таким образом, выгода заключается в уменьшении объема памяти. Integer занимает половину памяти, которую занимает long. Теперь мы говорим о 2 байтах, так что это не будет иметь большого значения, если вы не храните TON целых чисел.
НО в 32- разрядной системе 16-разрядное целое число тихо преобразуется в длинное без использования большего диапазона чисел для работы. Переполнения по-прежнему случаются и занимают столько же памяти. Производительность может даже ухудшиться, потому что тип данных должен быть преобразован (на очень низком уровне).
Не ссылка, которую я искал, но....
Насколько я понимаю, основной движок VB преобразует целые числа в длинные, даже если он объявлен как целое число. Поэтому можно отметить небольшое снижение скорости. Я верил этому в течение некоторого времени, и, возможно, именно поэтому было сделано вышеупомянутое заявление, я не просил аргументировать.
Это ссылка, которую я искал.
Короткий ответ: в 32-разрядных системах 2-байтовые целые числа преобразуются в 4-байтовые значения. На самом деле другого пути нет, поэтому соответствующие биты правильно выстраиваются для любой формы обработки. Рассмотрим следующее
MsgBox Hex(-1) = Hex(65535) ' = True
Очевидно, что -1 не равно 65535, но компьютер возвращает правильный ответ, а именно: "FFFF" = "FFFF"
Однако, если бы мы принудили -1 к длинному, мы получили бы правильный ответ (65535, превышающий 32 КБ, автоматически является длинным)
MsgBox Hex(-1&) = Hex(65535) ' = False
"FFFFFFFF" = "FFFF"
Как правило, в VBA нет смысла объявлять "как целое число" в современных системах, за исключением, возможно, некоторых устаревших API, которые ожидают получения целого числа.
И в конце концов я нашел документацию msdn, которую действительно искал.
Традиционно, программисты VBA использовали целые числа для хранения небольших чисел, потому что им требовалось меньше памяти. Однако в последних версиях VBA преобразует все целочисленные значения в тип Long, даже если они объявлены как тип Integer. Таким образом, больше нет преимущества в производительности при использовании переменных типа Integer; на самом деле, длинные переменные могут быть немного быстрее, потому что VBA не нужно преобразовывать их.
Итак, в целом, почти нет веских причин использовать Integer
введите эти дни. Если вам не нужно взаимодействовать со старым вызовом API, который ожидает 16-битное int.
Стоит отметить, что некоторые старые функции API могут ожидать параметры, которые являются 16-разрядными (2-байтовыми) целыми числами, и если вы используете 32-разрядный код и пытаетесь передать целое число (это уже 4-байтовый код) по ссылке он не будет работать из-за разницы в длине байтов.
Спасибо Vba4All за указание на это.
Хотя этому посту уже четыре года, мне было любопытно, и я провел несколько тестов. Самое важное, на что следует обратить внимание, это то, что кодировщик ВСЕГДА должен объявлять переменную как SOMETHING. Необъявленные переменные явно показали худшее (необъявленные являются технически Variant
)
Long
действительно быстрее всех, так что я должен думать, что рекомендация Microsoft всегда использовать Long
вместо Integer
имеет смысл. Я думаю, так же, как и в случае с Byte
, но большинство кодеров не используют это.
РЕЗУЛЬТАТЫ НА 64 BIT WINDOWS 10 LAPTOP
Используемый код:
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
Как отмечалось в других ответах, реальная разница между int и long - это размер его памяти и, следовательно, размер числа, которое он может содержать.
вот полная документация по этим типам данных: http://msdn.microsoft.com/en-us/library/office/ms474284(v=office.14).aspx
целое число составляет 16 бит и может представлять значение от -32 768 до 32 768
Длинна составляет 32 бита и может представлять от 2 147 483 648 до 2 147 483 648.
и есть LongLong, который составляет 64 бита и может обрабатывать как 9 Pentilion
Важно помнить, что типы данных различаются в зависимости от языка и операционной системы / платформы. В вашем мире VBA long - 32 бита, но в C# на 64-битном процессоре long - 64 бита. Это может привести к значительной путанице.
Хотя VBA не поддерживает его, при переходе на любой другой язык в.net, java или других, я очень предпочитаю использовать системные типы данных int16, int32 и int64, что позволяет мне гораздо более прозрачно понимать значения, которые могут быть проведены в этих типах данных.
У VBA много исторического багажа.
Integer
имеет ширину 16 бит и был хорошим числовым типом по умолчанию, когда преобладали размеры 16 битов архитектуры / слова.
Long
имеет ширину 32 бита и (IMO) должен использоваться везде, где это возможно.
Я взял метод @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
Это проблема пространства и необходимости.
В некоторых ситуациях необходимо использовать длинный. Если вы просматриваете строки в большом файле Excel, переменная, которая содержит номер строки, должна быть длинной.
Тем не менее, иногда вы будете знать, что целое число может решить вашу проблему, и использование long будет пустой тратой пространства (памяти). Отдельные переменные на самом деле не имеют большого значения, но когда вы начинаете работать с массивами, это может иметь большое значение.
В VBA7 целые числа составляют 2 байта, а длинные - 4 байта.
Если у вас есть массив из 1 миллиона чисел от 1 до 10, использование массива Integer займет около 2 МБ ОЗУ, по сравнению с примерно 4 МБ ОЗУ для длинного массива.
Как уже упоминалось, Long может занимать вдвое больше места, чем Integer. Как уже упоминали другие, высокая производительность современных компьютеров означает, что вы не увидите никакой разницы в производительности, если только вы не имеете дело с очень большими, очень большими данными:
Память
Учитывая 1 миллион значений, разница между использованием целых и длинных будет составлять 2 байта для каждого значения, так что 2 * 1 000 000 / 1024 / 1024 = менее 2 МБ разницы в ОЗУ, что, вероятно, намного меньше, чем 1% или даже 0,1% от объема вашей оперативной памяти.
обработка
Учитывая эталонный тест PGSystemTester, вы можете увидеть разницу в 811 - 745 = 66 секунд между Long и Integer при обработке 10 миллиардов пакетов по 4 операции в каждой. Сократите число операций до 1 миллиона, и мы можем ожидать 66/10 000/4 = менее 2 мс разницы во времени выполнения.
Лично я использую Integer и Longs, чтобы улучшить читаемость моего кода, особенно в циклах, где Integer указывает, что цикл, как ожидается, будет небольшим (менее 1000 итераций), тогда как Long говорит, что цикл, как ожидается, будет довольно большим (больше чем 1000).
Обратите внимание, что этот субъективный порог намного ниже верхнего предела Integer, я использую Longs просто для того, чтобы провести различие между моими собственными определениями малого и большого.
Ty AJD и pgSystemTester . Я подтверждаю результаты AJD для Integer 33 и Long 20 . Тем не менее, Long намного быстрее только потому, что ваша тестовая программа достаточно мала, чтобы полностью поместиться в кэш-памяти процессора. Самая быстрая оперативная память - это кэш L1, который составляет всего 32 КБ для данных и 32 КБ для программы. Тестирование Byte, Integer и Long для одного или нескольких массивов, которые едва помещаются в 32 КБ кеша данных L1, даст вам совершенно разные или противоположные результаты в отношении скоростей.
В моем случае для тех же массивов, которые составляют 120 КБ для целых чисел и 240 КБ для длинных, у меня был тот же результат для длинных, что и для целых. Это связано с тем, что при изменении на Long одинаковые массивы в сумме удваивают размер по сравнению с массивами Integer, и поэтому все больше и больше данных выпадают за пределы кеша L1 из-за перехода на Long. Для доступа к данным за пределами кеша L1 требовалось гораздо больше часов или времени.
Поэтому ваш тест хорош только как тест, но в реальной жизни вводит в заблуждение, поскольку рекомендация msdn.microsoft использовать Long независимо. Кроме того, те, кто подчеркивают, что размер оперативной памяти удваивается для Long, не обращают внимания на последствия времени ожидания процессором для достижения данных за пределами кеша L1 или даже в худшем случае за пределами кеша L2 или вне кеша L3. Для каждого внешнего L1, L2 и L3 время доступа к данным резко возрастет, и это наиболее важно для скорости.
Обобщить:
если ваши данные помещаются в кеш L1, то Long является самым быстрым, но это всего 4 КБ данных , умноженных на 4 байта для Long = 16 КБ (поскольку другие программы и ОС будут заполнять остальные 32 КБ кеша L1),
Byte и Integer резко увеличат скорость для массивов размером не менее 16 КБ, потому что изменение на Long увеличит размер, и это заставит больше данных находиться за пределами самой быстрой оперативной памяти кэша L1.
Попробуйте тот же тест, но вместо 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
Сначала установите «loops» на 1, чтобы увидеть, сколько времени это займет. Затем постепенно увеличивайте его, стремясь в течение нескольких секунд к As Bytes. Это займет больше времени для As Integer и еще больше времени для As Long...
Размер каждого из 3-х массивов 8x25x61 = 12200 и это
12200 КБ умножить на 3 = 36600 КБ для As Byte ,
24400 КБ умножить на 3 = 73200 КБ для As Integer ,
48800 КБ умножить на 3 = 146400 КБ для 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 (1 МБ общего для всех 4 ядер).
Если вы увеличите одно измерение в 200 раз, вы ожидаете увеличения продолжительности в 200 раз, но оно снова будет намного больше, потому что теперь данные будут попадать за пределы кеша L3 (6-8 МБ общего для всех 4 ядер и столько же 8 МБ для 8 ядер или 16 МБ, если райзен 5800...).
Я не могу понять, почему через 20 или более лет кэш L1 составляет всего 64 КБ, когда он может быть как минимум 16x16=256 КБ. С 16 битами для адреса строки и 16 битами для адреса столбца вам нужно прочитать только 32 бита, и это одно чтение для 32-битного процессора. Я подозреваю, что это потому, что, возможно, ядро все еще работает на 16 битах (8 для строки + 8 для адреса столбца, 8x8=64 КБ) или, что еще хуже, только на 8 битах.
После тестирования отпишитесь о результатах.
Я знаю, что это старый пост, но я нашел его во время просмотра, и я также хотел поделиться своими выводами: после метода @PGSystemTester я получил эти
и после AJD
Процессор Intel i5-8500T, 8 гигабайт ОЗУ, 64-разрядная система и ОС Win10Enterprise 21H1, сборка 19043.2006, в то время как Excel находится в версии 2108 и сборке 14326.20852.
Я также не знаю, повлияло ли это, но я также получил Rubberduck Vers. 2.5.2.5906
но похоже, что мой Integer быстрее в обоих случаях
Возможно, выпускник CS может объяснить это лучше, но да, целое число требует меньше памяти для хранения (может не иметь отношения к очень простому сценарию VBA).
Это также гарантирует / проверяет, что переменная всегда будет целым числом (недесятичным). Я уверен, что если вы храните 6.7 как целое число, оно будет преобразовано в число 7 в VBA.
Возможно, это не лучшая практика, но могут быть случаи, когда это необходимо. Я думаю, что основной целью является проверка данных / целостность. В частности, да для циклов, где что-то должно происходить X раз - десятичная дробь не имеет смысла.
Основное различие заключается в памяти, хотя - если я помню, длинное 64-битное, а целое число 32-битное.