Вы когда-нибудь задумывались, какие и куда данные передаются при клиент-серверных вызовах и как на это влияет ключевое слово Знач в описании параметров? Я вот не особо. Пока жизнь не заставила. Занялся в очередной раз проблемой быстродействия какой-то там формы. И так и сяк - вроде всё нормально написано. А время где-то теряется. И не могу понять, где...
Как обычно: если что-то не получается с двадцатого раза, прочтите наконец инструкцию.
На ИТС в руководстве разработчика в главе «4.7.3.2. Вызов с передачей управления с клиента на сервер» нашёл:
...передача параметров в случае клиент-серверного взаимодействия происходит особым образом:
- При передаче управления с клиента на сервер (и обратно) всегда передаются копии параметров. При вызове серверной процедуры или функции с клиента происходит создание копии фактического параметра и передача этой копии на сторону сервера. При возврате управления с сервера на клиента, также происходит создание копии формального параметра (с которым происходила работы в вызванной процедуре или функции) для передачи обратно на клиента.
- Если формальный параметр указан с модификатором Знач, то значение параметра будет передаваться только при вызове процедуры или функции и не будет передаваться обратно при возврате управления на клиента.
Стало очень интересно как на практике это Знач влияет на происходящее…
Накидал простую тестовую обработку. Она за указанное количество итераций постепенно наполняет массив и передаёт его как параметр в серверном вызове то по ссылке, то по значению. Для экономии времени на получение результата все первые вызовы делаются на клиенте. И только на последних 4 проходах вызываю сервер.
Использовать имеет смысл в реальной клиент-серверной системе в режиме тонкого клиента.
У меня на 1000 итераций на релизе платформы 8.3.19 получилось вот так:
И для сравнения на 5000:
Где:
- Итерация - номер итерации (размер массива)
- ТипВызова - тип вызова тестовой функции
- Начало- время в миллисекундах на клиенте перед вызовом тестовой функции
- НаСервере - время в миллисекундах на сервере в начале выполнения тестовой функции
- ДлительностьНаСервер - время в миллисекундах на передачу параметров и управления с клиента на сервер
- Финиш - время в миллисекундах на клиенте после вызова тестовой функции
- ДлительностьНаКлиент - время в миллисекундах на возврат данных и управления с сервера на клиент
- ОбщаяДлительность - время в миллисекундах на весь вызов тестовой функции от Начала до Финиша
Наблюдения:
- Локальные вызовы на клиенте с передачей параметра по ссылке выполняются очень быстро, в пределах 1 микросекунды
- На сам по себе клиент-серверный вызов всегда тратится некоторое заметное количество времени даже без передачи параметров. Как на передачу управления с клиента на сервер, так и на обратную передачу управления. Это время всегда плюс-минус постоянно
- Почему оно разное на разных итерациях - я не знаю. Занятно то, что между разными итерациями на клиенте и на сервере проходит разное количество микросекунд. Прям квантовое взаимодействие с участием кота Шрёдингера...
- Длительность перехода управления на сервер предсказуемо линейно растет по мере увеличения размера передаваемых параметров
- А вот длительность возврата управления на клиент зависит от способа передачи параметра: по ссылке или по значению
- Как и было предсказано в документации, при передаче параметра по ссылке возврат управления с сервера на клиент сопровождается обратной передачей параметров. Время на эту операцию практически такое же, что и на передачу с клиента на сервер
Не знаю как вам, но мне не часто попадаются функции со Знач в параметрах. Хотя это явно увеличивает быстродействие. Примерно вдвое. Поэтому бросаем всё и бежим переписывать весь код с использованием Знач :-)
Но, думаю, не стоит слишком спешить.
Для себя я сделал выводы:
- В локальных вызовах без передачи управления эффективнее вызывать функции с передачей параметров по ссылке. Я не проверял это предположение, но оно выглядит обоснованным - не надо тратить время и память на создание копии параметра
- В серверных функциях, которые будут вызываться с клиента, надо учитывать это свойство платформы. В общем случае переменные таких функций есть смысл объявлять со Знач. Влияние будет заметнее при росте объёма передаваемых параметров
- Но если функция реально будет возвращать изменённое значение в параметре, то Знач надо убрать. Иначе оно на клиент не вернётся в параметре
-
Кстати, это означает, что конкретную серверную функцию рационально объявлять только для одной цели:
• ИЛИ для вызова с клиента – и тогда использовать Знач (осознанно, с учётом предыдущего пункта)
• ИЛИ для локального вызова на сервере – и тогда передавать параметры без Знач по ссылкеЕсли функция будет использоваться в обоих случаях, то правильнее сделать спец прокси-версию для вызова с клиента со Знач и дальше уже сразу вызывать серверную версию по ссылкам.
Использование временного хранилища и других технологий для передачи данных при клиент-серверном взаимодействии имеет место быть. Но все они выходят за рамки интереса данной статьи.
Про ключевое слово Знач есть полезная статья:
//infostart.ru/1c/articles/388527/
Для любителей DIY форма обработки:
Код:
&НаКлиентеНаСервереБезКонтекста
Функция ОбщаяФункцияНаКлиентеНаСервере(м, Поправка, ТипВызова)
тудм = ТекущаяУниверсальнаяДатаВМиллисекундах() + Поправка;
Возврат Новый Структура("НаСервере,ДлительностьНаСервер,ТипВызова", тудм, тудм - м[м.ВГраница()].Начало, ТипВызова);
КонецФункции
&НаСервере
Функция ВыполнитьНаСервереПоСсылке(м, Поправка)
Возврат ОбщаяФункцияНаКлиентеНаСервере(м, Поправка, "По ссылке");
КонецФункции
&НаСервере
Функция ВыполнитьНаСервереПоЗначению(Знач м, Знач Поправка)
Возврат ОбщаяФункцияНаКлиентеНаСервере(м, Поправка, "По значению");
КонецФункции
&НаСервере
Функция ВремяНаСервере()
Возврат ТекущаяУниверсальнаяДатаВМиллисекундах();
КонецФункции
&НаКлиенте
Процедура ВыполнитьЗамер(Команда)
Результат = Новый Массив;
Поправка = ТекущаяУниверсальнаяДатаВМиллисекундах() - ВремяНаСервере();
Для х = 1 По Итераций Цикл
Стр = Новый Структура("Итерация,ТипВызова,Начало,НаСервере,ДлительностьНаСервер,Финиш,ДлительностьНаКлиент,ОбщаяДлительность");
Результат.Добавить(Стр);
Стр.Итерация = х;
Стр.Начало = ТекущаяУниверсальнаяДатаВМиллисекундах();
Если х >= Итераций - 3 Тогда // Чтобы поменьше ждать. Всё равно самое интересное можно увидеть в конце
Если х % 2 = 0 Тогда
Ответ = ВыполнитьНаСервереПоСсылке(Результат, Поправка);
Иначе
Ответ = ВыполнитьНаСервереПоЗначению(Результат, Поправка);
КонецЕсли;
Иначе
Ответ = ОбщаяФункцияНаКлиентеНаСервере(Результат, 0, "На клиенте");
КонецЕсли;
тудм = ТекущаяУниверсальнаяДатаВМиллисекундах();
Стр = Результат[Результат.ВГраница()];
ЗаполнитьЗначенияСвойств(Стр, Ответ);
Стр.Финиш = тудм;
Стр.ДлительностьНаКлиент = тудм - Стр.НаСервере;
Стр.ОбщаяДлительность = тудм - Стр.Начало;
КонецЦикла;
Для х = Результат.ВГраница() - 5 По Результат.ВГраница() Цикл
Сообщить(ДампСтруктуры(Результат[х]));
КонецЦикла;
КонецПроцедуры
&НаКлиенте
Функция ДампСтруктуры(Что)
Результат = "";
Для каждого Эл Из Что Цикл
Результат = Результат + Эл.Ключ + " = " + Эл.Значение + "; ";
КонецЦикла;
Возврат Результат;
КонецФункции