Здравствуйте, Sinclair, Вы писали:
V>>- они могут располагаться только на стеке;
V>>- не могут быть аргументами генериков.
S>Не могут реализовывать интерфейсы.
Это следствие невозможности боксирования.
Хотя, странное ограничение.
ИМХО, правильней было бы просто запретить боксирование таких типов.
Вплоть до
where T : ISomeAPI, ref struct
S>Не могут использоваться в async методах или блоках итераторов.
Следствие невозможности располагаться в полях не ref-struct типов.
V>>Оба случая хорошо подходят под интероп.
S>Ну, получается их область действия ограничена тонким слоем интеропа, а общаться с прикладным кодом надо уже через классы или классические структуры.
Не обязательно ограничена интеропом, просто хорошо подходит как доп. искуственно-вводимые разработчиком ограничения через систему типов.
Для пущей безопасности, кароч, чтобы временные ссылки никуда случайно не утекли.
V>>Такой же stackalloc начинается.
S>О, это интересно. То есть вы размечаете "структуру" в стеке как серию stackalloc? А есть гарантии их взаимного расположения?
Случаев сразу несколько.
Раньше был доступен stackalloc только под примитивные blit-типы.
Например, делаешь
byte * b = stackallock byte[N], затем приводишь к указателю на структуру.
Потом стало можно привести к управляемой ref-ссылке на структуру через CompilerServices.Unsafe.
В последней версии C# можно делать stackalloc пользовательских структур, которые вдоль всей иерархии агрегации состоят или из простых типов или структур, состоящих из простых типов.
Т.е. структуры, попадающие под ограничение unmanaged.
В т.ч. в теле которых располагаются inplace-массивы примитивных типов или тоже unmanaged структур.
V>>Чтобы GC игнорировал стек нейтивных вызовов.
V>>Т.е., возможна зебра вглубь всех вызовов:
V>>- из управляемого кода в нейтив;
V>>- оттуда приходит колбэк в управляемый код (многие перечисления в системных АПИ так работают);
V>>- из этого фрейма опять вызывается что-то в нейтиве.
S>Ну в вашем-то случае весь метод — это инкремент лонга по указателю. Никаких колбэков, никаких зебр.
У меня просто бенчмарк различных способов вызова ф-ий.
А ты спрашивал, зачем замыкается стек перед вызовом нейтивной функциональности.
А как GC должен знать, где в стеке управляемые данные, а где игнорить?
V>>Дотнетный дебаггер при отладке, кстате, показывает места, где участки нейтивного стека пропущены.
S>Ну, это если у нас реально тяжёлый нативный метод — долго работает и/или делает колбэки в менеджед.
Всегда показывает, если такой участок на стеке есть.
S>В первом случае микросекунды задержки нам неважны; во втором случае такой метод просится на перетаскивание в менеджед.
Стек замыкается в любом случае, т.к. GC stop world никто не отменял.
V>>Tiered compilation и есть hotspot.
S>Ну это очень вольная трактовка.
Согласно терминологии.
S>Формально — да, но с тех пор, как появилась сама идея "оптимизируем только узкие места", её стандартная трактовка расширилась до "runtime profile-based dynamic optimization".
Хот-спот (от англ. hot spot — «горячая точка»)
S>То есть "учитываем счётчик вызовов для применения tier1" — это очень, очень примитивный уровень. Чтобы, к примеру, заработал спекулятивный инлайнинг, нужно:
S>- прикрутить инструментирование кода для определения частотности call targets
Да.
Одно "но" — интрументы прикручиваются к тормознутой реализации.
Т.е., разумеется, чтобы где-то прибавить, надо где-то отнять. ))
Это почему у нас, например, очень осторожно относятся к введению новой, "умной" и т.д. функциональности, бо всё это не бесплатно.
Основная стратегия — оптимизировать надо самые горячие пути исполнения.
S>- прикрутить умение JIT-тить инлайн ожидаемого таргета с guard condition
Ага, лишние приседания/тормоза.
Всё так.
S>- прикрутить умение откатывать оптимизацию
Этого нету.
Оптимизировали — и с концами.
AppDomain тоже больше нет.
S>Это одно стоит в разы больше, чем, собственно, tiered compilation.
Некоторые простые вещи инлайнят/оптимизируют сразу.
Заходят на повторную оптимизацию только относительно сложных методов.
И да, практически перестала работать опция метода AggressiveOptimization в случае tiered compilation, т.е., что с этой опцией что без происходящее примерно одинаково.
S>А ведь помимо этого в настоящем хотспоте (т.е. в profile guided optimization) ещё много чего прикручено.
Средней руки приложение может компиляться под AOT iOS несколько минут, сколько-нибудь большое — десятки минут.
Поэтому, всё это херня собачья, гнаться за хотспотом.
Над разрабывать либы так, чтобы они были готовы к нейтивной компиляции потом, а клиент при публикации своего приложения пусть запускает АОТ-оптимизатор.
Там минимальный hello world получается под 5 мег бинаря, а какое-нить среднее приложение 5.5 мег. ))
V>>В моём примере с IConfig до появления Tiered compilation джит сразу генерил свой оптимизированный, но глупый код.
V>>Теперь этот код надо исполнять сколько-то раз в цикле, чтобы джит его соптимизировал, иначе там 1-в-1 с исходником.
S>Ну, именно поэтому BDN имеет фазу прогрева
Этим можно управлять в настройках проекта.
Просто изменилось дефолтное значение.
Т.е. tiered compilation можно и порой даже нужно отключать.
V>>Средней величины дотнетное приложение до него запускалось по 600-800 ms, сейчас примерно вдвое быстрее.
S>Ну почему же "ничего более". Прямо в анонсе показывали ускорение steady state, местами почти двукратное.
Ну да, примерно двухкратное.
V>>Особенно чувствительны к этой технологии те приложения, где инициализируется много статических данных (например, WPF и прочие, где описаны мильоны dependency property).
V>>Cтоимость джита инициализирующего кода была в разы больше стоимости его исполнения, а код-то одноразовый.
S>Возможно. Я WPF приложения никогда не видел
Ну, у нас зато тоже в некоторых проектах тонны статического-справочного кода, вот на такх проектах разница видна хорошо.
И что характерно — если эти "справочные" данные бинарно сериализовать (не встроенной бинарной сериализацией, которой теперь тоже нет в .Net Core, а просто ручками), сохранить в ресурсах, а потом при старте прочесть — это занимает примерно 30 ms. А в исходном виде добавляло примерно 400 ms.
V>>Спасёт только качественный AOT.
S>Это для десктопа. А для любимого мной бэкенда важен именно хотспот, т.к. никакой AOT не предскажет реальную нагрузку.
А толку, если нагрузка может меняться?
В нейтиве для этого отродясь было PGO.
К АОТ его тоже надо прикручивать.
Хотя, АОТ хорош тем, что не рассматривает типы как открытые, хотя бы по виртуальности/наследованию.
Плюс, у него достаточно времени на всевозможные оптимизации.
Т.е. и дефолтная оптимизация может много чего дать.
Просто непонятно — когда это всё будет в наличии! ))
V>>К нашей пенсии всё будет ОК, не переживай. ))
S> А вот я уверен, что и тогда будет к чему стремиться.
Стремиться всегда есть к чему, речь пока идёт о том, чтобы иметь хоть что-то из наработанного индустрией в дотнете.
V>>В общем, там несравнимые объемы и сложность кода, имелось ввиду это.
V>>Если тебе Рослин кажется сложным проектом — ты сильно заблуждаешься.
V>>Рослин простой как балалайка, хотя и достаточно объемный.
S>Ну так его пилили не столько лет, сколько GCC
GCC активно развивался в эти годы, под стандарт C++0x, 14, 17.
(за пару лет до каждого из стандартов, т.е. вычитай года)
Изменения в С++ произошли чудовищные.
Помимо этого GCC неплохо освоил оптимизацию в этот же период.
Т.е. не важно, сколько лет было GCC до этого.
V>>Например, тот же пример с SomeClass<Config1> c1 = ...; c.Foo();
V>>Если все типы объявлены в одной сборке, там нефик делать соптимизировать лишнее компилятором.
S>Да пусть хотя бы свой dup для value типов уберут А то смешно сказать — два совершенно семантически эквивалентных куска С# кода порождают совершенно разный бинарь. Ну куда это годится?
dup тут не при чём, при чём его пережёвывание джитом.
V>>Аналогично куча строковых вычислений и прочих, особенно при инициализации — компилятор честным образом отдаёт это всё в рантайм, хотя там до половины и более зачастую вычислимы в compile time.
S>Как раз простые строковые вычисления компилятор делает сразу.
Не, если вызываются простейшие методы — не делает.
В С++ делает через constexpr.
https://www.cppstories.com/2021/constexpr-new-cpp20/
Т.е., часть программы выполняется прямо в compile-time.
Очень удобная была техника на Forth, помнится.
V>>Основное при разработке сложных проектов — хорошо понимать собственный код.
V>>Итеративность в этом смысле привносит оперативность.
V>>Т.е., в чём-то больше работы, да, но в целом происходящее оперативнее, т.к. некоторые бока/недоработки нового кода раньше вскрываются.
S>Ну о какой итеративности можно тут говорить? Они же не могли выпустить "рослин шарп 1.0" без генериков и прочих плюшек, и потом итеративно его деливерить.
Могли.
Просто это был бы не Рослин, а портированный на шарп плюсовый компилятор.
По моему опыту, портирование кода из С++ в дотнет тривиальное (это обратно нетривиально), у меня получалось портировать примерно 2 тыс строк в день.
Если хотя бы 4-5 человек навалятся, первый шарповый компилятор на шарпе можно было бы получить примерно за неделю.
Затем итеративный рефаткоринг и добавление функциональности.
В момент, когда устаканились типы AST и емиттер байт-кода из этого AST до возможности опубликования их публичного АПИ — вот тебе первая версия Рослина, т.е. компилятор как сервис.
Ну и дальше больше.
Если там больше одного разработчика (хотя бы 4-5) это гораздо меньше 9 месяцев на первую итерацию.
Просто они вообще с 0-ля его писали.
Наглухо.
Похоже, еще и учились писать компилятор в процессе, бгг...
Заодно похерили ранее сделанные вложения в уже имеющийся компилятор.
Детсад, штаны на лямках.
S>Внутри проекта, понятное дело, были какие-то майлстоуны и дедлайны, но снаружи всё это не могло быть наблюдаемо.
Думаю, все дедлайны просрали примерно на 3 года.
Потому что сначала речь была о полуторе-двух годах.
V>>И не переубедишь. ))
S>Ну, может вам в Теслу пойти? А то с анонса автопилота прошло уже существенно больше 18 месяцев, а он всё ещё не ездит.
Автопилот — новая технология, а здесь ничего нового от слова вообще.
Такое ощущение, что разработка Рослина шла со скоростью приобретения компетенции исполнителями.
Я же почитывал Липперта — он нифига не спец в компиляторостроении.
Это отдельная дисциплина, не бог весть какая сложная, но в ней надо шарить...
Желательно, до начала разработки...
А эти гаврики с шашкой на танк помчались.
Результат известен.
Я же видел исходники плюсового опенсорсного компилятора C# — классика LR(1), таблица, правда, с некоторыми с хуками, но это понятно, под чистый LR(1) мало какой менстримовый язык войдёт.
В любом случае, такой компилятор пишется единицы месяцев, если человек знает, что такое LR.
Отделите машинку-парсер от AST, пробуйте потом независимо различные техники парсинга (я топлю за GLR, бо он кушает любую грамматику, даже многозначную) и т.д.
PEG неплохо заходит для "ручных" парсеров и т.д.