S>>Как видим, все действуют именно так, как я говорю.
V>Ложь.
V>Причём, ложь глупая, игнорующая реальность.
V>В других сообщениях я утверждал, что те или иные классы можно было сделать структурами, ты отвечал "они и так структуры" — так где?
Вы неверно поняли то, что я писал. Я писал, что внутри MSSQL-драйвера данные лежат в буферах структурного типа, чтобы избежать боксинга на горячих путях. Единственное что — мне показалось, что они там прямо массив структур в датаридере держат, а на самом деле — массив объектов, в которых есть поле-union, в котором лежат данные.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Я просил ссылку на тот код чтобы понять, наконец, это реальный код или умозрительный твой.
V>>В других сообщениях я утверждал, что те или иные классы можно было сделать структурами, ты отвечал "они и так структуры" — так где? S>Вы неверно поняли то, что я писал. Я писал, что внутри MSSQL-драйвера данные лежат в буферах структурного типа, чтобы избежать боксинга на горячих путях. Единственное что — мне показалось, что они там прямо массив структур в датаридере держат, а на самом деле — массив объектов, в которых есть поле-union, в котором лежат данные.
Именно.
И так там вдоль всего кода.
И тоже боксинг в простейших случаях:
internal int Int32
{
get
{
ThrowIfNull();
if (StorageType.Int32 == _type)
{
return _value._int32;
}
return (int)Value; // anything else we haven't thought of goes through boxing.
}
set
{
Debug.Assert(IsEmpty, "setting value a second time?");
_value._int32 = value;
_type = StorageType.Int32;
_isNull = false;
}
}
Re[56]: MS забило на дотнет. Питону - да, сишарпу - нет?
Здравствуйте, vdimas, Вы писали:
V>Продолжаю не видеть там этого кода, про который спросил здесь: V>http://www.rsdn.org/forum/flame.comp/8083927.1
V>Я просил ссылку на тот код чтобы понять, наконец, это реальный код или умозрительный твой.
Это был умозрительный мой код. Неожиданно оказалось, что драйвер с эквивалентным кодом таки существует. Ну, только вместо MemoryMarshal.Cast<> сразу используется MemoryMarshal.Read, а .Slice вызывается на один уровень выше.
V>Именно. V>И так там вдоль всего кода. V>И тоже боксинг в простейших случаях:
Это не "простейшие" случаи; это смена типа поля при чтении. Надо полагать — не самый частый случай.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[46]: MS забило на дотнет. Питону - да, сишарпу - нет?
Здравствуйте, vdimas, Вы писали:
V>Здравствуйте, Sinclair, Вы писали:
S>>Это и есть те случаи, когда "сама структура не нужна за пределами текущего фрейма стека, и не хочется нагружать GC." Вы по-прежнему невнимательно читаете.
V>В своём репертуаре. )) V>С двух раз не понял, о чём речь, но виноваты окружающие.
S>>Во всех остальных случаях мы делаем просто var t = new Struct1[42]. Это же не Java с её отсутствием value types.
V>Структуру можно вернуть и по-значению. V>Унутре вызывающая сторона подготавливает место под структуру и передаёт ссылку на это место в кач-ве аргумента.
Так по ссылке или по значению?
S>>А в вашем случае все "проблемы" сводятся к S>>
V>Нарушение типизации, почва для ошибок. V>Сейчас можно типизированно.
Ну, да — улучшение можно только приветствовать V>Опять что-то странное пишешь. V>Это не работает: V>
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 и хотспот.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[54]: MS забило на дотнет. Питону - да, сишарпу - нет?
Здравствуйте, vdimas, Вы писали:
V>Т.е., даже в случае дотнетной идеальной безбоксированной реализации надо было бы тоже боксировать для GUI, приводя к Object. V>А если до этого уже боксировали и анбоксировали, то доп. боксирование выглядит и вовсе грустно.
И это на интерпретаторе и SQL на одной машине с 1С сервером приложений! Проблема в том, что нельзя одним запросом вытащить данные и приходится тащить на клиента, потом опять делать запрос. И все это упирается в межпроцессный маршалинг!
Пусть лучше будет копирование, но в одном процессе как в Сингулярити https://www.rsdn.org/article/singularity/singularity.xml
Что бы те же EF, Linq2DB работали на SQL сервере! На Java что то есть для оракула, но как то не особо об этом говорят.
А гуй то сам тормозной, что на боксинг точно не является лимитирующей стадией процесса!
На самом деле как ты сам видишь .Net движется к ускорению и упрощению взаимодействия с нативом
и солнце б утром не вставало, когда бы не было меня
Re[45]: MS забило на дотнет. Питону - да, сишарпу - нет?
Здравствуйте, vdimas, Вы писали:
V>>>GetFunctionPointerForDelegate — это единственный (ранее) был способ получать колбэки из нейтивного кода в управляемый. S>>Вы невнимательно читаете. Вот эту часть явно пропустили: "...и вызовем его из менеджед кода"
V>Сорри, это уже утомительно... V>Через "пирог" вызова именно так и будет.
Вот интересно зачем этот пирог организовывать, если из манагеда можно передать все возможные данные зная алгоритм.
Это как раз из области таскать данные с SQL на клиента и обратно.
Или если ну невозможно предусмотреть все варианты, учитывая, что вызов из манагеда дешевле в десятки раз вернуть ссылку на структуру хотелок.
Обработав эту структуру манагед вызовет нативную функцию передав данные и ссылку на эту структуру
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>Нет, не телепатия, а намек на то что ты не прочел то, на что отвечал. Речь шла про необходимость боксинга при выводе данных в гуй,
Изначально речь была про чтение бд и "если это значение передадут GUI-таблице"
А я добавил, что в определенных кейсах твои утверждения не работают и прямо написал, в каких именно.
> потому что контролы и биндинг в гуе работает с обжектами.
Вот это и есть проблема, что тупенький GUI работает с обжектами. И при большом объеме данных, которые затягиваются в такой GUI, это дает конские издержки. Вместо мелкой операции в пересчете на объект получаем конские издержки на боксинг-анбоксинг, которые в разы дольше самой операции.
> А теперь попробуй объяснить, при чем тут твои рассчеты?
Я ж тебе не зря про САПР сказал. Допустим, тебе надо отобразить модель и результат её работы на конкретной выборке. Выборка >1gb. Её рисовать как раз не надо. Но вот загрузить — да, надо, те части, которые изменились с момента последней операции.
"а теперь посчитаем вот с таким условием" — означает, что тебе надо загрузить совсем другой гигабайт данных.
Вот здесь боксинг-анбоксинг мне и нахрен не упал.
I>>Грид при каждом изменении надо пересчитать полностью.
НС>Зачем при этом боксинг всех данных?
Все данные идут из бд
1 " нормальный гуй будет боксировать только то что сейчас отображается"
Неверно:
нормальный гуй вообще боксировать не должен
см выше про выборку
2 "Боксирование же нескольких десятков строк в условиях находящейся в почти эксклюзивном использовании персональной машины — не заметить и в мелкоскоп"
Здравствуйте, Serginio1, Вы писали:
V>>Через "пирог" вызова именно так и будет. S> Вот интересно зачем этот пирог организовывать, если из манагеда можно передать все возможные данные зная алгоритм.
В моей практике не раз приходилось делать колбэки из unmanaged в managed.
Re[55]: MS забило на дотнет. Питону - да, сишарпу - нет?
Здравствуйте, Serginio1, Вы писали:
S>На самом деле как ты сам видишь .Net движется к ускорению и упрощению взаимодействия с нативом
Движется к более безопасному и более удобному такому общению.
Де-факто ни одной новой задачи нововведения языка в этой области решать не позволяют, они позволяют более удобно решать старые задачи.
Re[55]: MS забило на дотнет. Питону - да, сишарпу - нет?
Здравствуйте, Serginio1, Вы писали: S> Пусть лучше будет копирование, но в одном процессе как в Сингулярити https://www.rsdn.org/article/singularity/singularity.xml S>Что бы те же EF, Linq2DB работали на SQL сервере! На Java что то есть для оракула, но как то не особо об этом говорят.
В SQL Server дотнет можно исполнять уже больше 15 лет, примерно так же, как и Java в Oracle.
И об этом тоже не особо говорят. И вообще это место не трогают. Я даже не в курсе, какой рантайм там запускается — когда это анонсили, остановились на возможностях C# 1.0 (в частности, там не поддерживаются генерик-типы).
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[56]: MS забило на дотнет. Питону - да, сишарпу - нет?
Здравствуйте, vdimas, Вы писали:
V>Здравствуйте, Serginio1, Вы писали:
S>>На самом деле как ты сам видишь .Net движется к ускорению и упрощению взаимодействия с нативом
V>Движется к более безопасному и более удобному такому общению. V>Де-факто ни одной новой задачи нововведения языка в этой области решать не позволяют, они позволяют более удобно решать старые задачи.
А какие новые задачи? Кстати всегда можешь им написать свои хотелки и чем больше людей их поддержат, тем скорее их реализуют
и солнце б утром не вставало, когда бы не было меня
Re[60]: MS забило на дотнет. Питону - да, сишарпу - нет?
Здравствуйте, Ikemefula, Вы писали:
НС>>Нет, не телепатия, а намек на то что ты не прочел то, на что отвечал. Речь шла про необходимость боксинга при выводе данных в гуй, I>Изначально речь была про чтение бд и "если это значение передадут GUI-таблице"
Ну я ж говорю, то ли не читал, то ли не понял что написано:
Для джавы боксинг/анбоксинг немного пофик с её espace-анализом.
Зато дотнету не пофик.
Т.е., происходящее в джаве затем зависит от дальнейшего использования полученного значения на вызывающей стороне.
Например, если это значение передадут GUI-таблице, то как раз надо передавать Object, то бишь Integer.
Если нет, т.е., если происходят числовые вычисления над полученным значением, то escape-анализ потенциально может убрать ненужное боксирование.
Т.е., даже в случае дотнетной идеальной безбоксированной реализации надо было бы тоже боксировать для GUI, приводя к Object.
I>А я добавил, что в определенных кейсах твои утверждения не работают и прямо написал, в каких именно.
Кейс был подробно расписан. Ты придумал другой кейс и поспорил с ним. У Чапека это называется имаго.
>> потому что контролы и биндинг в гуе работает с обжектами. I>Вот это и есть проблема, что тупенький GUI работает с обжектами.
Есть пример биндинга в гуе, где не с обжектами?
I> И при большом объеме данных, которые затягиваются в такой GUI
А не надо затягивать в гуй большой объем данных, человек все равно много данных за раз не сможет воспринять.
I>, это дает конские издержки. Вместо мелкой операции в пересчете на объект получаем конские издержки на боксинг-анбоксинг
Издержки на боксинг, если мы говорим про гуй, совсем не конские. Конские они когда тебе гигабайты перемолотить надо. А в гуе на фоне отработки лейаута и рендеринга шрифтов боксинг не разглядеть и в микроскоп.
I>Я ж тебе не зря про САПР сказал. Допустим, тебе надо отобразить модель и результат её работы на конкретной выборке. Выборка >1gb. Её рисовать как раз не надо. Но вот загрузить — да, надо, те части, которые изменились с момента последней операции.
Это не имеет никакого отношения к биндингу рекордсетов на контролы. Соответственно и проблемы боксинга при этом никакой нет.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[62]: MS забило на дотнет. Питону - да, сишарпу - нет?
Здравствуйте, vdimas, Вы писали:
I>>Если обсуждаются лидеры рынка, то это целиком статистика.
V>Статистика чего именно, если речь шла о доступности/развитости ср-в рефакторинга?
Доступность/развитость это все статистика, а не твой личный топ.
I>>У тебя же вместо статистики "я дал ему третье место" V>И вполне заслуженно.
Ты всерьёз не понял, что речь была не про твой личный топ ?
Re[61]: MS забило на дотнет. Питону - да, сишарпу - нет?
Здравствуйте, Ночной Смотрящий, Вы писали:
I>>А я добавил, что в определенных кейсах твои утверждения не работают и прямо написал, в каких именно.
НС>Кейс был подробно расписан. Ты придумал другой кейс и поспорил с ним. У Чапека это называется имаго.
Когда ты начинаешь сливаться, то всегда притягиваешь этот мем с имаго. Так, наблюдения.
"кейс был подробно расписан". Давай смотреть вместе. Вот собтсвенно это описание "Боксирование же нескольких десятков строк в".
Ранее вы не оговаривали объемы данных, просто вдруг возник вопрос стоимости боксинга.
То есть, ты просто от балды по ходу беседы вбросил, что де будет именно несколько десятков строк.
А я тебе говорю, что может быть и на порядки больше данных, и тогда боксинг станет существенным препятствием.
I>>Вот это и есть проблема, что тупенький GUI работает с обжектами. НС>Есть пример биндинга в гуе, где не с обжектами?
Да успокойся — не надо хранить инт, нужно хранить акцессор, один на колонку
I>> И при большом объеме данных, которые затягиваются в такой GUI
НС>А не надо затягивать в гуй большой объем данных, человек все равно много данных за раз не сможет воспринять.
Похоже, ты про САПР только слышал.
I>>, это дает конские издержки. Вместо мелкой операции в пересчете на объект получаем конские издержки на боксинг-анбоксинг
НС>Издержки на боксинг, если мы говорим про гуй, совсем не конские. Конские они когда тебе гигабайты перемолотить надо.
Цитирую себя: "Выборка >1gb." Ты реально что ли не читая строчишь?
>А в гуе на фоне отработки лейаута и рендеринга шрифтов боксинг не разглядеть и в микроскоп.
Профайлер с тобой не согласен. Ты извини, но САПР я занимался 12 лет на дотнете и С++.
Очевидно, что обработать придется каждый экземпляр данных, а вот будет ли каждый нарисован буквально — совсем необязательно. Более того, очевидно, что каждый подписывать точно нет необходимости.
I>>Я ж тебе не зря про САПР сказал. Допустим, тебе надо отобразить модель и результат её работы на конкретной выборке. Выборка >1gb. Её рисовать как раз не надо. Но вот загрузить — да, надо, те части, которые изменились с момента последней операции.
НС>Это не имеет никакого отношения к биндингу рекордсетов на контролы. Соответственно и проблемы боксинга при этом никакой нет.
Ты всё про тупенькие контролы А я тебе говорю про точно такие же, но которые без боксинга работают.
Условно, тебе нужен акцессор на колонку. Боксинг — это дешовый способ, вместо акцессора у нас боксовый объект, который мы дергаем непойми как. Потому я и называю такой UI тупеньким.
То есть, у тебя ссылка на список структур, и список акцессоров. Больше ничего для того же грида не нужно, вообще.
Re[47]: MS забило на дотнет. Питону - да, сишарпу - нет?
Здравствуйте, Sinclair, Вы писали:
V>>Структуру можно вернуть и по-значению. V>>Унутре вызывающая сторона подготавливает место под структуру и передаёт ссылку на это место в кач-ве аргумента. S>Так по ссылке или по значению?
А как, по-твоему, возращается по-значению структура, размер которой больше ширины регистра?
Для инфы, в этом сценарии существует т.н. return value optimization (гугл).
public unsafe struct Struct1 {
internal int _someField;
internal fixed int inplaceArray[1];
// ошибка компиляции, как и ожидалось
// error CS8170: Struct members cannot return 'this' or other instance members by referencepublic ref int SomeField => ref _someField;
// нет ошибки компиляции, хотя ожидаетсяpublic ref int this[int index] => ref inplaceArray[index];
}
public static unsafe class Struct1Ext {
public static ref int Item(ref this Struct1 @this, int index) => ref @this.inplaceArray[index];
}
internal class Class1 {
public readonly Struct1 field;
}
internal class Program {
private static void Main(string[] args) {
var c = new Class1();
// ошибка компиляции, как и ожидалось
// error CS0192: A readonly field cannot be used as a ref or out value (except in a constructor)ref var ptr2 = ref c.field.Item(0);
// нет ошибки компиляции, хотя ожидаетсяref var ptr1 = ref c.field[0];
var areSame = Unsafe.AreSame(ref ptr1, ref ptr2);
}
}
Ключевое — readonly Struct1 field.
Надо проверить в 10-м C# и зарепортить, если еще не починили.
V>>В случае flexible structs всё-равно надо создать Span от inplace-массива с "вручную" отмеренным нужным размером, т.к. при создании Span никаких проверок не делается. S>Проверки делаются при доступе к структуре, что позволяет передавать её в гражданский код.
В любых flexible-структурах фактическая длина известна после получения данных из некоторого АПИ.
Некий публичный метод или св-во могли бы возвращать Span.
1. Дотнет не гарантирует фактический порядок расположение полей, необходимо задать StructLayout хотя бы Sequential
2. Продолжение того же бага:
[StructLayout(LayoutKind.Sequential)]
public struct IntArray {
private int _length;
private int _data0;
// нет ошибки компиляции, хотя ожидаетсяpublic ref int this[int index] => ref MemoryMarshal.CreateSpan(ref _data0, Length)[index];
public int Length => _length;
}
[StructLayout(LayoutKind.Sequential)]
public struct IntArray2 {
private int _length;
private int _data0;
// ошибка компиляции, как и ожидалось
// error CS8347: Cannot use a result of 'Unsafe.Add<int>(ref int, int)' in this context because it may expose variables referenced by parameter 'source' outside of their declaration scopepublic ref int this[int index] => ref Unsafe.Add(ref _data0, Length);
public int Length => _length;
}
[StructLayout(LayoutKind.Sequential)]
public unsafe struct IntArray4 {
private int _length;
private fixed int _data0[1];
// нет ошибки компиляции, хотя ожидаетсяpublic ref int this[int index] => ref Unsafe.Add(ref _data0[0], index);
public int Length => _length;
}
Суть бага в следующем:
internal class С2 {
private ref int Bar2() {
var value = 0;
// ошибка компиляции, как и ожидалось
// error CS8168: Cannot return local 'value' by reference because it is not a ref localreturn ref value;
}
private ref int Bar3()
{
var value = 0;
ref var valueRef = ref value;
// ошибка компиляции, как и ожидалось
// error CS8157: Cannot return 'valueRef' by reference because it was initialized to a value that cannot be returned by referencereturn ref valueRef;
}
private unsafe struct SomeStruct {
public fixed int inplaceArray[42];
}
private unsafe ref int Foo() {
var s = new SomeStruct();
// нет ошибки компиляции, хотя ожидаетсяreturn ref s.inplaceArray[0];
}
}
V>>Для интеропа не подойдёт, т.к. сломает разметку ожидаемой структуры. S>Вот тут не понял.
Мне показалось, что ты хотел сделать Span как часть описания структуры.
В любом случае, Span лучше использовать в дизайне подобных объектов так:
[StructLayout(LayoutKind.Sequential)]
public unsafe struct IntArray3
{
private int _length;
private fixed int _data0[1];
// нет ошибки компиляции, хотя ожидаетсяpublic Span<int> AsSpan() => MemoryMarshal.CreateSpan(ref _data0[0], Length);
public int Length => _length;
}
Тогда for-циклы по полному Span-у соптимизируют одну проверку за выход за диапазон, как это делается для обычных массивов.
V>>Возможно. V>>TCPDirect дают примерно 20ns задержку от прихода пакета в сетевую карточку, поток тупо в цикле опрашивает диспетчер. V>>Но вызов туда нельзя помечать SuppressGCTransition, т.к. в этом вызове иногда может быть и вызов ядра (как повезёт). S>Как я понял из их доки, почти все вызовы там (включая поллинг) — неблокирующие, поэтому можно.
Неблокирующий вызов не означает не использование примитивов синхронзации, а там сказано:
* Not manipulate locks or other concurrency primitives
V>>Надо переносить в дотнет приличную часть драйвера — всё что касается набивки буфера команд. S>На первый взгляд, там всё должно быть достаточно примитивно.
Ес-но.
Стиль "черного ящика" в плане подробностей АПИ используют, чтобы не вешать себе гири на ноги в деле совместимости.
Т.е., чтобы иметь возможность что-то менять унутре в любой следующей версии.
S>>>Если у нас какая-то прямо жёсткая зависимость от внешнего вызова, но их много и они занимают мало времени на анменеджед стороне — PInvoke вовсе не так уж плох. Всего лишь втрое дороже call EAX. V>>В 17 раз дороже. S> Бенчмарк с вами не согласен. Я же привёл результаты. В 10 раз дороже — если нет SuppressGCTransition, и в три раза, если он есть.
Если есть, то ОК.
S>17 раз нет ни в каком варианте.
На моей машине в 17 раз.
И у тебя там замеры вообще странные были, если по твоим замерам простой вызов выходил 7 ns.
S>>>А вот в сервер-сайд у нас приложения работают неделями. V>>Но при обновлении должны будут тормозить несколько первых минут? S>При рестарте.
Т.е., при каждом обновлении.
V>>Я не вижу причин, по которым сервер-сайд должен отказываться от АОТ. V>>Т.е. вообще не вижу. S>Я тоже не вижу причин отказываться от AOT, но только в том случае, если он собирается работать совместно c хот-споттингом. S>Если же поставить выбор между АОТ и возможностью динамического кода + хотспот, то я выберу второе.
Выглядит так, что у тебя своеобразные представления как о возможностях AOT, так и о возможностях hot-spot оптимизаций.
V>>Боюсь, вычислительная сложность сериализации в XML такая, что никакой оптимизатор инлайнить это не будет. V>>У оптимизаторов в любом случае стоят пороги срабатывания от сложности методов, в т.ч. транзитивной/вложенной сложности. S>Ну, так это как раз потому, что у оптимизаторов ограничено время работы и нет информации о статистике выполнения.
У оффлайновых оптимизаторов время работы не ограничено, ограничен баланс м/у размером кода и оптимизацией.
Зато у хот-спот оптимизатора время ограничено.
V>>Скорее, оптимизатор заинлайнит что-то в кишках XML-сериализатора, но верхние уровни пойдут как есть. S>Ну, в нашем случае мы написали высокопроизводительный XML-сериализатор на $"<flightData><flightNo>{Escape(d.GetString(0))}</flightNo><departureDate>{d.GetDate(1).ToString(r)}</departureDate></flightData>"
Вручную писаный сериализатор будет оптимизирован и в АОТ, в отличие от обещприкладного, который и со схемами общается, и биндинг динамически внутри строит и что только не делает.
V>>Современные джавовские хот-спот оптимизаторы имеют еще большие ограничения на сложность, чем нейтивные оптимизаторы. V>>Т.е. на таком верхнем уровне вряд ли что-то будет сделано. S>Это был умозрительный пример. На каком бы уровне мы ни работали, в ООП-коде всегда есть какой-то косвенный вызов.
Верно, АОТ потенциально способно убирать лишнюю косвенность.
Например, "выпрямлять" в памяти банальный List<T>.
S>И всегда есть статистика реального исполнения этого кода, которая меняется от инсталляции к инсталляции и даже в процессе работы одной и той же инсталляции.
Статистика в основном нужна для определения мест, где требуется оптимизация.
АОТ тупо может оптимизировать всё подряд.
И да, атрибуты оптимизации над методами никто не отменял.
S>>>При этом, опять же, после инлайна кода у нас появляется статистика о том, какие именно запросы делаются в dataReader. И, соответственно, возможность ещё что-то проинлайнить, выбросив лишние ветки if вместе с косвенными вызовами.
V>>Оптимизации выполняются на уровне конкретного типа. V>>Плохо представляю себе приложение, где в DataReader в любой точке приложения приходит одна и та же схема данных. )) S>Ну так в том-то и дело: пока мы делаем косвенный вызов метода DataReader, в call target мы вынуждены обрабатывать общий случай. Ведь VMT для всех пользователей этого DataReader одинаковая. S>А вот когда мы встроили тело DataReader.GetDate(x) в конкретное место вызова, то внезапно оказывается, что для данной конкретной копии кода исходного GetDate в конкретном if/switch чаще всего (== всегда) попадается ровно один вариант. Ведь конкретно в этом месте всегда используется одна и та же схема данных.
Угу.
В рекордсете из 1001 элемента первые 1000 будут прочитаны без оптимизации.
Затем будет выполнена дорогостоящая оптимизация и последний элемент прочитают оптимальным образом.
Утрирую, но суть понятна.
У тебя в "одном и том же месте" каждый раз будут разные экземпляры DataReader и потенциально хотя бы немного отличающиеся схемы, хотя бы в плане nullable-полей, особенно если со стороны базы позвали какой-нить union. И даже если схемы будут те же — это будут другие экземпляры схем, вот в чём прикол.
S>Вроде бы не такая сложная конструкция, чего тут не понять? Погуглите любую статью со словами speculative inlining.
Который в той же JS-машинке требует предварительной проверки соответствия устройства объекта ожидаемому и работает с некоторым ограничением вложенности иерархии такой проверки, т.е. в относительно простых случаях.
В случае DataReader в нынешнем виде не прокатит — слишком большая иерархия объектов, всю её проверять на соответствие текущему хот-спот-коду будет накладней получаемых от хот-спот плюшек.
Надо упрощать архитектуру объектов.
Делать таблицу диспетчеризации, как я показывал.
Тогда динамически (по индексу) будет обращение только к строке такой таблицы, а столбец известен статически.
Тогда проверка соответствия лейаута объекта будет сводиться к проверке только ссылок на метаинформацию столбцов рекодрсета, где эти столбцы ссылаются на одни и те же статические-заготовленные конвертеры.
Т.е. сравни — проверить только равенство ссылок в объекте верхнего уровня или рекурсивно пройтись по кучерявым объектам, проверяя равенство всех полей всех дочерних элементов.
Счётчик сложности в последнем случае скажет "извините, в другой раз".
V>>Для DataReader "типы" полей динамические, зависят от схемы принятого рекордсета. V>>Тут и С++ ничего не сделает. S>Про С++ ничего сказать не могу. Способен ли он выполнять PGO-оптимизацию уже PGO-оптимизированного кода? А для хотспота это, насколько я знаю, норма.
"Норма" там в простейших случаях.
Я разбирал примеры работы джавовского хот-спота — покрывает только тривиальнейшие случаи и уже даже про них трубят как о победе.
V>>Надо тупо писать эффективный код. V>>И в то же самое время читабельный и поддерживаемый. )) S>Ну, так эти две цели в известной мере друг другу противоречат.
Распространённая ошибка.
Я показал устройство таблицы конвертеров в сообщении, на которое уже давал ссылку.
Читабельность и поддерживаемость (то бишь расширяемость) прекрасная.
А в варианте с новыми указателями на ф-ии — еще и максимально-эффективная, т.е. даже, грубо, на асме или IL быстрее не сделаешь.
И хотспоту проще ввиду низкой вычислительной сложности, т.е. мог бы заинлайнить тела вызываемых по указателю конвертеров, т.е. убрать косвенный вызов.
ОК, про гипотетический хотспот пока спорить не буду, по крайней мере пока его нет в природе для .Net.
V>>Всю эту кодогенерацию, которую ты показывал для linq, можно было выполнить и для AOT. S>Ну, это всего лишь небольшой маленький пример. Реальные задачи не исчерпываются linq выражениями. V>>Просто тут нужно соотв. плагинное АПИ к АОТ. V>>Т.е., AOT даёт тебе конкретные типы, т.к. в закрытой системе типов они известны (даже если заведомо абстрактны — это тоже известно), а "плагин" генерит код, который опять же компиляется AOT. S>В теории — интересно. На практике — где посмотреть на такой AOT?
Нигде.
Даже существующий полноценный доступен только для iOS на основе mono (ХЗ какого он там качества).
А виндовые UWP-приложения на .Net Core UWP компиляются серверами магазина Windows под сетку устройств, тот код публично недоступен.
V>>2. В обычных компиллируемых как минимум бета-редуцируемых языках (шаблоны С++ в пример) через генерирование двух версий — для проверенного и непроверенного констрейна. S>Не двух, а 2количество констреинтов.
1. Так и есть, порой шаблоны порождают сетку-произведение воплощённых реализаций.
2. Опять включается счётчик сложности в случае хот-спота.
V>>Даже в языках с зависимыми типами всё-равно в какой-то точке программы идёт проверка и ветвление/диспетчеризация кода. V>>Так же и в твоём примере с SQL-сервером, для данной сущности берётся одна из готовых реализаций, соотв. констрейнам этой сущности. S>Нет. Просто при построении плана учитывается текущая статистика и актуальные метаданные.
Ес-но.
И затем берется одна из уже готовых реализаций способа скана.
В этом суть.
S>Напомню: в прошлый раз дискуссия про "повторное использование компонентов плана выполнения запроса" кончилась тем, что вы тихо слились, как только дело дошло до кода.
Я помню обратное — ты так и не прошёл понимание про повторно-используемые "кубики" при построении плана запроса.
S>И в этот раз будет то же самое.
Псевдокод давался.
V>>То бишь, у тебя может быть уже сетка неких алгоритмов под разные сценарии. При изменении сценария ты подставляешь указатель на нужный алгоритм. S>Ага. Размер этой сетки для баз данных быстро начинает превышать разумные пределы. Как, впрочем, и для любых комбинаторных задач.
Это для динамических вещей.
Для статических еще в момент компиляции известны наличествующие индексы.
Поэтому, комбинаторика будет только по различиям в статистике, где для многих типов индексов, которые я обозвал enum (разновидность справочных данных эдакого системного плана, т.е. которые жестко привязаны к версии приложения) — статистика известна на момент компиляции. То бишь, кол-во уникальных значений в индексе, характер этих значений (подряд или разрежёнными "островками" и т.д.).
V>>Хотспот еще более хрупкий и непредсказуемый. S> Пока что он демонстрирует впечатляющие результаты
Увы.
Чтобы он демонстрировал "впечатляющие результаты" в нашей области, например, ребята раскладывают объекты на составяющие ручками.
Т.е., вместо
class SomeClass { int a, b, c; };
SomeClass c1 = new SomeClass[42];
У них идёт:
int[] aFields = new int[42];
int[] bFields = new int[42];
int[] cFields = new int[42];
А в самых критических случаях и вовсе:
class SomeClassHelper {
const int FIELD_COUNT = 3;
const int FIELD_A_OFFSET = 0;
const int FIELD_B_OFFSET = 1;
const int FIELD_C_OFFSET = 2;
public void write(int[] array, int index, int a, int b, int c) {
index = index * FIELD_COUNT;
array[index + FIELD_A_OFFSET] = a;
...
}
int[] data = ...
SomeClassHelper.Write(data, index, a, b, c);
Смысл примерно должен быть понятен.
А если поля разных типов (по ширине разных), то всё еще забавнее происходит над массивом байт.
S>И вытягивает её как раз JIT и хотспот.
Вытягивают её ручками, помогая хот-споту.
Сам понимаешь, поддерживаемость ТАКОГО кода ни идёт ни в какое сравнение с предложенной мною микро-оптимизацией на основе таблицы диспетчеризации конвертеров.
V>Вручную писаный сериализатор будет оптимизирован и в АОТ, в отличие от обещприкладного, который и со схемами общается, и биндинг динамически внутри строит и что только не делает.
Кстати Try the new System.Text.Json source generator
Начали вовсю уже использовать source generator для оптимизации и уменьшения кодогенерации которую легко использовать и для АОТ
и солнце б утром не вставало, когда бы не было меня