Страницы: 1
RSS
Как лучше передавать аргументы в функцию или процедуру — ссылкой (ByRef) или значением (ByVal)?, ByRef or ByVal?
 
Приветствую!
    Решил тут проверить кое-что для себя и зафиксировать тут  :)
    Если тип передачи аргумента не указывается, то по умолчанию используется ByRef. Иными словами, только передача аргумента значением нуждается в явном указании — ByVal.
    Передача аргумента по ссылке избегает его (аргумента) копирования и позволяет изменять передаваемые аргументы.
    Нельзя передать значением (ByVal) аргументы массивов, Но можно передавать значением аргумент типа Variant, в который запросто можно передать массив.

Справка:
    Passing Arguments by Value and by Reference (Visual Basic)

Похожие темы:
    Передача параметров в процедуру
    Передать значение переменной в управляющий модуль

Test String
Test Long (намного более непонятная картина)

Тесты показывают:
    • вариативный аргумента (по ссылке) — универсален. Чуть медленнее строгого типа, зато быстрее при несовпадении типов.
    • если строго ожидается и передаётся строка по ссылке, то это быстрее всего, но для других типов (если перед передачей понадобиться преобразование) будет медленнее вариативного аргумента.
    • по скорости передача значением равна присвоению "нужной" переменной внутри процедуры/функции, а это довольно медленно.

    Также, плюсом вариативного аргумента является то, что его очень легко можно проверить на IsMissing(), если он опциональный — то есть был ли передан аргумент пользователем.
Например, в случае опционального лонга, его НЕпередача и его передача со значением 0 ничем не будут отличаться (если значение по умолчанию для опционального аргумента не задано или задано 0) и проверить будет просто невозможно (насколько мне известно).

    Выводы делайте сами  ;)
Изменено: Jack Famous - 08.08.2023 15:08:39
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Вроде есть такое правило, если размер аргумента меньше размера указателя, то быстрей передавать по значению, если больше - по ссылке (указателю).
Размер указателя в 32битном VBA - 4 бит(Long), в 64битном - 8бит (LongLong).
 
Цитата
testuser: Вроде есть такое правило, если размер аргумента меньше размера указателя, то быстрей передавать по значению, если больше - по ссылке (указателю).Размер указателя в 32битном VBA - 4 бит(Long), в 64битном - 8бит (LongLong).
первый раз об этом слышу и сильно сомневаюсь. Можно пруфы?
Изменено: Jack Famous - 07.08.2023 19:10:45
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Цитата
Jack Famous написал:
Можно пруфы?
Видел где-то на vb-шных форумах, ссылку не сохранял. Вообще, если взять функцию VarPtr, которая возвращает указатель на переменную, в x64 VBA
Typenae(VarPtr(a)) вернет LongLong.
На сколько знаю при передаче параметров функции они записываются в некий "стек", в виде ссылок(указателей) или значений. И получается, если значение меньше ссылки, то быстрее записать в стек значение.
 
Мне кажется, это дело вкуса и стиля программиста.
Я всегда использую ByVal для входных параметров скалярных типов (не массивов) и массивов небольшого размера. Иными словами, ByRef используется редко и только в тех случаях, когда дает ощутимое преимущество.
Еще надо помнить, что если вы вызываете (или планируете вызывать) макрос через Application.Run, то его параметры должны быть описаны как ByVal.
Владимир
 
Цитата
sokol92: Мне кажется, это дело вкуса и стиля программиста.
вот не соглашусь  :)
    Как я понимаю (и описал в шапке), ByVal — по сути, создаёт копию переданного аргумента и, поэтому, в лучшем случае "отставания" по скорости мы не заметим (как в тесте Long), но оно всегда будет.
    Собираюсь провести ещё несколько тестов.
Цитата
sokol92: Еще надо помнить, что если вы вызываете (или планируете вызывать) макрос через  Application.Run , то его параметры должны быть описаны как ByVal.
спасибо! Неочевидно и не знал  :idea:

UPD:
добавил в шапку для String-теста функцию, Var3(), где аргументом является Variant и передаётся значением (ByVal).
    Результат стабильно самый худший. Даже хуже (ненамного) копирования  (в String) аргумента внутри функции Var2().
Изменено: Jack Famous - 08.08.2023 09:53:49
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Ну вообще, странно, что об этом не упомянуто сразу в начале(или я просмотрел), но главное отличие 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 совсем не скорость.
ну здрасьте  :D
Цитата
Jack Famous: Передача аргумента по ссылке избегает его (аргумента) копирования и позволяет изменять передаваемые аргументы.
только зачем это, если я могу внутри функции сделать копию аргумента самостоятельно. Я бы понял, если бы так быстрее было или ещё что …

Дмитрий(The_Prist) Щербаков, зачем тебе Call? Можно же просто Test_ByVal s
    Чтобы показать, что идёт вызов своего кода?
Изменено: Jack Famous - 08.08.2023 10:48:49
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Цитата
Jack Famous написал:
ну здрасьте
да-да, это видел. Но описано уж очень кратко и мало понятно - если в тему зайдет новичок, ничего не поймет из этой фразы. Видимо, поэтому и не уложилось в памяти :) Так что сорян - проглядел. Но, думаю, более полное описание этого поведения не повредит теме.
Цитата
Jack Famous написал:
только зачем это, если я могу внутри функции сделать копию аргумента самостоятельно.
как минимум - чтобы знать, что по умолчанию именно такое поведение предусмотрено. И это можно использовать - как с ByRef, так и с ByVal. Сейчас не буду накидывать примеры, когда это может пригодиться - как правило это более сложные коды, чем просто вызов одного из другого. Но как замена тем же глобальным переменным это тоже используется. Просто в основной процедуре берется одно имя переменной и передается в другие через ByRef, чтобы её изменить и использовать далее в основной процедуре. При этом нет необходимости очищать переменную после завершения основного кода, что обычно необходимо делать с глобальными переменными. Особенно это актуально, если переменная с одним именем используется с нескольких основных процедурах, а изменяется в одних и тех же вспомогательных. Хоть сам такое "неявное" присвоение не практикую, но и ничего плохого здесь нет. Для запутывания кода, кстати, тоже неплохой прием.

Цитата
Jack Famous написал:
зачем тебе Call?
мне так удобнее. Мы уже как-то обсуждали тему с Call :)
Даже самый простой вопрос можно превратить в огромную проблему. Достаточно не уметь формулировать вопросы...
 
Цитата
написал:
зачем тебе Call?
И мне так больше нравится)) По мне, код более читаем. Не спутаешь с переменной и проч.
Согласие есть продукт при полном непротивлении сторон
 
Цитата
Дмитрий(The_Prist) Щербаков: Но, думаю, более полное описание этого поведения не повредит теме.
разумеется, тема от этого только выиграет  :idea:
Цитата
Дмитрий(The_Prist) Щербаков: Мы уже как-то обсуждали тему с Call  
и я там забыл ответить… Исправился — посмотри  :)

Цитата
Sanja: Не спутаешь с переменной и проч.
я для своих процедур и функций в надстройке использую префикс "PRDX_"
Изменено: Jack Famous - 08.08.2023 11:53:31
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
А как правильно читается или произносится  "PRDX_"?  :)
 
nilske, как и положено — "ПэЭрДэИкс нижнее подчёркивание"  ;)
    Только не припомню, чтобы мне приходилось произносить это вслух …

    А вообще, это сокращение от "PaRADoX" — мой (и не только) очень древний псевдоним в играх (и не только). Хотел давно тут поменять, но Виктор не дал — мол Джека уже запомнили  :)
Изменено: Jack Famous - 08.08.2023 14:03:36
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Jack Famous,  а, понятно, три раза "э", а не только первая
 
Цитата
nilske: Jack Famous ,  а, понятно, три раза "э", а не только первая
рад, что помог вам разобраться с правилом чтения аббревиатур, но постарайтесь больше не оффтопить тут  ;)
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
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

На выходе у меня (в 32 битном VBA) получается
Код
Byte(1)       Integer(2)    Double(8)     Single(4)     Varian(16)    Currency(8)   String..      Long
Ptr1          Ptr2          Ptr3          Ptr4          Ptr5          Ptr6          Ptr7          Ptr8
 1             2             8             4             6             8             6 
 4             4             8            -176           196          -200           212 
                                                         20(Ptr6-Ptr4)              12(Ptr8-Ptr6)


Видно, во-первых
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 - 08.08.2023 18:00:46
 
testuser, спасибо, но в вашем исследовании нет самого интересного (для меня), а именно — сравнения скоростей (или, хотя бы, удобства или ещё чего).
    Мои тесты показывают, что ByVal может быть, в лучшем случае, НЕ медленнее ByRef и никаких преимуществ в его (ByVal) использовании я (во всяком случае, пока что) не вижу.
    Для себя я решил все аргументы передавать ссылками (ByRef), с указанием их типов (если логикой не предусмотрен Variant) — это самый быстрый вариант без каких-либо существенных неудобств.
Изменено: Jack Famous - 08.08.2023 17:55:30
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Выскажу своё мнение! Вы слишком углубляйтесь в тему, а это ни к чему, ведь излишняя оптимизация - это лишняя трата времени и удел новичков. Программирование макросов на VBA это прежде всего решение прикладных задач, где жёсткой потребности в Performance не требуется. Это же не системное программирование или разработка критических систем!  

Цитата
написал:
Мои тесты показывают, что ByVal может быть, в лучшем случае, НЕ медленнее ByRef и никаких преимуществ в его (ByVal) использовании я (во всяком случае, пока что) не вижу.
Главной идеей (по информации от разработчиков) создания данного механизма передачи данных является возможность изменять/неизменять программный элемент, лежащий в основе аргумента в вызывающем коде. То есть ни о какой производительности речи не идёт. Приоритет - универсальность!

Цитата
написал:
нет самого интересного (для меня), а именно — сравнения скоростей

Выбор механизма передачи данных, где в приоритете Performance (производительность) будет зависеть прежде всего от входных данных! Нельзя точно сказать, какой способ выбрать, если не знаешь, какие данные будешь обрабатывать!

То есть если на вход Вы передаёте String, Object, любые виды Arrays (вектор (одномерный массив), матрицу (двумерный массив) или тензор (3D массив и более)), то рекомендуется конечно передача через ByRef, иначе при ByVal во время инициализации процедуры с аргументами (её запуске) дополнительно будет произведено копирование данных в локальные переменные, что увеличит общее время выполнения функции! А работать приходиться, в основном с немалыми объёмами данных!

Если душнить, то есть такие понятия, как изменяемые и неизменяемые элементы, но это Вы можете в документации прочитать и без моих комментариев!

Итог: Разбирая только интересующую тему, а именно сравнение скоростей, то делаем вывод, что всё зависит от входных данных! Сравнивать производительность на незначительных примерах и цепляться за каждую наносекунду - это признак киберпсихоза и постоянства (нужно учить что-то новое, а не сидеть на одном стеке всю жизнь)!

P.S. Данный комментарий сгенерирован ботом на основе ChathGPT, которого разоблачили (Пруфы)!
 
Цитата
Aнастасия написал:
Данный комментарий сгенерирован ботом на основе ChathGPT,
и именно по этому много воды и мало толка.
По вопросам из тем форума, личку не читаю.
 
Цитата
написал:
и именно по этому много воды и мало толка.
А аргументы...? Или у Вас принято только мнениями обмениваться (без негатива)?
 
Цитата
Aнастасия: P.S. Данный комментарий сгенерирован ботом на основе ChathGPT
явка с повинной заложена в модель или это реакция?)
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Это троллинг, типо я бот ChatGPT... Я так понимаю Мы неправильно поняли друг друга! Подскажите пожалуйста, я только зарегистрировалась на данном форуме! Я так понимаю комментировать и обсуждать статьи я пока не могу? Я серьёзно без шуток, у Вам такая политика?
 
Да у Нам такая политика. Бред в качестве советов помечать "Лунтизмом" и удалять. Не с той карты вы явно зашли. На этом точка. Как писал ранее треп буду удалять, если не буду там видеть рационального ответа или вопроса. Поро ответы генерируемые роботом - даже не говорю. если интересно, то берите свой форум, сажайте туда двух ботов пусть общаются.

Форум (общение) — место (площадка) для общения и споров людей на различные темы жизни; также — собственно сам процесс общения.
По вопросам из тем форума, личку не читаю.
 
БМВ, хорошо, Вас поняла и Бот не использовала Я.
Тогда вопрос такой! Я привела аргументы в пользу того, что ориентироваться на производительность кода не нужно! Всё зависит от обрабатываемых данных! В чём проблема тогда того комментария? Jack Famous, может же аргументировать, если я не права!
 
Цитата
Aнастасия: Я привела аргументы в пользу того, что ориентироваться на производительность кода не нужно!
аргументов не увидел, от входящих данных зависит, но это описано, но, самое важное: это вы за меня решили, ориентироваться ли мне на скорость работы или нет?
Боюсь, не смогу подчиниться...
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Jack Famous, хорошо, вопросов нету! Верните пожалуйста тогда мой комментарий, а то непонятно о чём речь!
 
Цитата
Aнастасия написал:
Я привела аргументы в пользу того, что ориентироваться на производительность кода не нужно! Всё зависит от обрабатываемых данных!
Я писал ранее , хватит прописные истины толкать, даже если они не ботом писаны. А то никто не знает что все зависит от данных.
Как вы ответите на вопрос как быстро дойти пешком до Луны? А если Луна  - это соседний бар?
Все стоп. Одно только утверждение про то что производительность удел не кодов на VBA уже бесит.
Цитата
Aнастасия написал:
Программирование макросов на VBA это прежде всего решение прикладных задач, где жёсткой потребности в Performance не требуется.

Темы закрыты, а то руки будут болеть их чистить.
По вопросам из тем форума, личку не читаю.
Страницы: 1
Читают тему
Наверх