Про замыкания, ну бокс бог с ним - не будем называть это прямо "боксингом"
Не "не будем", а "нельзя".
Боксинг - хоть в .НЕТ, хоть в абстрактном CS-определении - это не "копировние из стека в кучу" и кроме аллокации тянет за собой и вычислительные расходы.
В случае с захватом этого не происходит.
Более того, и самой локальной переменной на стеке вообще [в большинстве случаев] не окажется, она сразу будет определена в классе, созданном компилятором для лямбды.
но факт, что (возможно, нежелательное) копирование из стека в кучу при замыкании вполне возможно
Не "вполне возможно", а обязательно произойдёт, бай дизайн, так сказать.
When a callvirtmethod instruction has been prefixed by constrainedthisType, the instruction is executed as follows:
If thisType is a reference type (as opposed to a value type) then ptr is dereferenced and passed as the 'this' pointer to the callvirt of method.
If thisType is a value type and thisType implements method then ptr is passed unmodified as the 'this' pointer to a callmethod instruction, for the implementation of method by thisType.
If thisType is a value type and thisType does not implement method then ptr is dereferenced, boxed, and passed as the 'this' pointer to the callvirtmethod instruction.
This last case can occur only when method was defined on Object, ValueType, or Enum and not overridden by thisType. In this case, the boxing causes a copy of the original object to be made. However, because none of the methods of Object, ValueType, and Enum modify the state of the object, this fact cannot be detected.
Но ок, box в IL действительно нет, спасибо за напоминание.
Справедливости ради, факт встречи человека, который вообще рассматривает вариант перемещения по Женеве за рулём, а не на общественном транспорте (на крайняк такси) тоже достаточно неожиданный ;)
А можете побенчмаркать, сколько он через OpenVpn и Wireguard сможет прокачать? В качестве сервера и в качестве клиента?
Я тоже планирую подобное собрать, пытаюсь определиться с процессором, в идеале чтобы хватило полностью забить как минимум 1 гигабит (а то и 2.5 в пике), при этом держать 2-3 впн-сервера для 4-8 клиентов и 2-3 клиентских подключения к сторонним ВПН-серверам.
Вообще, в ConcurrentQueue не так уж много локов. На самом деле из конкурентной троицы Queue-Stack-Bag она самая быстрая (на моём опыте).
Любопытно было бы взглянуть, что её так убило.
… ок, приврал, ConcurrentQueue быстрее стека, ConcurrentBag легче, но это другая история.
Спасибо за ссылку, о паре нюансов даже не задумывался.
Но всё это не отменяет моего удивления: мой случай идеален для SpinLock: очень короткая операция, количество потоков меньше количества ядер, очень низкий или нулевой contention — казалось бы, почему Monitor/Lock заметно лучше? Если бы было +- одинаково — не было бы вопросов.
… по следам этого доклада, прогнал бенчмарк для new SpinLock(false): быстрее дефолтного, но в случае с (около)нулевым контеншном всё равно медленнее lock'a, в случае с высоким контеншном — быстрее.
Позор, конечно, что я упускал это раньше.
Немного не в тему, но всё-же: а пробовали сравнивать производительность SpinLock и Monitor?
К моему удивлению, Monitor работает быстрее, по крайней мере в случаях, когда в основном ему не приходится блокировать поток(и).
Честно говоря, считал, что СпинЛок должен быть гораздо дешевле и шустрее.
Ну, рельный юз-кейс же, никем (судя по всему) не поддерживаемый :)
Я бы на месте производителя как минимум проанализировал бы, чисто для расширения кругозора.
Не "не будем", а "нельзя".
Боксинг - хоть в .НЕТ, хоть в абстрактном CS-определении - это не "копировние из стека в кучу" и кроме аллокации тянет за собой и вычислительные расходы.
В случае с захватом этого не происходит.
Более того, и самой локальной переменной на стеке вообще [в большинстве случаев] не окажется, она сразу будет определена в классе, созданном компилятором для лямбды.
Не "вполне возможно", а обязательно произойдёт, бай дизайн, так сказать.
Ноль вопросов, никаких личностей, просто я (лично) считаю, что учиться никогда не поздно и не зазорно, а иногда ещё и нужно ;)
Вообще, не сказал бы, что прям неявно:
https://learn.microsoft.com/en-us/dotnet/api/system.reflection.emit.opcodes.constrained?view=net-8.0&redirectedfrom=MSDN
Но ок, box в IL действительно нет, спасибо за напоминание.
Тем не менее, что насчёт обсуждаемого случая?
Есть там цитата?
Отлично, первые шаги к изучению учебника сделаны.
А можно теперь конкретную цитату, которая относится к обсуждаемому случаю?
Любопытно было бы увидеть примеры, не поделитесь?
...но это, к примеру, легко доступная элементарная оптимизация:
Объявляем локальную переменну, объявляем лямбду, которая её захватывает.
В цикле n раз изменяем значение переменной и вызываем метод, в который передаём лямбду - итого одна аллокация вместо n
Всё может быть чревато :)
С "проблемой цикла" можно научиться жить, а вот невозможность изменять захваченную переменную - мне бы мешало.
А вот это можно почитать и в учебнике.
Жаль нельзя на большие деньги поспорить, погулял бы на НГ за чужой счёт в кои-то веки.
Повторю: боксинга нет, ref struct захватывать нельзя по другой причине (да, именно из-за отъезда в кучу).
"Уезжать в кучу" != боксинг.
нет там никакого боксинга.
Большое спасибо.
А можете добавить в бенчмарки измерение аллокаций и GC?
Справедливости ради, факт встречи человека, который вообще рассматривает вариант перемещения по Женеве за рулём, а не на общественном транспорте (на крайняк такси) тоже достаточно неожиданный ;)
Буду благодарен.
ОпенВПН сервер на ОпенВрт тоже поднять совсем несложно, очень бы помогло, если и его сможете протестировать.
А можете побенчмаркать, сколько он через OpenVpn и Wireguard сможет прокачать? В качестве сервера и в качестве клиента?
Я тоже планирую подобное собрать, пытаюсь определиться с процессором, в идеале чтобы хватило полностью забить как минимум 1 гигабит (а то и 2.5 в пике), при этом держать 2-3 впн-сервера для 4-8 клиентов и 2-3 клиентских подключения к сторонним ВПН-серверам.
Вообще, 150 Мбит это очень скромно, если не сказать прямо: неприемлимо мало.
Сам пытался найти бенчмарки чтобы подобрать железо — нереально.
Вообще, в ConcurrentQueue не так уж много локов. На самом деле из конкурентной троицы Queue-Stack-Bag она самая быстрая (на моём опыте).
Любопытно было бы взглянуть, что её так убило.
… ок, приврал, ConcurrentQueue быстрее стека, ConcurrentBag легче, но это другая история.
Спасибо за ссылку, о паре нюансов даже не задумывался.
Но всё это не отменяет моего удивления: мой случай идеален для SpinLock: очень короткая операция, количество потоков меньше количества ядер, очень низкий или нулевой contention — казалось бы, почему Monitor/Lock заметно лучше? Если бы было +- одинаково — не было бы вопросов.
… по следам этого доклада, прогнал бенчмарк для new SpinLock(false): быстрее дефолтного, но в случае с (около)нулевым контеншном всё равно медленнее lock'a, в случае с высоким контеншном — быстрее.
Позор, конечно, что я упускал это раньше.
Немного не в тему, но всё-же: а пробовали сравнивать производительность SpinLock и Monitor?
К моему удивлению, Monitor работает быстрее, по крайней мере в случаях, когда в основном ему не приходится блокировать поток(и).
Честно говоря, считал, что СпинЛок должен быть гораздо дешевле и шустрее.
Кстати, под NET Core вполне можно было оставаться на EF6 и спокойно дожидаться, пока они доделают необходимые фичи.
Ну, рельный юз-кейс же, никем (судя по всему) не поддерживаемый :)
Я бы на месте производителя как минимум проанализировал бы, чисто для расширения кругозора.