Re[19]: MS забило на дотнет. Питону - да, сишарпу - нет?
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 05.09.21 14:16
Оценка:
Здравствуйте, vfedosov, Вы писали:

S>> Тут проблема в том, что нужно постоянно на нескольких языках программировать.

V>ИМХО это не проблема — это решение. Проблема в том, что спрос на языки меняется и то, что сегодня популярно, завтра может умереть. Поэтому надо выходить из зоны комфорта и писать на разных языках. Это как в той истории с мышкой: мышка, которая живет в сыре, должна постоянно принюхиваться — а не подгнивает ли сыр. Если подгнивает, то срочно нужно искать другой — иначе голодная смерть. А еще лучше всегда иметь другой сыр про запас.
Я как раз так и поступаю
Кроссплатформенное использование классов .Net в 1С через Native ВК. Или замена COM на Linux II
Здесь 3 языка.

CEF, ES6, Angular 2, TypeScript использование классов .Net Core. Создание кроссплатформенного GUI для .Net с помощью CEF
Здесь тоже. Но в реальности ты используешь 1 язык. И главное нужно некоторое время на переключение с одного языка на другой. Переход этот не мгновенный
и солнце б утром не вставало, когда бы не было меня
Отредактировано 05.09.2021 17:04 Serginio1 . Предыдущая версия .
Re[9]: MS забило на дотнет. Питону - да, сишарпу - нет?
От: Nuzhny Россия https://github.com/Nuzhny007
Дата: 05.09.21 17:03
Оценка:
Здравствуйте, vfedosov, Вы писали:


V>Ну OpenCV доступна на Python — собственно я с ней и работаю в основном. И я бы не сказал, что прям все переписывается на плюсы — иногда приходится — ИМХО где-то треть тасок с имаджпроцессингом приходится писать на плюсах. Иногда и ассемблер нужен — таких тасок процентов 5. Но не Java!. С какой стати на Java код будет работать быстрее?


В Питоне нет нормальной многопоточности. Поэтому пайплайн сложнее линейного на нем не сделать.

V>Насчет компактности OpenCV кода на плюсах тоже неправда. Пайтоновский как минимум не хуже — а ИМХО намного более компактен.


В том-то и дело, что нет. Он за,астую менее компактен, потому что питоновск й API беднее плюсового. А при необходимости ускорения или доработки кода из OpenCV, что время от времени надо делать, из плюсов это делается легко и естественно. Из Питона — гемор и приседания.
Re[10]: MS забило на дотнет. Питону - да, сишарпу - нет?
От: Ночной Смотрящий Россия  
Дата: 05.09.21 18:45
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Ценой, понятное дело, дописывания к инициализациям деклараций вроде Dictionary<string, (int, int, string)>.


Свеженький шарп позволяет и это местами не писать.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[51]: MS забило на дотнет. Питону - да, сишарпу - нет?
От: vdimas Россия  
Дата: 05.09.21 18:57
Оценка:
Здравствуйте, Sinclair, Вы писали:

V>>Так я попросил показать, что это за код, где живет, который

V>>
V>>GetFieldType(i) == typeof(int)
V>>      ? MemoryMarshal.GetReference(MemoryMarshal.Cast<byte, int>(_rawData.Slice(GetFieldOffset(i)));
V>>      : Convert.ToInt32(GetValue(i));
V>>

S>Примерно такой код и живёт

Так где?


V>>И да, если читать из поля Int16, например, Int32, то опять пляшем через боксинг, верно?

S>Смотря как делать. Но это — не очень важный аспект: поля int16 в базах встречаются нечасто.

Торговля.


S>А там, где встречаются, разработчики знают, зачем их применяют, и не будут делать конверсию на том конце, который сделает её медленнее.


На "том конце" выгодней использовать In32 или даже UIn32, в случае которого опять боксинг, согласно приведённого кода.

V>>Ну так из приемного буфера (который уже managed) — в MemoryRecordBuffer, а оттуда в датасет или в поля объектов какого-нить ORM.

S>Вот не вижу этого момента "из приёмного буфера".

Я приводил и сниппеты и ссылки дотнетного ODBC-драйвера, вернись да посмотри.


V>>>>И в любом случае твой пример только для MSSQL, а для любых других баз на основе OLEDB или ODBC будет как я дал ссылку на исходники дотнета.

S>Для любых других баз будет их собственные реализации IDataRecord.

В которых всё не лучше:
https://github.com/mysql/mysql-connector-net/blob/502d718bed8ca9cf81a3a0397574f24ec41b25ba/MySQL.Data/src/datareader.cs#L619
   public override Int32 GetInt32(int i)
    {
      IMySqlValue v = GetFieldValue(i, true);
      if (v is MySqlInt32)
        return ((MySqlInt32)v).Value;

      return (Int32)ChangeType(v, i, typeof(Int32));
    }



V>>И все пишут в своих проектах собственные дрова к БД?

V>>Или пользуются имеющимся в дотнете?
S>Зачем собственные? Зачем имеющиеся в дотнете? Если есть такая потребность — пишется один раз эффективная реализация для конкретной СУБД.

Это умозрительно.
Речь о том, как по-факту.


S>Так-то я могу и в нативе взять первое попавшееся чопопало и потом расстраиваться, что у меня двойные-тройные конверсии внутри.


Но можешь взять и не что попало.
А в дотнете не можешь.


V>>Куда я тебе показал в первый раз — это реализация ODBC драйвера.

V>>Все базы имеют ODBC-дрова, но далеко не все имеют OLEDB.
V>>Например, нет OLEDB драйвера к самой популярной в вебе базе MySQL.
S>Эмм, причём тут OLEDB? Мы же вроде бы про дотнет говорим.

Потому что под виндами самая популярная технология доступа к БД, почти все базы, доступные под Windows, имели ODBC и OLEDB-драйверы.
ODBC легковеснее, но сложнее в использовании.
OLEDB проще, т.к. это некая объектная модель на основе COM.


S>Это же COM — значит, никакого инлайнинга вызовов GetData; да и сигнатура у его методов подразумевает копирование в caller-allocated storage.


Ага.


S>Ну, и где тут хвалёный С++ с его возможностью "вернуть сразу данные из буфера без промежуточных копирований"?


Вот OLEDB именно так и устроен с ценой одного виртуального вызова на одно чтение одного поля.


S>Так что давайте мы не будем рассматривать OLE DB в качестве какой-то особенно быстрой альтернативы дотнету.


Откуда ты взял "альтернативу"?
Никакой альтернативы. ))

Дотнетный ADO.Net — это изначально обертка над нейтивными ODBC и OLEDB.
Даже которая была изначально оберткой над MS SQL Native Client — там тоже обертка над OLEDB-подключением была.

Речь о кач-ве этой обертки.


S>А если мы посмотрим на дотнет, то есть, к примеру, System.Data.Sqlite.org.


Это локальная база, в основном.


V>>Следующая по популярности идёт PostgreSQL, к ней тоже живые/актуальные только ODBC-дрова.

S>Для PostgreSQL живы/актуальны нативные дотнетные дрова, 100% написанные на C#. Судя по всему — тоже не очень плохо написанные: https://www.npgsql.org/

Лень смотреть еще и на Postgre-дрова, но вот для MySQL ссылку привёл — мрак.


V>>Я говорил как оно есть по-факту, а ты рассуждаешь как оно могло бы быть, если бы все в мире действовали самым разумным способом.

S>Как видим, все действуют именно так, как я говорю.

Ложь.
Причём, ложь глупая, игнорующая реальность.


V>>"В нейтиве оптимизированные" — это ты только что придумал.

V>>Дрова там обычные, бо парсить разметку любого протокола много ума не надо.
V>>Оптимизированным получается сам сценарий зачитки данных, потому что максимально непосредственный.
S>Непонятно. Вы в качестве оптимизированного сценария приводите почему-то OLEDB, который by design страдает от тех же (и худших) ограничений, что и дотнет.

Нет, не страдает.
Страдаешь ты непонимаем той простой вещи, что зачитка данных и вообще задачи драйвера БД — это такие же задачи как сериализации-десериализации.

И что если (допустим) кто-то где-то навертел слои абстракций, то и обходить их надо как принято — через IoC.

Сравни poll-метод в случае "матрёшки" абстракций:

class Layer1Obj {
    public virtual int GetInt32(int index) {}
}

class Layer2Obj {
    Layer1Obj _obj1;

    public virtual int GetInt32(int index) {
        return _obj1.GetInt32(int index);
    }
}

...

class Layer10Obj {
    Layer9Obj _obj9;

    public virtual int GetInt32(int index) {
        return _obj9.GetInt32(int index);
    }
}

Сравнить надо с принятым в OLEDB подходом с акцессорами, когда акцессоры устанавливаются на текущий recordset и вызываются одной командой.

В дотнете такой подход можно сравнить с подпиской на события:
class Layer10Obj {
    Layer9Obj _obj9;

    public event SomeEventSignature SomeEvent {
        add { _obj9.SomeEvent += value; }
        remove { _obj9.SomeEvent -= value; }
    }
}

IoC-подход "прошивает" слои абстракции (бо сам OLEDB — уже абстракция).


V>>Там должно было быть до 80% общего кода, если не больше.

S>Со временем, может быть, и будет.

Еще через 20 лет? ))


S>А может быть — нет. Потому что какой-нибудь SQLite вообще буфера клиенту не отдаёт — зачем ему? У него все манипуляции делаются прямыми вызовами в inproc DLL.


У многих драйверов так делается.
Речь о том, что код этот глупейший.
Банальную задачу сериализации/десериализации ни сформулировать не могут, ни решить.

Как ни крути, но в плане доступа к БД над дотнетом как проклятие висит.
Днище.


S>А устройство протоколов обмена данными для каждого вендора, мягко говоря, очень своё.


Да пофик.
Когда речь о десериализации тела сообщения с данными, то там всё очень близко — данные идут подряд согласно схеме рекордсета и битового вектора NULL-значений.
Иногда с учётом выравнивания, но это уже совсем мелочи в различиях, т.е. код десериализации под 99% баз мог быть очень близок.
И предлагать модель эффективной десериализации.

На сегодня не предлагает.

Забавно еще то, что ADO хотя и унаследовало от COM ADODB аббревиатуру, но не унаследовала принципы работы, бо ADODB — это IDispatch-реализации прямо поверх OLEDB, т.е. для возможности использовать OLEDB из скриптовых языков (например, для VBA, хотя для VB IDIspatch уже не нужен, он может пользовать OLEDB напрямую).

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

И твои попытки оправдать всё это днище через игнор фактического положения вещей или даже через прямую ложь — такое же днище.


S>Если мы захотим сделать что-то более оптимальное, чем дефолтный драйвер поверх универсального коннектора типа OLE DB или ODBC, то придётся погрузится вот в эти подробности.


Ой, уже страшно...
Аж протокол распарсить...
Неподъёмного веса задача. ))
Ты хоть себя слышишь?



V>>Ну вот я тебе ссылку дал на кишки драйвера к MSSQL.

V>>Это я уже молчу о том, что половину типов по той ссылке можно было сделать value-type.
S>В теории — да.
V>>По-факту там ад и ужас, нагрузка на GC на ровном месте.
S>По факту там внутри — массив структур.

Опять глупая ложь. ))
Ладно, надоело.
Днище.
Отредактировано 06.09.2021 19:42 vdimas . Предыдущая версия . Еще …
Отредактировано 05.09.2021 21:02 vdimas . Предыдущая версия .
Re[45]: MS забило на дотнет. Питону - да, сишарпу - нет?
От: vdimas Россия  
Дата: 05.09.21 18:57
Оценка:
Здравствуйте, Serginio1, Вы писали:

V>>Не обязательно.

V>>Можно просто описать разные типы структур, читать поле-дискриминатор и приводить к соотв. структуре.
V>>Твоё описание хорошо, когда одно поле всего в union, но бывает union структур.
S>Ну просто имея [FieldOffset()] и fixed byte data[] можно мапить структуру на любой IntPtr или byte[]

Ес-но, я этим тоже регулярно пользуюсь в интеропе, бо иначе порой никак.
Re[51]: MS забило на дотнет. Питону - да, сишарпу - нет?
От: vdimas Россия  
Дата: 05.09.21 19:04
Оценка:
Здравствуйте, Ночной Смотрящий, Вы писали:

V>>>>И в любом случае твой пример только для MSSQL, а для любых других баз на основе OLEDB или ODBC

НС>>>И много таких баз данных в реальности?
V>>А много используют MSSQL в вебе?
НС>Много. В Ажуре, к примеру, это основная РСУБД. К чему вопрос?

Вопрос был про веб.
В вебе MSSQL не входит в тройку самых популярных баз.


V>>>>Разве что написали дотнетный драйвер к MS SQL, изначально это была обычная обёртка над OLEDB,

НС>>>Никогда не была. Изначално это была обертка над низкоуровневоцй библиотекой парсинга протокола (tdslib.dll или как то так).
V>>Над MS SQL Native Client.
НС>Вроде того. Это не ADO, это более низкоуровневая штука.

MS SQL Native Client — обычные OLEDB + ODBC драйвера в одном флаконе.
(И абревиатура в COM не ADO, а ADODB — IDispatch-based обёртка над OLEDB и ODBC, давшая для обеих технологий одинаковую объектную модель для скриптовых языков во второй половине 90-х)


V>>В котором доступны были 3 вида подключения — ODBC, OLEDB и еще не помню название вида подключения

НС>Нет. Конкретно та либа — обычная плоская dll со специфичным контрактом, по сути просто парсер TDS.

Думаю, твои данные из более позднего периода, чем от времени выхода первого дотнета, когда я это всё подробно ковырял.


НС>>>Хинты, как показывает практика, нужны хорошо если в 1% запросов. Это почему DAL не стоит затачивать под конкретного провайдера.

V>>Как показывает практика, 1% запросов используется в 99% случаев.
НС>Это неважно. Потому что 1% запросов это 1% кода, и проще сделать для 1% специальное исключение, а не переписывать ради этого под каждый сервер оставшиеся 99%.

Верно.
Но в случае того же EF нетривиально, т.е., подозреваю, что редко используется.
Re[51]: MS забило на дотнет. Питону - да, сишарпу - нет?
От: vdimas Россия  
Дата: 05.09.21 19:16
Оценка: :)
Здравствуйте, Ночной Смотрящий, Вы писали:

V>>Куда я тебе показал в первый раз — это реализация ODBC драйвера.

V>>Все базы имеют ODBC-дрова, но далеко не все имеют OLEDB.
V>>Например, нет OLEDB драйвера к самой популярной в вебе базе MySQL.
V>>Следующая по популярности идёт PostgreSQL, к ней тоже живые/актуальные только ODBC-дрова.
НС>Опять за рыбу деньги. ADO.NET драйвера и к MySQL, и к Postgres — полностью managed, не используют ни ODBC, ни OLEDB, ни вообще какую либо внешнюю библиотеку.

Ага, специально посмотрел:
http://www.rsdn.org/forum/flame.comp/8084694.1

Для MySQL такой же мрак.


НС>Я сейчас вообще затрудняюсь придумать хоть одну БД, где бы ODBC или OLEDB мост был бы единственным выбором в силу отсутствия pure драйвера.


И почему так вышло? ))
Ну вот, допустим, был бы у дотнета нормальный драйвер ODBC, был бы смысл дублировать функциональность в дотнете?
Допустим, как поступила джава в JDBC:
https://github.com/eagle518/jdk-source-code/blob/91b771140de051fb843af246ab826dd6ff688fe3/jdk5.0_src/j2se/src/share/classes/sun/jdbc/odbc/JdbcOdbc.c#L140

Т.е., интеропа нормального нет, приходится делать JNI, но там ни строчки лишней.
Вот это по ссылке — это всё, собсно, получи значение поля.
Re[45]: MS забило на дотнет. Питону - да, сишарпу - нет?
От: vdimas Россия  
Дата: 05.09.21 19:40
Оценка:
Здравствуйте, Sinclair, Вы писали:

V>>Хотя, подозреваю, что быстродействие вызова UnmanagedCallersOnly-метода должно быть примерно на уровне DllImport.

S>Ну, с практической точки зрения там почти всё — бессмысленно. Вот эти все "давайте получим анменеджед указатель на менеджед делегат, и вызовем его из менеджед кода" — нулевая практическая ценность.

Да конечно, опять разбрасываешься громкими заявлениями.
GetFunctionPointerForDelegate — это единственный (ранее) был способ получать колбэки из нейтивного кода в управляемый.


S>Удивительно то, что вызов через делегат, ещё недавно чуть ли не на порядок проигрывавший вызову через интерфейс


Опять жжёшь. ))
Никогда делегат интерфейсу не проигрывал, откуда ты эту траву берёшь?

Этот вызов проигрывал только в цикле, например:
ISomeObj obj = ...;

for(int i = 0; i < 1000000; i++)
    obj.Foo();


Здесь джит один раз достаёт адрес фактической виртуальной ф-ии и вызывает потом миллион раз ф-ию по закешированному адресу (часто прямо в EAX).

Собсно, мои обертки для тестирования специально сделаны такими, чтобы исключить эффект кеширования адреса виртуальной ф-ии.
Внимательный человек обратил бы внимание.


S>теперь сравнялся — у меня делегат вышел даже быстрее.


Мне ты показал, что имел странные представления о происходящем в дотнете.


S>В общем, предсказуемым образом, менеджед код быстрее всего вызывать через managed func pointer, а анменеджед — через PInvoke без переключения GC.


Что такое "без переключения GC"?


S>Самый дешёвый вызов — по менеджед указателю — на моём процессоре стоит 7ns.


Ага, простой call EAX.
Только опять какие-то недостоверные у тебя данные — у меня в цикле этот вызов стоит в среднем 0.16 ns, что ближе к истине, т.к. безусловные jump и call, считай, бесплатны в современных процах.

Вычитай baseline из результатов, зря ты вывел столбец ratio — я же специально предупреждал.
Дотнетная бенчмарк-библиотека позволяет подставлять свои провайдеры столбцов для корректной интерпретации результатов (т.е. не всегда дефолтная интерпретация корректна), я просто обломался это делать, бо результаты и так слишком красноречивы.


S>Вызовы менеджед кода как анменеджед стоят космически дорого (1мкс), но они и не нужны.


Это при такой эффективности не нужны.
А так бы был еще один инструмент проектирования — можно было бы некоторому коду подавать различные реализации, смешанно из нейтива и дотнета.

Собсно, тест для того и был написан, чтобы уяснить для себя, как стоит использовать появившийся указатель на ф-ию в дотнете.
Вывод — в горячих ветках кода смешивать unmanaged и managed код через указатели не стоит.


S>То есть когда мы говорим "вот тебе десять мегов данных, позови меня, когда отправишь" — это норм.


Да тоже не норм при такой цене.
Т.е. колбэков из нейтива стоит избегать в горячем коде.
Re[43]: MS забило на дотнет. Питону - да, сишарпу - нет?
От: vdimas Россия  
Дата: 05.09.21 20:28
Оценка:
Здравствуйте, Sinclair, Вы писали:

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

V>>Для пущей безопасности, кароч, чтобы временные ссылки никуда случайно не утекли.
S>Ну так в том-то и дело, что не так много случаев, когда в гражданском коде мы будем пользоваться вот такой вот структурой "ограниченного действия".

ref struct вообще не выглядит средством для "гражданского кода". ))
Т.е., тут планка входа должна быть чуть выше, чем нулевая.
Это инструмент для тех, кто понимает происходящее.
Оно не сложное, это происходящее, но понимать его обязательно.


V>>Т.е. структуры, попадающие под ограничение unmanaged.

V>>В т.ч. в теле которых располагаются inplace-массивы примитивных типов или тоже unmanaged структур.
S>Ага, вы говорите про "массивы структур". Но ведь с ними и до этого особенных проблем не было

Были проблемы, я их обрисовал, похоже, ты не понял на словах.
Даю в коде.
Вот так было нельзя:
struct Struct1 {
    int a, b;
}

Struct1* ptr = stackalloc Struct1[42];


А вот так до сих пор нельзя в 5-м дотнете C# 9.0:
unsafe struct Struct2 {
    // error CS1663: Fixed size buffer type must be one of the following: bool, byte, short, int, long, char, sbyte, ushort, uint, ulong, float or double
    fixed Struct1 inplaceArray[42];    
}


Я не проверял еще в 6-м дотнете, ты там вроде хвастался, что дёргал C# 10.0, самое время проверить.


S>Я-то имел в виду работу с flexible array member https://en.wikipedia.org/wiki/Flexible_array_member


fixed struct тебе в помощь, но за границей памяти следить самостоятельно:
    unsafe struct Struct2
    {
        public fixed byte inplaceArray[1];
    }

    ...

    byte * ptr1 = stackalloc byte[10000];
    Struct2 * ptr2 = (Struct2 *)ptr1;

    // пишем за пределы структуры:
    ptr2->inplaceArray[9999] = 42;



V>>А как GC должен знать, где в стеке управляемые данные, а где игнорить?

S>Программист ему подскажет, где можно безопасно игнорить, при помощи SuppressGCTransition.

Если бы еще нейтив вызывался исключительно для тривиальных вещей...
Но за подсказку о новой фиче спасибо.


V>>Стек замыкается в любом случае, т.к. GC stop world никто не отменял.

S>Для конкретного метода с инкрементом отключение GC Transition ускоряет вызов в натив вдвое. Если вычитать baseline — то втрое.

С инкрементом понятно.
Прямо сейчас не буду утверждать, но на вскидку не помню у себя случаев, чтобы нейтивный код не делал вызовов АПИ ОС, иначе такой код давно живёт в дотнете.
Т.е. в этом случае SuppressGCTransition использовать нельзя.

В прошлых проектах были вызовы медиа-кодеков из дотнета, но такие вещи лучше целиком убирать в нейтив, оставляя дотнету только управляющую роль — создать канал/стрим, настроить, запустить/остановить.


S>Для часто вызываемых микро-методов типа "набъём команды в буфер" — самое то.


На дотнетной стороне делается, например, в том же Unity в плагинах к драйверу.
Там в среднем 2-6 чисел в буфер надо напихать за раз, где первое число код команды, потом аргументы.
Вызывать ради этого нейтив не имеет смысла.


V>>Средней руки приложение может компиляться под AOT iOS несколько минут, сколько-нибудь большое — десятки минут.

V>>Поэтому, всё это херня собачья, гнаться за хотспотом.
S>Не уверен.

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

Поэтому, ИМХО, альтернативы AOT нет.
Не зря Андроид практически полностью переехал на AOT.


V>>Ну, у нас зато тоже в некоторых проектах тонны статического-справочного кода, вот на такх проектах разница видна хорошо.

V>>И что характерно — если эти "справочные" данные бинарно сериализовать (не встроенной бинарной сериализацией, которой теперь тоже нет в .Net Core, а просто ручками), сохранить в ресурсах, а потом при старте прочесть — это занимает примерно 30 ms. А в исходном виде добавляло примерно 400 ms.
S>Непонятно, откуда тормоза.

От мегабайт кода инициализации справочников:
FieldDescriptor field1 = new FieldDescriptor (Tag42, DataType.Int32, "Field Description")

И вот такого кода бесконечно.
Джит его честно джитил ради однократного исполнения.
В случае хранения в ресурсах я экспериментировал — джитится только код десериализатора, загрузка одного массива байт из ресурсов быстрая всё отрабатывает на порядок с лишним быстрее.

В нейтиве такие вещи сидят в сегменте data, т.е. просто подгружаются в память при загрузке бинаря на исполнение, а в дотнете статические данные инициализируются по-старинке, как будто у нас всё еще есть апп-домены с динамическим выделением статических данных для каждого домена.


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

V>>В нейтиве для этого отродясь было PGO.
V>>К АОТ его тоже надо прикручивать.
S>У PGO ровно та же самая проблема — устаревание данных.

Я плохо себе представляю сценарий, когда полностью оптимизированный код надо опять оптимизировать.
PGO управляет обычно выбором — меньше кода или больше инлайна.
Т.е. для холодных участков код можно сделать поменьше в объёме, для горячих — больше инлайна.
Но при максимальной оптимизации никакая дальнейшая оптимизация лучше не сделает.
Хуже — да, например, опять перекомпилять ход как холодный, т.е. уменьшив занимаемый кодом размер в памяти.


V>>Хотя, АОТ хорош тем, что не рассматривает типы как открытые, хотя бы по виртуальности/наследованию.

S>Это как это он так не рассматривает? А если я динамически код сгенерю?

Нельзя.


S>А если будет подгружена новая сборка?


Нельзя.
Эти вещи надо выносить как стадию сборки, например, это можно делать в LinqToDb, не обязательно заниматься кодогенерацией во время работы приложения.


V>>Плюс, у него достаточно времени на всевозможные оптимизации.

S>Для "настольного" применения — согласен, АОТ нужен. А для серверсайда, когда приложение живёт неделями, выделить 1% времени на дооптимизацию даст столько времени, что никакому АОТ столько не дадут.

Я вообще не понимаю, зачем это делать, особенно сейчас, когда всё-равно нет перезагрузки апп-доменов ввиду отсутствия оных.
Зачем нагружать компиляцией-оптимизацией боевую машину/машины?
Скомпиллировать-оптимизировать можно на машине, предназначенной для компиляции-оптимизации.
Отредактировано 05.09.2021 21:07 vdimas . Предыдущая версия .
Re[46]: MS забило на дотнет. Питону - да, сишарпу - нет?
От: Sinclair Россия https://github.com/evilguest/
Дата: 06.09.21 02:39
Оценка:
Здравствуйте, vdimas, Вы писали:
V>Да конечно, опять разбрасываешься громкими заявлениями.
V>GetFunctionPointerForDelegate — это единственный (ранее) был способ получать колбэки из нейтивного кода в управляемый.
Вы невнимательно читаете. Вот эту часть явно пропустили: "...и вызовем его из менеджед кода"

V>Опять жжёшь. ))

V>Никогда делегат интерфейсу не проигрывал, откуда ты эту траву берёшь?
Вопрос хороший. Я сам не мерил, но помню ещё на заре обсуждения дотнета бытовали такие мнения. Возможно — заблуждение.


S>>В общем, предсказуемым образом, менеджед код быстрее всего вызывать через managed func pointer, а анменеджед — через PInvoke без переключения GC.


V>Что такое "без переключения GC"?
То, что вы называете "замыкание стека", в официальной документации описывается как "переключение GC из кооперативного в вытесняющий режим".
V>Ага, простой call EAX.
V>Только опять какие-то недостоверные у тебя данные — у меня в цикле этот вызов стоит в среднем 0.16 ns, что ближе к истине, т.к. безусловные jump и call, считай, бесплатны в современных процах.
Я взял ваш же бенчмарк и запустил его. Может, конечно, я неправильно вычел и поделил...

V>Вычитай baseline из результатов, зря ты вывел столбец ratio — я же специально предупреждал.

Вы невнимательно читаете. Ratio — это отношение дополнительного времени (т.е. с вычтенным baseline) ко времени работы чемпиона. Поэтому у baseline ratio равен нулю, а у managed ptr — 100%

S>>Вызовы менеджед кода как анменеджед стоят космически дорого (1мкс), но они и не нужны.


V>Это при такой эффективности не нужны.

V>А так бы был еще один инструмент проектирования — можно было бы некоторому коду подавать различные реализации, смешанно из нейтива и дотнета.
Это и так можно делать. Достаточно подавать этому коду только менеджед реализации; а в случае необходимости вызова анменеджед мы бы подавали менеджед-обёртку, которая вызывает PInvoke. Навскидку, это должно быть примерно втрое быстрее исследованного вами способа.
V>Да тоже не норм при такой цене.
О чём вы? Даже если предположить, что вся стоимость, замеренная вами — это цена unmanaged-to-managed transition (у вас же в обе стороны происходит переключение), то один вызов стоит примерно 8.6мкс.
При работе крупнымми блоками у нас характерная гранулярность вызова — миллисекунд 100, не меньше. Т.е. мы говорим об относительных стоимостях в 0.01%.
V>Т.е. колбэков из нейтива стоит избегать в горячем коде.
А можно хотя бы отдалённый намёк на то, что за горячий код собирается вызывать колбек из натива в менеджед?
Ну, мне просто интересно — это реальная задача, проистекающая из объективных ограничений, или просто архитектурный косяк, который нужно исправить.
И, к тому же, надо бы замерить стоимость вызовов из анменеджед, применив UnmanagedCallersOnly.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Отредактировано 06.09.2021 7:01 Sinclair . Предыдущая версия .
Re[44]: MS забило на дотнет. Питону - да, сишарпу - нет?
От: Sinclair Россия https://github.com/evilguest/
Дата: 06.09.21 03:24
Оценка: 3 (1)
Здравствуйте, vdimas, Вы писали:

V>Были проблемы, я их обрисовал, похоже, ты не понял на словах.

V>Даю в коде.
Да, код всегда помогает.

V>Вот так было нельзя:

V>
V>struct Struct1 {
V>    int a, b;
V>}

V>Struct1* ptr = stackalloc Struct1[42];
V>

Это и есть те случаи, когда "сама структура не нужна за пределами текущего фрейма стека, и не хочется нагружать GC." Вы по-прежнему невнимательно читаете.
Во всех остальных случаях мы делаем просто var t = new Struct1[42]. Это же не Java с её отсутствием value types.
А в вашем случае все "проблемы" сводятся к
byte *tmp = stackalloc byte[sizeof(Struct1)*42];
Struct1* ptr = (struct1*) tmp


V>А вот так до сих пор нельзя в 5-м дотнете C# 9.0:

V>
V>unsafe struct Struct2 {
V>    // error CS1663: Fixed size buffer type must be one of the following: bool, byte, short, int, long, char, sbyte, ushort, uint, ulong, float or double
V>    fixed Struct1 inplaceArray[42];    
V>}
V>

V>Я не проверял еще в 6-м дотнете, ты там вроде хвастался, что дёргал C# 10.0, самое время проверить.
Нет, пока не дёргал — только анонсы читал.
А fixed array как-то вообще не очень хорошо себя ведёт в том смысле, что им пользоваться неудобно.
Ну, вот например — хочется сделать структуру относительно замкнутой, чтобы можно ей было удобно пользоваться:
public unsafe struct
{
   private int _length;
   private fixed int _inplaceArray[42];
   public ref int this[int index] {...}
}

Вроде бы — нормальное желание. Хочется сделать структуру, которой можно пользоваться из safe-кода; внутрь индексера мы вставим проверку границ, которая должна устраняться джитом в простых случаях.
Увы — даже тут нельзя внутри индексера просто написать _inplaceArray[index]. Надо пинить this через fixed().
В свете этого удобнее получается делать примерно так:
public unsafe struct
{
   private fixed int _inplaceArray00;
   private fixed int _inplaceArray01;
   ...
   private fixed int _inplaceArray41;
   public ref int this[int index] => ref MemoryMarshal.CreateSpan(ref _inplaceArray00, 42)[index];
}



V>fixed struct тебе в помощь, но за границей памяти следить самостоятельно:

V>
V>    unsafe struct Struct2
V>    {
V>        public fixed byte inplaceArray[1];
V>    }

V>    ...

V>    byte * ptr1 = stackalloc byte[10000];
V>    Struct2 * ptr2 = (Struct2 *)ptr1;

V>    // пишем за пределы структуры:
    ptr2->>inplaceArray[9999] = 42;
V>

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

V>Если бы еще нейтив вызывался исключительно для тривиальных вещей...

На всякий случай напомню, что вы как аргумент для недостаточного быстродействия PInvoke приводили как раз случай тривиальных вещей — невозможно делать миллион нетривиальных вещей в секунду.
V>Но за подсказку о новой фиче спасибо.
Да не за что — сам узнал в ходе дискуссии. За что и ценю вот эти форумные баталии

V>Прямо сейчас не буду утверждать, но на вскидку не помню у себя случаев, чтобы нейтивный код не делал вызовов АПИ ОС, иначе такой код давно живёт в дотнете.

V>Т.е. в этом случае SuppressGCTransition использовать нельзя.
Можно. Нельзя если есть колбэки, и не рекомендуется, если код работает более секунды.

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

Да, наверное. Не очень знаком с деталями взаимодействия с медиа-кодеками.

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

V>Там в среднем 2-6 чисел в буфер надо напихать за раз, где первое число код команды, потом аргументы.
V>Вызывать ради этого нейтив не имеет смысла.
Я об этом сразу писал в комментах про вулкан — набивка чисел в буфер прекрасно живёт и в менеджед. Но вы настаивали (в обсуждении с Serginio1) на миллионах PInvoke вызовов в секунду...
Если у нас какая-то прямо жёсткая зависимость от внешнего вызова, но их много и они занимают мало времени на анменеджед стороне — PInvoke вовсе не так уж плох. Всего лишь втрое дороже call EAX.

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

Повторюсь — это зависит от того, что мы считаем временем боевой работы программы.

V>Поэтому, ИМХО, альтернативы AOT нет.

V>Не зря Андроид практически полностью переехал на AOT.
Каждому своё. Мобильная платформа — это максимально короткое время исполнения приложения, чтобы сохранить батарею. Запустил — воспользовался — выкинул.
Для таких сценариев альтернативы АОТ нету. Даже если мы изобретём способ персистентной инкрементальной компиляции, то есть будем записывать на диск результаты JIT и на следующем запуске начинать не с нуля, то это будет очень плохо восприниматься пользователем — к моменту, когда такая схема "прогреется", недовольный пользователь давно уже снесёт задолбавшее тормозное приложение с устройства.

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

V>От мегабайт кода инициализации справочников:

V>
V>FieldDescriptor field1 = new FieldDescriptor (Tag42, DataType.Int32, "Field Description")
V>

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

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

Да, это представляет потенциал для возможного будущего улучшения. К примеру, те же атрибуты, АФАИК, работают не так — там как раз параметры конструктора кладутся в данные; и потом конструкторы вызывает магия среды, без построения кода статических инициализаторов. Теоретически, можно сворачивать инициализаторы в аналогичный код.

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

А вот я — хорошо. Вот у вас, грубо говоря, был высокоабстрактный код, который читает данные из базы в цикле и сериализовывает их при помощи настроенного снаружи сериализатора:
...
foreach(var d in dataReader)
{
   _serializer.Write(_outstream, d);
}
...

Допустим, мы умеем поддерживать два сериализатора — в XML и в JSON. Выбирается тот или иной традиционно — на основе анализа хидера Accept у клиентского запроса.
В тестах у нас PGO выясняет, что dataReader у нас обычно — это SqlDataReader, и выполняет спекулятивный инлайнинг. В тестах, на основе которых строился профайл, JsonSerializer и XmlSerializer вызывались 50%/50%, поэтому доминируюшего фактического типа не было, поэтому оставлен косвенный вызов.

Хотспот же выясняет, что в процессе реальной эксплуатации у нас 99% вызовов запрашивали XML. Он перекомпилирует код, выполняя спекулятивный инлайнинг и устраняя лишнюю косвенность.
V>PGO управляет обычно выбором — меньше кода или больше инлайна.
V>Т.е. для холодных участков код можно сделать поменьше в объёме, для горячих — больше инлайна.
V>Но при максимальной оптимизации никакая дальнейшая оптимизация лучше не сделает.
А потом у нас выкатили новую версию фронт-енда, и теперь чаще запрашивается JSON. Хотспот перекомпилирует наш цикл, инлайня на этот раз JsonSerializer.
При этом, опять же, после инлайна кода у нас появляется статистика о том, какие именно запросы делаются в dataReader. И, соответственно, возможность ещё что-то проинлайнить, выбросив лишние ветки if вместе с косвенными вызовами. В итоге, после достаточно длинного прогрева, у нас получается почти такой же код, который строит компилятор С++ в том случае, если ему известны точные типы всех участников нашего абстрактного конвеера.

V>Нельзя.

Это очень плохо

V>Нельзя.

Это очень плохо
V>Эти вещи надо выносить как стадию сборки, например, это можно делать в LinqToDb, не обязательно заниматься кодогенерацией во время работы приложения.
Для ряда задач это правильно. Но я — фанат рантайм кодогенерации. Это как раз способ во-первых, получить эффекты, сравнимые с вышеописанным хотспотом (который для дотнета пока что находится где-то между областью мечты и областью фантазий), а во-вторых, даже при его наличии подняться на уровень выше. То есть использовать семантическую оптимизацию — не на основе "лабораторных наблюдений", а на основе известных на момент исполнения вышележащей программе фактов. Ну, вот примерно как SqLServer внутри себя оптимизирует запросы с использованием Foreign key — если этот констреинт проверен, то условия типа where exists(select from manager where id = managerId) устраняются из предикатов. А если нет — то не устраняются. Далеко не всегда можно статически установить, будет ли этот констреинт проверен, на этапе компиляции программы.


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

V>Зачем нагружать компиляцией-оптимизацией боевую машину/машины?
V>Скомпиллировать-оптимизировать можно на машине, предназначенной для компиляции-оптимизации.
Это потребует ещё более сложной системы, которая будет собирать profile информацию с боевой машины/машин и доставлять их на машины, предназначенные для компиляции/оптимизации .
Лично я не возьмусь за проектирование такой системы — уж очень хрупкой она получается
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Отредактировано 06.09.2021 7:00 Sinclair . Предыдущая версия .
Re[52]: MS забило на дотнет. Питону - да, сишарпу - нет?
От: Sinclair Россия https://github.com/evilguest/
Дата: 06.09.21 04:00
Оценка: :)
Здравствуйте, vdimas, Вы писали:


S>>А там, где встречаются, разработчики знают, зачем их применяют, и не будут делать конверсию на том конце, который сделает её медленнее.


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

Непонятное утверждение. Если нам на приёмном конце удобнее иметь Int32, то делаем int a = d.GetInt16().
Если программист намеренно сделал в базе поле int16, но читает его через int a = d.GetInt32(), то он сам себе дебил.

V>>>Ну так из приемного буфера (который уже managed) — в MemoryRecordBuffer, а оттуда в датасет или в поля объектов какого-нить ORM.

S>>Вот не вижу этого момента "из приёмного буфера".
V>Я приводил и сниппеты и ссылки дотнетного ODBC-драйвера, вернись да посмотри.
Дотнетный ODBC драйвер никакого отношения к MemoryRecordBuffer не имеет. С чего вы взяли, что ODBC драйвер как-то применяется при заполнении MemoryRecordBuffer.


V>В которых всё не лучше:

V>https://github.com/mysql/mysql-connector-net/blob/502d718bed8ca9cf81a3a0397574f24ec41b25ba/MySQL.Data/src/datareader.cs#L619
V>
V>   public override Int32 GetInt32(int i)
V>    {
V>      IMySqlValue v = GetFieldValue(i, true);
V>      if (v is MySqlInt32)
V>        return ((MySqlInt32)v).Va

V>      return (Int32)ChangeType(v, i, typeof(Int32));
V>    }
V>

Ну, это же MySQL. Его задача — "чтобы хоть как-то работало".

V>Речь о том, как по-факту.

Ну, вы только что несли чушь про то, что все пользуются ODBC драйвером потому, что не существует OLE DB.
По факту оказалось, что для всех мало-мальски известных СУБД написан менеджед-драйвер.
То, что он неэффективен — заслуга авторов драйвера, а не команды из Редмонда.

V>OLEDB проще, т.к. это некая объектная модель на основе COM.

И?

V>Вот OLEDB именно так и устроен с ценой одного виртуального вызова на одно чтение одного поля.

Точно одного? Вы смотрели в исходники OLE DB драйверов? Снаружи-то и у дотнета один виртуальный вызов на чтение одного поля.

V>Речь о кач-ве этой обертки.

Речь о том, что эта обёртка вообще не нужна.

V>Это локальная база, в основном.

Ну, так как раз для неё и нужно, в основном, выжимать такты.

V>Лень смотреть еще и на Postgre-дрова, но вот для MySQL ссылку привёл — мрак.



V>>>Я говорил как оно есть по-факту, а ты рассуждаешь как оно могло бы быть, если бы все в мире действовали самым разумным способом.

S>>Как видим, все действуют именно так, как я говорю.

V>Ложь.

V>Причём, ложь глупая, игнорующая реальность.
В смысле "ложь"? Вы только что рассказывали, что все пользуются генерик ODBC драйвером, и вынуждены жить с его плохим качеством. По факту, оказалось, что всё ровно так, как я говорил — для основных СУБД существует родной дотнетный драйвер.

V>Нет, не страдает.

Ну конечно же страдает. Как вы думаете, что будет делать OLE DB, если я привяжу поле типа Int16 к буферу типа Int32? Ну, боксинга, наверное, не будет. Но тем не менее, будут преобразования на стороне драйвера, которые потребуют лишних затрат.

V>Страдаешь ты непонимаем той простой вещи, что зачитка данных и вообще задачи драйвера БД — это такие же задачи как сериализации-десериализации.


V>И что если (допустим) кто-то где-то навертел слои абстракций, то и обходить их надо как принято — через IoC.


V>Сравни poll-метод в случае "матрёшки" абстракций:


V>
V>class Layer1Obj {
V>    public virtual int GetInt32(int index) {}
V>}

V>class Layer2Obj {
V>    Layer1Obj _obj1;

V>    public virtual int GetInt32(int index) {
V>        return _obj1.GetInt32(int index);
V>    }
V>}

V>...

V>class Layer10Obj {
V>    Layer9Obj _obj9;

V>    public virtual int GetInt32(int index) {
V>        return _obj9.GetInt32(int index);
V>    }
V>}
V>

V>Сравнить надо с принятым в OLEDB подходом с акцессорами, когда акцессоры устанавливаются на текущий recordset и вызываются одной командой.

V>В дотнете такой подход можно сравнить с подпиской на события:

V>
V>class Layer10Obj {
V>    Layer9Obj _obj9;

V>    public event SomeEventSignature SomeEvent {
V>        add { _obj9 += value; }
V>        remove { _obj9 -= value; }
V>    }
V>}
V>

V>IoC-подход "прошивает" слои абстракции (бо сам OLEDB — уже абстракция).
Очень отдалённая абстракция, т.к. "Аксессор" в OLE DB — это всего лишь описание маппинга в некоторой структуре. Т.е ответственность за преобразования типов тоже лежит на драйвере.
Это перечёркивает любые шансы на то, что обращение к данным превратится в "чтение по смещению" без копирования.


V>Банальную задачу сериализации/десериализации ни сформулировать не могут, ни решить.



V>Как ни крути, но в плане доступа к БД над дотнетом как проклятие висит.

V>Днище.
Ну, коллега — это же золотое дно! Напишите вменяемый драйвер для MySQL, да продайте ораклу за пару сотен тыщ.


S>>Если мы захотим сделать что-то более оптимальное, чем дефолтный драйвер поверх универсального коннектора типа OLE DB или ODBC, то придётся погрузится вот в эти подробности.


V>Ой, уже страшно...

V>Аж протокол распарсить...
V>Неподъёмного веса задача. ))
V>Ты хоть себя слышишь?
Я-то да, а вот у вас явно с усвоением проблемы. Дело не в сложности задачи, а в том, что протокол у каждого свой. И способ получения буфера, и лэйаут в этом буфере, и способы передачи метаданных.
Поэтому никакого особенно общего кода там не будет.


S>>По факту там внутри — массив структур.

V>Опять глупая ложь. ))
Хорошо. Массив указателей на структуры.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[52]: MS забило на дотнет. Питону - да, сишарпу - нет?
От: Sinclair Россия https://github.com/evilguest/
Дата: 06.09.21 04:03
Оценка: +1
Здравствуйте, vdimas, Вы писали:

V>Вопрос был про веб.

V>В вебе MSSQL не входит в тройку самых популярных баз.
Ну, и какие базы из тройки самых популярных в вебе вынуждены использовать ODBC/OLE DB в дотнете?

V>>>В котором доступны были 3 вида подключения — ODBC, OLEDB и еще не помню название вида подключения

НС>>Нет. Конкретно та либа — обычная плоская dll со специфичным контрактом, по сути просто парсер TDS.
V>Думаю, твои данные из более позднего периода, чем от времени выхода первого дотнета, когда я это всё подробно ковырял.
Думаю, что нет. В 1999 году эта либа была устроена именно так, как пишет коллега НС.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[52]: MS забило на дотнет. Питону - да, сишарпу - нет?
От: Sinclair Россия https://github.com/evilguest/
Дата: 06.09.21 04:22
Оценка:
Здравствуйте, vdimas, Вы писали:
V>Т.е., интеропа нормального нет, приходится делать JNI, но там ни строчки лишней.
V>Вот это по ссылке — это всё, собсно, получи значение поля.
Муахаха.
Вот, к примеру, как JdbcOdbc получает значение int32:
https://github.com/eagle518/jdk-source-code/blob/91b771140de051fb843af246ab826dd6ff688fe3/jdk5.0_src/j2se/src/share/classes/sun/jdbc/odbc/JdbcOdbcResultSet.java#L582
Это, собсно, анбоксинг.
А боксинг для него делается здесь:
https://github.com/eagle518/jdk-source-code/blob/91b771140de051fb843af246ab826dd6ff688fe3/jdk5.0_src/j2se/src/share/classes/sun/jdbc/odbc/JdbcOdbc.java#L3850
Как грица, не надо ля-ля.

То есть дотнетовый драйвер, по большому счёту, отличается только тем, что кэширует результаты. В джаве я этого кэширования не вижу, что означает что либо я плохо смотрю, либо этот драйвер нарушает спецификацию ODBC, плюс нещадно тормозит на повторных обращениях.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[52]: MS забило на дотнет. Питону - да, сишарпу - нет?
От: Sinclair Россия https://github.com/evilguest/
Дата: 06.09.21 05:18
Оценка: :)
Здравствуйте, vdimas, Вы писали:

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


V>>>Так я попросил показать, что это за код, где живет, который

V>>>
V>>>GetFieldType(i) == typeof(int)
V>>>      ? MemoryMarshal.GetReference(MemoryMarshal.Cast<byte, int>(_rawData.Slice(GetFieldOffset(i)));
V>>>      : Convert.ToInt32(GetValue(i));
V>>>

S>>Примерно такой код и живёт

V>Так где?

https://github.com/mysql-net/MySqlConnector/blob/76357a15baf0a9437d123e8a884044d633eb006c/src/MySqlConnector/Core/Row.cs#L179
https://github.com/mysql-net/MySqlConnector/blob/76357a15baf0a9437d123e8a884044d633eb006c/src/MySqlConnector/Core/BinaryRow.cs#L65


V>В которых всё не лучше:

V>https://github.com/mysql/mysql-connector-net/blob/502d718bed8ca9cf81a3a0397574f24ec41b25ba/MySQL.Data/src/datareader.cs#L619
V>
V>   public override Int32 GetInt32(int i)
V>    {
V>      IMySqlValue v = GetFieldValue(i, true);
V>      if (v is MySqlInt32)
V>        return ((MySqlInt32)v).Value;

V>      return (Int32)ChangeType(v, i, typeof(Int32));
V>    }
V>

Не ройтесь в помойке.

V>А в дотнете не можешь.

Можешь. Вот вы — взяли что попало. А надо было — взять https://mysqlconnector.net/
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[52]: MS забило на дотнет. Питону - да, сишарпу - нет?
От: Ночной Смотрящий Россия  
Дата: 06.09.21 09:18
Оценка:
Здравствуйте, vdimas, Вы писали:

НС>>Много. В Ажуре, к примеру, это основная РСУБД. К чему вопрос?

V>Вопрос был про веб.

Ажур это и есть веб.

V>В вебе MSSQL не входит в тройку самых популярных баз.


Опять как в прошлый раз с С++ и рефакторингом — мамой клиянусь?
Вот результат опроса на SO:

Это, заметь, со всякой носиквельщиной.

НС>>Вроде того. Это не ADO, это более низкоуровневая штука.

V>MS SQL Native Client — обычные OLEDB + ODBC драйвера в одном флаконе.

Это не означает, что старый драйвер ADO.NET использовал OLEDB или ODBC. Если что — я там в свое время нашел багу и внимательно код драйвера для этого изучал. Не было там никакого намека на СОМ вообще или что то про ODBC.

V>>>В котором доступны были 3 вида подключения — ODBC, OLEDB и еще не помню название вида подключения

НС>>Нет. Конкретно та либа — обычная плоская dll со специфичным контрактом, по сути просто парсер TDS.
V>Думаю, твои данные из более позднего периода

Нет. Это было в 1.0 и 1.1.

V>Но в случае того же EF нетривиально, т.е., подозреваю, что редко используется.


Это вполне тривиально при помощи любого приличного ORM. На крайний случай всегда есть возможность заменить табличку на вьюху.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[52]: MS забило на дотнет. Питону - да, сишарпу - нет?
От: Danchik Украина  
Дата: 06.09.21 10:19
Оценка: 1 (1) +2
Здравствуйте, vdimas, Вы писали:

[Skip]

V>В которых всё не лучше:

V>https://github.com/mysql/mysql-connector-net/blob/502d718bed8ca9cf81a3a0397574f24ec41b25ba/MySQL.Data/src/datareader.cs#L619
V>
V>   public override Int32 GetInt32(int i)
V>    {
V>      IMySqlValue v = GetFieldValue(i, true);
V>      if (v is MySqlInt32)
V>        return ((MySqlInt32)v).Value;

V>      return (Int32)ChangeType(v, i, typeof(Int32));
V>    }
V>


Нашли же вы пример. Oracle .NET команда леворукая, это давно известно. Они и асинки не осилили.

Вот переписаное с нуля опенсорсный провайдер. Кого-то достало.
https://github.com/mysql-net/MySqlConnector/blob/1b2757d69f15555fcd05dd8b319a3ae6e3aaf6ad/src/MySqlConnector/Core/Row.cs#L206

[Skip]


V>>>Следующая по популярности идёт PostgreSQL, к ней тоже живые/актуальные только ODBC-дрова.

S>>Для PostgreSQL живы/актуальны нативные дотнетные дрова, 100% написанные на C#. Судя по всему — тоже не очень плохо написанные: https://www.npgsql.org/

V>Лень смотреть еще и на Postgre-дрова, но вот для MySQL ссылку привёл — мрак.


А надо было, это один из лучших драйверов. Там за такты и память борются.
Re[52]: MS забило на дотнет. Питону - да, сишарпу - нет?
От: Ночной Смотрящий Россия  
Дата: 06.09.21 10:22
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Ну вот, допустим, был бы у дотнета нормальный драйвер ODBC, был бы смысл дублировать функциональность в дотнете?


Был. Ты сам с ODBC то работал? Оно очень примитивно.

V>Допустим, как поступила джава в JDBC:

V>https://github.com/eagle518/jdk-source-code/blob/91b771140de051fb843af246ab826dd6ff688fe3/jdk5.0_src/j2se/src/share/classes/sun/jdbc/odbc/JdbcOdbc.c#L140

В джаве все ровно так же. Есть Odbc мост, но все нормальные БД предоставляют pure драйвер.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[53]: MS забило на дотнет. Питону - да, сишарпу - нет?
От: vdimas Россия  
Дата: 06.09.21 12:58
Оценка:
Здравствуйте, Danchik, Вы писали:

D>Нашли же вы пример. Oracle .NET команда леворукая, это давно известно. Они и асинки не осилили.


Оракл наложил лапки 4 года назад, а по ссылке коммиты и 6-тилетние видны.
С другой стороны, твой комментарий делает бесполезным трату времени на просмотр исходников коннекта к оракловым базам, я правильно понял?

Диверсия от владельца джавы Оракла в сторону дотнета? ))


D>Вот переписаное с нуля опенсорсный провайдер. Кого-то достало.

D>https://github.com/mysql-net/MySqlConnector/blob/1b2757d69f15555fcd05dd8b319a3ae6e3aaf6ad/src/MySqlConnector/Core/Row.cs#L206

Тоже не очень:
    if (ordinal < 0 || ordinal > ResultSet.ColumnDefinitions!.Length)
        throw new ArgumentOutOfRangeException(nameof(ordinal), "value must be between 0 and {0}.".FormatInvariant(ResultSet.ColumnDefinitions!.Length));

В рассмотренных ранее исходниках доп. специальную такую проверку не делали, т.к. она делается системным дотнетным кодом при обращении к массиву.

Т.е., достаточно было описать так:
ColumnDefinitionPayload[] _columnDefinitions;


Это тоже нубство:
if (columnDefinition.ColumnType is ColumnType.Decimal or ColumnType.NewDecimal)
...
else if
...
if(...


И вот это нубство:
ResultSet.ColumnDefinitions

где ResultSet — класс.

И вот это вызывается в теле затем, тоже нубство:
        protected override int GetInt32Core(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
        {
            var isUnsigned = (columnDefinition.ColumnFlags & ColumnFlags.Unsigned) != 0;
            return columnDefinition.ColumnType switch
            {
                ColumnType.Tiny => isUnsigned ? (int) data[0] : (sbyte) data[0],
                ColumnType.Decimal or ColumnType.NewDecimal => Utf8Parser.TryParse(data, out decimal decimalValue, out int bytesConsumed) && bytesConsumed == data.Length ? checked((int) decimalValue) : throw new FormatException(),
                ColumnType.Int24 or ColumnType.Long => isUnsigned ? checked((int) MemoryMarshal.Read<uint>(data)) : MemoryMarshal.Read<int>(data),
                ColumnType.Longlong => isUnsigned ? checked((int) MemoryMarshal.Read<ulong>(data)) : checked((int) MemoryMarshal.Read<long>(data)),
                ColumnType.Short => isUnsigned ? (int) MemoryMarshal.Read<ushort>(data) : MemoryMarshal.Read<short>(data),
                ColumnType.Year => MemoryMarshal.Read<short>(data),
                _ => throw new FormatException(),
            };
        }


Должен быть примерно такой код:
public int GetInt32(int ordinal)
{
    return _columnDefs[ordinal]._converter.ToInt32(_data, _dataOffsets[ordinal]);
}

Вызов либо делегата, либо виртуальной ф-ии.

Например, в случае виртуальных ф-ий:
internal class Int32Converter : IDataConverter {
    public ToInt32(Memory<byte> data, int offset) => DataReader.ReadInt32(data, offset);
    public ToUInt32(Memory<byte> data, int offset) => DataReader.ReadUInt32(data, offset);
    public ToInt16(Memory<byte> data, int offset) => checked((short)DataReader.ReadInt32(data, offset));
    public ToUInt16(Memory<byte> data, int offset) => checked((ushort)DataReader.ReadUInt32(data, offset));
    ...
}


В последних версиях дотнета можно было бы заменить на указатель на ф-ию и получить максимально-достижимое быстродействие:
internal class DataConverter {
    public delegate *<Memory<byte>, int, int> ToInt32;
    public delegate *<Memory<byte>, int, uint> ToUInt32;
    public delegate *<Memory<byte>, int, short> ToInt16;
    public delegate *<Memory<byte>, int, ushort> ToUInt16;
    ...
}

internal class Int32Converter : DataConverter {
    Int32Converter() {
        ToInt32 = &DataReader.ReadInt32;
        ToUInt32 = &DataReader.ReadUInt32;

        static short toInt16(Memory<byte> data, int offset) => checked((short)DataReader.ReadInt32(data, offset));
        ToInt16 = &toInt16;

        static ushort toUInt16(Memory<byte> data, int offset) => checked((ushort)DataReader.ReadUInt32(data, offset));
        ToUInt16 = &toUInt16;

        ...
    }
}


То бишь, есть простая таблица конвертеров для строки данных.
Динамически в этой таблице ищется строка по ordinal (строка — фактический тип поля) и статически прыгаем на колонку, т.к. запрашиваемый тип в методе известен (в колонке запрашиваемый тип).

И да, внутренней структуре ColumnDefinitionPayload вовсе не требуется быть классом, структура справилась бы.


V>>Лень смотреть еще и на Postgre-дрова, но вот для MySQL ссылку привёл — мрак.

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

Ну, хорошо, если так.
Кто-то же должен был справиться с банальной задачей десериализации данных? ))
Отредактировано 06.09.2021 19:12 vdimas . Предыдущая версия . Еще …
Отредактировано 06.09.2021 13:24 vdimas . Предыдущая версия .
Отредактировано 06.09.2021 13:23 vdimas . Предыдущая версия .
Отредактировано 06.09.2021 13:21 vdimas . Предыдущая версия .
Отредактировано 06.09.2021 13:00 vdimas . Предыдущая версия .
Отредактировано 06.09.2021 12:59 vdimas . Предыдущая версия .
Re[53]: MS забило на дотнет. Питону - да, сишарпу - нет?
От: vdimas Россия  
Дата: 06.09.21 13:14
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Вот, к примеру, как JdbcOdbc получает значение int32:

S>Это, собсно, анбоксинг.

Для джавы боксинг/анбоксинг немного пофик с её espace-анализом.
Зато дотнету не пофик.

Т.е., происходящее в джаве затем зависит от дальнейшего использования полученного значения на вызывающей стороне.
Например, если это значение передадут GUI-таблице, то как раз надо передавать Object, то бишь Integer.
Если нет, т.е., если происходят числовые вычисления над полученным значением, то escape-анализ потенциально может убрать ненужное боксирование.

Т.е., даже в случае дотнетной идеальной безбоксированной реализации надо было бы тоже боксировать для GUI, приводя к Object.
А если до этого уже боксировали и анбоксировали, то доп. боксирование выглядит и вовсе грустно.

В общем, я не особо люблю джаву (помню как мы в IT-лаборатории любопытства ради разбирали этот язык в 95-м и ржали просто в голос), но в продуманности ей порой не откажешь.
Отредактировано 07.09.2021 19:23 vdimas . Предыдущая версия .
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.