Приветствую! Решил тут проверить кое-что для себя и зафиксировать тут Если тип передачи аргумента не указывается, то по умолчанию используется ByRef. Иными словами, только передача аргумента значением нуждается в явном указании — ByVal. Передача аргумента по ссылке избегает его (аргумента) копирования и позволяет изменять передаваемые аргументы. Нельзя передать значением (ByVal) аргументы массивов, Но можно передавать значением аргумент типа Variant, в который запросто можно передать массив.
Option Base 1
Option Explicit
Option Private Module
'==================================================================================================
Private Function f_ByRef(s$) As Long
f_ByRef = InStr(s, "b")
End Function
'==================================================================================================
Private Function f_ByVal(ByVal s$) As Long
f_ByVal = InStr(s, "b")
End Function
'==================================================================================================
Private Function f_Var(s) As Long
f_Var = InStr(s, "b")
End Function
'==================================================================================================
Private Function f_Var2(s) As Long
Dim ss$: ss = s
f_Var2 = InStr(ss, "b")
End Function
'==================================================================================================
Private Function f_Var3(ByVal s) As Long
f_Var3 = InStr(s, "b")
End Function
'==================================================================================================
'==================================================================================================
'==================================================================================================
'==================================================================================================
Private Sub Test()
Dim v, aVar(1), aStr$(1), t!, s$, res&, n&
Const nCyc& = 10000000 ' 10 mln
s = "bob"
v = s: aVar(1) = s: aStr(1) = s
t = Timer ' s
For n = 1 To nCyc
' res = f_ByRef(s) ' 0.6
' res = f_Var(s) ' 0.8
' res = f_ByVal(s) ' 1.4
' res = f_Var2(s) ' 1.4
res = f_Var3(s) ' 1.7
Next n
Debug.Print Format$(Timer - t, "0.0"), res
't = Timer ' aStr$()
' For n = 1 To nCyc
' res = f_ByRef(aStr(1)) ' 0.6
' res = f_Var(aStr(1)) ' 0.8
' res = f_ByVal(aStr(1)) ' 1.4
' res = f_Var2(aStr(1)) ' 1.5
' res = f_Var3(aStr(1)) ' 1.7
' Next n
'Debug.Print Format$(Timer - t, "0.0"), res
't = Timer ' v
' For n = 1 To nCyc
' res = f_Var(v) ' 0.8
' res = f_ByRef(CStr(v)) ' 1.0
' res = f_ByVal(v) ' 1.4
' res = f_Var2(v) ' 1.4
' res = f_Var3(v) ' 1.6
' Next n
'Debug.Print Format$(Timer - t, "0.0"), res
't = Timer ' aVar()
' For n = 1 To nCyc
' res = f_Var(aVar(1)) ' 0.8
' res = f_ByRef(CStr(aVar(1))) ' 1.1
' res = f_ByVal(aVar(1)) ' 1.5
' res = f_Var2(aVar(1)) ' 1.5
' res = f_Var3(aVar(1)) ' 1.7
' Next n
'Debug.Print Format$(Timer - t, "0.0"), res
End Sub
Test Long (намного более непонятная картина)
Код
Option Base 1
Option Explicit
Option Private Module
'==================================================================================================
Private Function f_ByRef(n&) As Boolean
f_ByRef = (n <> 0)
End Function
'==================================================================================================
Private Function f_ByVal(ByVal n&) As Boolean
f_ByVal = (n <> 0)
End Function
'==================================================================================================
Private Function f_Var(n) As Boolean
f_Var = (n <> 0)
End Function
'==================================================================================================
Private Function f_Var2(n) As Boolean
Dim nn&: nn = n
f_Var2 = (nn <> 0)
End Function
'==================================================================================================
'==================================================================================================
'==================================================================================================
'==================================================================================================
Private Sub Test()
Dim v, t!, l&, n&, f As Boolean
Const nCyc& = 10000000 ' 10 mln
l = 1: v = l
t = Timer ' l
For n = 1 To nCyc
f = f_ByRef(l) ' 0.5
' f = f_ByVal(l) ' 0.5
' f = f_Var2(l) ' 0.6
' f = f_Var(l) ' 1.1
Next n
Debug.Print Format$(Timer - t, "0.0"), f
't = Timer ' v
' For n = 1 To nCyc
' f = f_ByRef(CLng(v)) ' 0.5
' f = f_ByVal(v) ' 0.5
' f = f_Var2(v) ' 0.6
' f = f_Var(v) ' 1.1
' Next n
'Debug.Print Format$(Timer - t, "0.0"), f
End Sub
'==================================================================================================
'==================================================================================================
Тесты показывают: • вариативный аргумента (по ссылке) — универсален. Чуть медленнее строгого типа, зато быстрее при несовпадении типов. • если строго ожидается и передаётся строка по ссылке, то это быстрее всего, но для других типов (если перед передачей понадобиться преобразование) будет медленнее вариативного аргумента. • по скорости передача значением равна присвоению "нужной" переменной внутри процедуры/функции, а это довольно медленно.
Также, плюсом вариативного аргумента является то, что его очень легко можно проверить на IsMissing(), если он опциональный — то есть был ли передан аргумент пользователем. Например, в случае опционального лонга, его НЕпередача и его передача со значением 0 ничем не будут отличаться (если значение по умолчанию для опционального аргумента не задано или задано 0) и проверить будет просто невозможно (насколько мне известно).
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Вроде есть такое правило, если размер аргумента меньше размера указателя, то быстрей передавать по значению, если больше - по ссылке (указателю). Размер указателя в 32битном VBA - 4 бит(Long), в 64битном - 8бит (LongLong).
testuser: Вроде есть такое правило, если размер аргумента меньше размера указателя, то быстрей передавать по значению, если больше - по ссылке (указателю).Размер указателя в 32битном VBA - 4 бит(Long), в 64битном - 8бит (LongLong).
первый раз об этом слышу и сильно сомневаюсь. Можно пруфы?
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Видел где-то на vb-шных форумах, ссылку не сохранял. Вообще, если взять функцию VarPtr, которая возвращает указатель на переменную, в x64 VBA Typenae(VarPtr(a)) вернет LongLong. На сколько знаю при передаче параметров функции они записываются в некий "стек", в виде ссылок(указателей) или значений. И получается, если значение меньше ссылки, то быстрее записать в стек значение.
Мне кажется, это дело вкуса и стиля программиста. Я всегда использую ByVal для входных параметров скалярных типов (не массивов) и массивов небольшого размера. Иными словами, ByRef используется редко и только в тех случаях, когда дает ощутимое преимущество. Еще надо помнить, что если вы вызываете (или планируете вызывать) макрос через Application.Run, то его параметры должны быть описаны как ByVal.
sokol92: Мне кажется, это дело вкуса и стиля программиста.
вот не соглашусь Как я понимаю (и описал в шапке), ByVal — по сути, создаёт копию переданного аргумента и, поэтому, в лучшем случае "отставания" по скорости мы не заметим (как в тесте Long), но оно всегда будет. Собираюсь провести ещё несколько тестов.
Цитата
sokol92: Еще надо помнить, что если вы вызываете (или планируете вызывать) макрос через Application.Run , то его параметры должны быть описаны как ByVal.
спасибо! Неочевидно и не знал
UPD: добавил в шапку для String-теста функцию, Var3(), где аргументом является Variant и передаётся значением (ByVal). Результат стабильно самый худший. Даже хуже (ненамного) копирования (в String) аргумента внутри функции Var2().
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Ну вообще, странно, что об этом не упомянуто сразу в начале(или я просмотрел), но главное отличие ByRef от ByVal совсем не скорость. ByRef - всегда ссылка. А это означает, что изменив аргумент внутри вызванной функции, он будет изменен и в вызвавшей его процедуре/функции. ByVal - всегда новое значение, дублирующее по свойствам переданное. И при его изменении внутри функции он изменяется только в ней, никак не влияя на аргумент вызывавшей процедуры/функции. Пример:
Код
Sub Main()
Dim s$, sTest$, sRef$, sVal$
'передаем как ссылку
sTest = "Привет"
s = sTest
Call Test_ByRef(s)
sRef = s
'передаем как значение
s = sTest
Call Test_ByVal(s)
sVal = s
MsgBox "Start variable: " & sTest & _
vbNewLine & "after ByRef variable: " & sRef & _
vbNewLine & "after ByVal variable: " & sVal
End Sub
Function Test_ByRef(ByRef sRef$) 'ByRef - по умолчанию, так что если не указывать об этом надо помнить
sRef = "Пока ByRef"
End Function
Function Test_ByVal(ByVal sVal$)
sVal = "Пока ByVal"
End Function
Даже самый простой вопрос можно превратить в огромную проблему. Достаточно не уметь формулировать вопросы...
Дмитрий(The_Prist) Щербаков: Ну вообще, странно, что об этом не упомянуто сразу в начале(или я просмотрел), но главное отличие ByRef от ByVal совсем не скорость.
ну здрасьте
Цитата
Jack Famous: Передача аргумента по ссылке избегает его (аргумента) копирования и позволяет изменять передаваемые аргументы.
только зачем это, если я могу внутри функции сделать копию аргумента самостоятельно. Я бы понял, если бы так быстрее было или ещё что …
Дмитрий(The_Prist) Щербаков, зачем тебе Call? Можно же просто Test_ByVal s Чтобы показать, что идёт вызов своего кода?
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
да-да, это видел. Но описано уж очень кратко и мало понятно - если в тему зайдет новичок, ничего не поймет из этой фразы. Видимо, поэтому и не уложилось в памяти Так что сорян - проглядел. Но, думаю, более полное описание этого поведения не повредит теме.
Цитата
Jack Famous написал: только зачем это, если я могу внутри функции сделать копию аргумента самостоятельно.
как минимум - чтобы знать, что по умолчанию именно такое поведение предусмотрено. И это можно использовать - как с ByRef, так и с ByVal. Сейчас не буду накидывать примеры, когда это может пригодиться - как правило это более сложные коды, чем просто вызов одного из другого. Но как замена тем же глобальным переменным это тоже используется. Просто в основной процедуре берется одно имя переменной и передается в другие через ByRef, чтобы её изменить и использовать далее в основной процедуре. При этом нет необходимости очищать переменную после завершения основного кода, что обычно необходимо делать с глобальными переменными. Особенно это актуально, если переменная с одним именем используется с нескольких основных процедурах, а изменяется в одних и тех же вспомогательных. Хоть сам такое "неявное" присвоение не практикую, но и ничего плохого здесь нет. Для запутывания кода, кстати, тоже неплохой прием.
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
nilske, как и положено — "ПэЭрДэИкс нижнее подчёркивание" Только не припомню, чтобы мне приходилось произносить это вслух …
А вообще, это сокращение от "PaRADoX" — мой (и не только) очень древний псевдоним в играх (и не только). Хотел давно тут поменять, но Виктор не дал — мол Джека уже запомнили
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
nilske: Jack Famous , а, понятно, три раза "э", а не только первая
рад, что помог вам разобраться с правилом чтения аббревиатур, но постарайтесь больше не оффтопить тут
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Небольшое исследование. Согласно некоторым информациям (+моих догадок), которые я уже частично упоминал, аргументы, переданые по значению должны укладываться в стек в виде этих значений, т.е. занимать место, согласно их размеру. Размер, занимаемый аргументом в стеке можно проверить отняв его указатель от указателя следующего за ним аргумента. Пробуем это проверить
Код
Private Sub CallProc1()
Proc1 1, 2, 3.4, 5.6, 7.8, 9.1, "абв", 0
End Sub
Private Sub Proc1(ByVal b As Byte, ByVal i As Integer, ByVal d As Double, _
ByVal sn As Single, ByVal v As Variant, ByVal c As Currency, _
ByVal st As String, ByVal l As Long)
Dim Ptr1&, Ptr2&, Ptr3&, Ptr4&, Ptr5&, Ptr6&, Ptr7, Ptr8
Ptr1 = VarPtr(b): Ptr2 = VarPtr(i): Ptr3 = VarPtr(d)
Ptr4 = VarPtr(sn): Ptr5 = VarPtr(v): Ptr6 = VarPtr(c)
Ptr7 = VarPtr(st): Ptr8 = VarPtr(l)
Debug.Print "Byte(1)", "Integer(2)", "Double(8)", "Single(4)", "Varian(16)", "Currency(8)", "String..", "Long"
Debug.Print "Ptr1", "Ptr2", "Ptr3", "Ptr4", "Ptr5", "Ptr6", "Ptr7", "Ptr8"
Debug.Print LenB(b), LenB(i), LenB(d), LenB(sn), LenB(v), LenB(c), LenB(st)
Debug.Print Ptr2 - Ptr1, Ptr3 - Ptr2, Ptr4 - Ptr3, Ptr5 - Ptr4, Ptr6 - Ptr5, Ptr7 - Ptr6, Ptr8 - Ptr7
Debug.Print , , , , " " & (Ptr6 - Ptr4) & "(Ptr6-Ptr4)", (Ptr8 - Ptr6) & "(Ptr8-Ptr6)"
End Sub
Видно, во-первых 1) Для типов, размер которых меньше Long (4байт-размера указателя), все равно выделяется 4байт. Видимо это т.н. выравнивание. 2) Функция LenB не правильно показывает байтовый размер для типа Variant, который должен быть мин. 16. 3) VarPtr для "варианта" также показывает другое, что сбивает общую картину, хотя косвенно, отняв из 6 указателя 4й можно увидеть, что под аргумент Variant выделено именно 16 байт. (здесь мы не говорим, о том, что внутри этих 16 байт, содержится ли там, передаваемая "ночинка" или нет). 4) VarPtr для типа String также возвращает не то. Но косвенно можно понять, что строковый аргумент занимает 12 - 6 = 4байта, очевидно, указатель. Строка из 3 букв должна занимать 6 байт (фактически еще +6байт)
testuser, спасибо, но в вашем исследовании нет самого интересного (для меня), а именно — сравнения скоростей (или, хотя бы, удобства или ещё чего). Мои тесты показывают, что ByVal может быть, в лучшем случае, НЕ медленнее ByRef и никаких преимуществ в его (ByVal) использовании я (во всяком случае, пока что) не вижу. Для себя я решил все аргументы передавать ссылками (ByRef), с указанием их типов (если логикой не предусмотрен Variant) — это самый быстрый вариант без каких-либо существенных неудобств.
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Выскажу своё мнение! Вы слишком углубляйтесь в тему, а это ни к чему, ведь излишняя оптимизация - это лишняя трата времени и удел новичков. Программирование макросов на VBA это прежде всего решение прикладных задач, где жёсткой потребности в Performance не требуется. Это же не системное программирование или разработка критических систем!
Цитата
написал: Мои тесты показывают, что ByVal может быть, в лучшем случае, НЕ медленнее ByRef и никаких преимуществ в его (ByVal) использовании я (во всяком случае, пока что) не вижу.
Главной идеей (по информации от разработчиков) создания данного механизма передачи данных является возможность изменять/неизменять программный элемент, лежащий в основе аргумента в вызывающем коде. То есть ни о какой производительности речи не идёт. Приоритет - универсальность!
Цитата
написал: нет самого интересного (для меня), а именно — сравнения скоростей
Выбор механизма передачи данных, где в приоритете Performance (производительность) будет зависеть прежде всего от входных данных! Нельзя точно сказать, какой способ выбрать, если не знаешь, какие данные будешь обрабатывать!
То есть если на вход Вы передаёте String, Object, любые виды Arrays (вектор (одномерный массив), матрицу (двумерный массив) или тензор (3D массив и более)), то рекомендуется конечно передача через ByRef, иначе при ByVal во время инициализации процедуры с аргументами (её запуске) дополнительно будет произведено копирование данных в локальные переменные, что увеличит общее время выполнения функции! А работать приходиться, в основном с немалыми объёмами данных!
Если душнить, то есть такие понятия, как изменяемые и неизменяемые элементы, но это Вы можете в документации прочитать и без моих комментариев!
Итог: Разбирая только интересующую тему, а именно сравнение скоростей, то делаем вывод, что всё зависит от входных данных! Сравнивать производительность на незначительных примерах и цепляться за каждую наносекунду - это признак киберпсихоза и постоянства (нужно учить что-то новое, а не сидеть на одном стеке всю жизнь)!
P.S. Данный комментарий сгенерирован ботом на основе ChathGPT, которого разоблачили (Пруфы)!
Aнастасия: P.S. Данный комментарий сгенерирован ботом на основе ChathGPT
явка с повинной заложена в модель или это реакция?)
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Это троллинг, типо я бот ChatGPT... Я так понимаю Мы неправильно поняли друг друга! Подскажите пожалуйста, я только зарегистрировалась на данном форуме! Я так понимаю комментировать и обсуждать статьи я пока не могу? Я серьёзно без шуток, у Вам такая политика?
Да у Нам такая политика. Бред в качестве советов помечать "Лунтизмом" и удалять. Не с той карты вы явно зашли. На этом точка. Как писал ранее треп буду удалять, если не буду там видеть рационального ответа или вопроса. Поро ответы генерируемые роботом - даже не говорю. если интересно, то берите свой форум, сажайте туда двух ботов пусть общаются.
Форум (общение) — место (площадка) для общения и споров людей на различные темы жизни; также — собственно сам процесс общения.
БМВ, хорошо, Вас поняла и Бот не использовала Я. Тогда вопрос такой! Я привела аргументы в пользу того, что ориентироваться на производительность кода не нужно! Всё зависит от обрабатываемых данных! В чём проблема тогда того комментария? Jack Famous, может же аргументировать, если я не права!
Aнастасия: Я привела аргументы в пользу того, что ориентироваться на производительность кода не нужно!
аргументов не увидел, от входящих данных зависит, но это описано, но, самое важное: это вы за меня решили, ориентироваться ли мне на скорость работы или нет? Боюсь, не смогу подчиниться...
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Aнастасия написал: Я привела аргументы в пользу того, что ориентироваться на производительность кода не нужно! Всё зависит от обрабатываемых данных!
Я писал ранее , хватит прописные истины толкать, даже если они не ботом писаны. А то никто не знает что все зависит от данных. Как вы ответите на вопрос как быстро дойти пешком до Луны? А если Луна - это соседний бар? Все стоп. Одно только утверждение про то что производительность удел не кодов на VBA уже бесит.
Цитата
Aнастасия написал: Программирование макросов на VBA это прежде всего решение прикладных задач, где жёсткой потребности в Performance не требуется.