Re[46]: MS забило на дотнет. Питону - да, сишарпу - нет?
От: Sinclair Россия https://github.com/evilguest/
Дата: 08.09.21 05:28
Оценка: 3 (1)
Здравствуйте, vdimas, Вы писали:

V>Здравствуйте, Sinclair, Вы писали:


S>>Это и есть те случаи, когда "сама структура не нужна за пределами текущего фрейма стека, и не хочется нагружать GC." Вы по-прежнему невнимательно читаете.


V>В своём репертуаре. ))

V>С двух раз не понял, о чём речь, но виноваты окружающие.


S>>Во всех остальных случаях мы делаем просто var t = new Struct1[42]. Это же не Java с её отсутствием value types.


V>Структуру можно вернуть и по-значению.

V>Унутре вызывающая сторона подготавливает место под структуру и передаёт ссылку на это место в кач-ве аргумента.
Так по ссылке или по значению?

S>>А в вашем случае все "проблемы" сводятся к

S>>
S>>byte *tmp = stackalloc byte[sizeof(Struct1)*42];
S>>Struct1* ptr = (struct1*) tmp
S>>


V>Нарушение типизации, почва для ошибок.

V>Сейчас можно типизированно.
Ну, да — улучшение можно только приветствовать
V>Опять что-то странное пишешь.
V>Это не работает:
V>
V>public ref int this[int index] {...}
V>

V>Получить ref или readonly ref-ссылку на поле структуры через метод самой структуры нельзя, можно только через метод-расширение, где саму структуру передавать через ref this.
Вот удивительно, но можно. То ли новый дотнет, то ли я в прошлый раз как-то не так смотрел:
https://sharplab.io/#v2:C4LghgzgtgPgAgJgIwFgBQcDMACR2DC2A3utmbjgK4B2EYAZgKbYTABOlAxsNgJLXAAgmzZgAnqXIk05WdgAObAJYA3MMGZKB2APoAbRtQDmwABYBuSXIXK1G7PSUAPRgBNsWnq/VgA2piQAXUsZawpsNkZ6D20zJQhfTxjXRidA7ABeAD4IqOxvYD8tFLSQsPCkgBlDE1NMnP0aszKwrD4BYVExAAokg2MzAEp63X7azOwx5qtsAF8Zmba4ABZsAFlu4elytTZsMAnqRgB3dqERcW6kAAZBlus/AFZAjOWEe7n0WaA=
Впрочем, для моих целей всё равно удобнее работать со Span<T> — из-за того же ограничения, которое вы указали: невозможность fixed T data[]
V>fixed-массивы не при чём, никакое поле структуры нельзя так вернуть.

V>И да, пинить поля структуры в её методах — плохая идея.

V>Структура не имеет информации — был ли вызван её метод со ссылкой this на значение целевой переменной (или поля с типом этой структуры) или рантайм создал временную копию структуры и вызвал метод со ссылкой на копию. Т.е., по выходу из такого метода ты можешь получить ссылку на несуществующую более память.
V>А какая разница?
V>В случае flexible structs всё-равно надо создать Span от inplace-массива с "вручную" отмеренным нужным размером, т.к. при создании Span никаких проверок не делается.
Проверки делаются при доступе к структуре, что позволяет передавать её в гражданский код.
Остаётся проблема с честностью выделения — если мы выделим такую структуру напрямую или нечаянно в результате боксинга, то всё взорвётся.
Можно попробовать сделать оператор приведения примерно так: https://sharplab.io/#v2:C4LghgzgtgPgAgJgIwFgBQcAMACOSB0ASgK4B2wAllAKb4CS51ATgPYAOAyswG4UDG1CAG506OAGZcSAGy4E2BsACCTJmACeACWoAbNs3QBvdNlO5JeWU2oAzBeRVr12JREWONACmAALChGwONjBSAB4AI3VgagA+bAATMGAwABpsCnJsHWpSAEoTM2M0MxL0u09s0gAqCAoAL2oWG08M4FyAalqGppbyXOw4xOT8ABkcgHNffOLS2d9WAHdsUmollXHiGnIAUQAPATZKFlJPUjAaHqGwXNyRGdmAWWooFiZ1B7AmCB8wHXwAdSYFGiniuaWsdkqtwKszgAHZsBDsE8Xm8Pl8fn9XIRbKF3KoNDFQUlrncSgBfdCUtBiSRkCBgGzUbAQYBMYh8YD2ZQE9RGGHYNhA7hJZmtbAAfUqkx8ZLMQooIui6UyEqumDlpgkiNsKq5vn8AG1xRl4tRdgBdbAAXjiSJRr3en2+v3wAGFrKKgiFPEi1STMGkpRMpsbSGbLZrzHrsGNSDKbXFg/HfHdqbS5Ng3dgiiVtfTGcy4AAWZGefq52bYEVMBI27CeVlgPgAa1+OhYfGwkWihqQmAQxYtuXwrnxTk8/cw0PuJQF1PJQA=

Тогда использовать flexible struct можно так:
var d = (stackalloc byte[1024]).AsIntArray(100);


V>Для интеропа не подойдёт, т.к. сломает разметку ожидаемой структуры.

Вот тут не понял.

V>Возможно.

V>TCPDirect дают примерно 20ns задержку от прихода пакета в сетевую карточку, поток тупо в цикле опрашивает диспетчер.
V>Но вызов туда нельзя помечать SuppressGCTransition, т.к. в этом вызове иногда может быть и вызов ядра (как повезёт).
Как я понял из их доки, почти все вызовы там (включая поллинг) — неблокирующие, поэтому можно.


V>Более микросекунды.

ой. Каюсь, был невнимателен.

V>И если есть вызовы примитивов синхронизации и вообще АПИ ОС.

вроде бы написано blocking API, а не просто любой.

V>В Vulkan вынесли CommandBuffer наружу (двумя способами — можно копировать подготовленный буфер в карточку, а можно маппить часть памяти графической подсистемы в обычное адресное пространство и набивать прямо память графики — в случае встроенной графики это в разы эффективней) и позволили самим выбирать момет flush этого буфера в карточку или unmap, но конкретные форматы команд тоже скрыты за АПИ типа такого:

V>https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkCmdSetViewport.html
V>Поэтому, дёргать эти АПИ из дотнета смысла нет.
V>Надо переносить в дотнет приличную часть драйвера — всё что касается набивки буфера команд.
На первый взгляд, там всё должно быть достаточно примитивно.

S>>Если у нас какая-то прямо жёсткая зависимость от внешнего вызова, но их много и они занимают мало времени на анменеджед стороне — PInvoke вовсе не так уж плох. Всего лишь втрое дороже call EAX.

V>В 17 раз дороже.
Бенчмарк с вами не согласен. Я же привёл результаты. В 10 раз дороже — если нет SuppressGCTransition, и в три раза, если он есть.
17 раз нет ни в каком варианте.

V>Но проблема одна — энергоэффективность вычислений.

V>Т.е., грубо, сколько "сжигается деревьев" на каждый чих.
Нет. Это режим эксплуатации.

S>>А вот в сервер-сайд у нас приложения работают неделями.

V>Но при обновлении должны будут тормозить несколько первых минут?
При рестарте.

V>Я не вижу причин, по которым сервер-сайд должен отказываться от АОТ.

V>Т.е. вообще не вижу.
Я тоже не вижу причин отказываться от AOT, но только в том случае, если он собирается работать совместно c хот-споттингом.
Если же поставить выбор между АОТ и возможностью динамического кода + хотспот, то я выберу второе.

V>Разумеется.

V>Как раз АОТ занимается и этими вещами тоже.


V>Боюсь, вычислительная сложность сериализации в XML такая, что никакой оптимизатор инлайнить это не будет.

V>У оптимизаторов в любом случае стоят пороги срабатывания от сложности методов, в т.ч. транзитивной/вложенной сложности.
Ну, так это как раз потому, что у оптимизаторов ограничено время работы и нет информации о статистике выполнения.
V>Скорее, оптимизатор заинлайнит что-то в кишках XML-сериализатора, но верхние уровни пойдут как есть.
Ну, в нашем случае мы написали высокопроизводительный XML-сериализатор на $"<flightData><flightNo>{Escape(d.GetString(0))}</flightNo><departureDate>{d.GetDate(1).ToString(r)}</departureDate></flightData>"

V>Современные джавовские хот-спот оптимизаторы имеют еще большие ограничения на сложность, чем нейтивные оптимизаторы.

V>Т.е. на таком верхнем уровне вряд ли что-то будет сделано.
Это был умозрительный пример. На каком бы уровне мы ни работали, в ООП-коде всегда есть какой-то косвенный вызов. И всегда есть статистика реального исполнения этого кода, которая меняется от инсталляции к инсталляции и даже в процессе работы одной и той же инсталляции.

S>>При этом, опять же, после инлайна кода у нас появляется статистика о том, какие именно запросы делаются в dataReader. И, соответственно, возможность ещё что-то проинлайнить, выбросив лишние ветки if вместе с косвенными вызовами.


V>Оптимизации выполняются на уровне конкретного типа.

V>Плохо представляю себе приложение, где в DataReader в любой точке приложения приходит одна и та же схема данных. ))
Ну так в том-то и дело: пока мы делаем косвенный вызов метода DataReader, в call target мы вынуждены обрабатывать общий случай. Ведь VMT для всех пользователей этого DataReader одинаковая.
А вот когда мы встроили тело DataReader.GetDate(x) в конкретное место вызова, то внезапно оказывается, что для данной конкретной копии кода исходного GetDate в конкретном if/switch чаще всего (== всегда) попадается ровно один вариант. Ведь конкретно в этом месте всегда используется одна и та же схема данных.
Вроде бы не такая сложная конструкция, чего тут не понять? Погуглите любую статью со словами speculative inlining.

V>Для DataReader "типы" полей динамические, зависят от схемы принятого рекордсета.

V>Тут и С++ ничего не сделает.
Про С++ ничего сказать не могу. Способен ли он выполнять PGO-оптимизацию уже PGO-оптимизированного кода? А для хотспота это, насколько я знаю, норма.

V>Надо тупо писать эффективный код.

V>И в то же самое время читабельный и поддерживаемый. ))
Ну, так эти две цели в известной мере друг другу противоречат.

V>Это принцип работы.

Стрёмный принцип.

V>Всю эту кодогенерацию, которую ты показывал для linq, можно было выполнить и для AOT.

Ну, это всего лишь небольшой маленький пример. Реальные задачи не исчерпываются linq выражениями.
V>Просто тут нужно соотв. плагинное АПИ к АОТ.
V>Т.е., AOT даёт тебе конкретные типы, т.к. в закрытой системе типов они известны (даже если заведомо абстрактны — это тоже известно), а "плагин" генерит код, который опять же компиляется AOT.
В теории — интересно. На практике — где посмотреть на такой AOT?

V>Еще как можно.

Нет, нельзя. Просто потому, что я как пользователь могу этот констреинт отключить на время балк-импорта, а потом не включить. Или включить через какое-то время.
V>2. В обычных компиллируемых как минимум бета-редуцируемых языках (шаблоны С++ в пример) через генерирование двух версий — для проверенного и непроверенного констрейна.
Не двух, а 2количество констреинтов.

V>Даже в языках с зависимыми типами всё-равно в какой-то точке программы идёт проверка и ветвление/диспетчеризация кода.

V>Так же и в твоём примере с SQL-сервером, для данной сущности берётся одна из готовых реализаций, соотв. констрейнам этой сущности.
Нет. Просто при построении плана учитывается текущая статистика и актуальные метаданные.
Напомню: в прошлый раз дискуссия про "повторное использование компонентов плана выполнения запроса" кончилась тем, что вы тихо слились, как только дело дошло до кода.
И в этот раз будет то же самое.

V>То бишь, у тебя может быть уже сетка неких алгоритмов под разные сценарии. При изменении сценария ты подставляешь указатель на нужный алгоритм.

Ага. Размер этой сетки для баз данных быстро начинает превышать разумные пределы. Как, впрочем, и для любых комбинаторных задач.

V>Хотспот еще более хрупкий и непредсказуемый.

Пока что он демонстрирует впечатляющие результаты — сама по себе джава очень тормозная, там принято множество заведомо неэффективных решений как на уровне языка, так и на уровне JVM. И тем не менее, она не слишком катастрофически проигрывает передовикам рынка во множестве областей. И вытягивает её как раз JIT и хотспот.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.