Информация об изменениях

Сообщение Re[41]: MS забило на дотнет. Питону - да, сишарпу - нет? от 03.09.2021 15:11

Изменено 03.09.2021 15:13 vdimas

Re[41]: MS забило на дотнет. Питону - да, сишарпу - нет?
Здравствуйте, 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 неплохо заходит для "ручных" парсеров и т.д.
Re[41]: MS забило на дотнет. Питону - да, сишарпу - нет?
Здравствуйте, 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 неплохо заходит для "ручных" парсеров и т.д.