Re[36]: MS забило на дотнет. Питону - да, сишарпу - нет?
От: Sinclair Россия https://github.com/evilguest/
Дата: 02.09.21 03:06
Оценка:
Здравствуйте, vdimas, Вы писали:

V>В C# можно пользоваться тем, что если до вызова external, помеченного DllImport, дело не доходит, то этого кода как бы и нет.

Можно, но тогда нужно бранчиться в вызывающем коде, чтобы выбрать правильный DllImport.

V>В этом случае приходится принимать решение, где у нас будет абстракция.

V>Идеально будет воткнуться туда, где уже есть виртуальные вызовы, а не создавать +1 виртуальный слой.
V>(по крайней мере для нашей специфики это так — лишний виртуальный вызов играет рояль)
А что у вас за специфика? Всё ещё непонятно, где же нужно так много микроскопических переходов менеджед/натив.
V>В итоге обыгрывание кроссплатформенности не выходит достаточно "точечным". субъективным опытом делимся...


V>Структуру АПИ можно сделать классом в C# и подавать по обычной ссылке, а можно сделать value-типом и подавать через ref, а можно сделать ref struct, т.е. ограничить время жизни на стеке. А можно просто IntPtr и заниматься сериализацией-десериализацией.

По-моему, нормальный вариант — только один. Если API принимает struct, то мы и отдаём struct.
ref struct нужен для очень специальных сценариев.

V>Та да.

V>Особенно весело, когда эти описания отличаются для разных сборок Linux, например, в случае select — зависит от параметра ядра max handles, т.е. размер структуры fd_set бывает разный.
Ну, вот за это мы не очень любим линукс
К сожалению, нормального способа работы со структурами переменного размера в дотнете/С# я не вижу.
То есть можно сделать враппер вокруг byte[], который работает при помощи MemoryMarshal, и даже приемлемо по эффективности (после JIT все эти обращения к MemoryMarhshal.Cast<byte, IntPtr>(data.AsSpan().Slice(sizeof(int))[index] превращаются в in-place инструкции типа LEA), но код выглядит, мягко говоря, нечитаемым.

V>Дотнет пытается дать высокоуровневое "всё изкаробки".

V>Т.е., все эти вещи так или иначе связаны с асинхронным режимом работы, вот он и даёт.
V>Сейчас постепенно расползается по АПИ системных библиотек 3-е поколение асинхроннщины — на основе ValueTask.
V>Т.е. эволюция была такой — BeginOp/EndOp, Task, ValueTask.
Да дело даже не в этом. В платформе АПИ объектно-ориентирован — есть множество "типов" хэндлов, которые могут обрабатываться единым образом.
В дотнете выбрана совершенно другая иерархия типов, из-за которой у сокета и файла нет ничего общего.
Поэтому крайне сложно реализовать библиотеку, которая бы, к примеру, умела ждать на разнородном наборе объектов. Даже не переходя к ValueTask и прочим нюансам управления асинхронщиной.

V>Идеально было бы сделать ValueTask как ref-struct, чтобы ограничить время жизни локальным scope, но тогда их невозможно будет использовать для захватываемых переменных async-автомата, т.е. для асинхронного кода, для которого ValueTask и предназначен, собсно.

Вот именно.

V>С другой стороны, этого и не требуется, т.к. м/у вхождениями в автомат работает не сам экземпляр ValueTask, а его awaiter, т.е. сохранять в машинке надо его, но проблема семантики ref struct остаётся — это надо будет сделать AsyncMethodBuilder совсем уж умным и что-то сделать с компилятором C#, чтобы дело вообще дошло до вызова своего AsyncMethodBuilder.

Ну, вообще движение в подобных направлениях есть. Недавно читал про реквест по расширению Linq query comprehension, чтобы можно было подсовывать свои реализации Queryable pattern.

V>В общем, туда как начинаешь вникать — всё за всё цепляется, принимать решения сложно, как всегда бывает, когда однозначно хорошего решения нет.

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

S>>Руки дойдут — посмотрю бенчмарки. Не должно там быть такого оверхеда — речь про единицы десятков ассемблерных инструкций.


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

V>Сравнить с вызовом managed-кода, когда просто в RAX загружается значение переменной и просто делается call RAX.
Ну по идее так и должно быть. Если маршалинга нет (а для достижения этого есть все предпосылки), то должно делаться именно это.


V>Простой пример:

V>
V>IConfig {
V>   bool SomeFlag1 { get; }
V>}

V>class SomeClass<TConfig> 
V>   where TConfig : struct, IConfig
V>{
V>    public void Foo() {
V>        if(default(TConfig).SomeFlag1) {
V>           // case 1
V>        } else {
V>           // case 2
V>        }
V>    }
V>}

V>struct Config1 : IConfig {
V>   public SomeFlag1 => true;
V>}

V>struct Config2 : IConfig {
V>   public SomeFlag1 => false;
V>}

V>...

V>SomeClass<Config1> c1;
V>c1.Foo();

V>SomeClass<Config1> c2;
V>c2.Foo();
V>


V>После работы JIT с оптимизацией изсчезает ветвление в методе Foo, т.к. ветвление происходит по константе, т.е. ассемблер показывает, что остаётся просто одна из веток.

V>Но, создание экземпляра Config1 длиной 1 байт происходит.

V>Причём, происходит в 2 этапа — сначала под него выделяется память и обнуляется.

V>Потом в эту память записывается 0 в результате вызова дефолтного конструктора.
V>Потом эта память нигде не используется, т.к. вызов св-ва SomeFlag1 JIT заинлайнил, соптимизировал и даже выкинул ненужную ветку if.
Нда, прикольно. Надо будет посмотреть на C# 10, где есть статические мемберы в интерфейсах. В нём можно убрать вот этот вот default(TConfig), и избежать вызова конструктора. Мне казалось, что одна из оптимизаций в пятом дотнете должна была убрать вызов конструктора для структур нулевого размера, но нет.


V>Кстате, кто-то сетовал, что в C# нельзя шаблоны генериков делать константами — вот, можно, через тип-"провайдер" таких констант.


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


V>(для воссоздания контекста — злейшим врагом современных программ являются лишние ветвления, т.к. они потенциально сбрасывают конвейер процессора)



S>>Именно там должна проводиться трассировка использования (вот эти вот все варнинги про value assigned to t is never used). У джита мало ресурсов и очень мало времени на то, чтобы отслеживать глобальные потоки данных.


V>Он не справляется даже в показанном простейшем случае.

V>Я периодически открываю issue-s в дотнете, но тут рука пока не поднялась, бо явно не самый нужный сценарий для отрасли, хотя был бы полезен конкретно мне. ))
Лучше открывать pull request

V>Сейчас прогресс именно языка виднее.

V>Разумеется, в т.ч. из-за вклада Липперта когда-то.
В основном — именно благодаря рослину. Тот компилятор, который был до него, не имело смысла опенсорсить — количество потенциальных контрибуторов туда было бы нулевым, особенно с учётом трудностей отладки.

V>Сравнил хрен с пальцем.

V>Хейльсберг тоже красавчег.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.