Re[37]: MS забило на дотнет. Питону - да, сишарпу - нет?
От: vdimas Россия  
Дата: 02.09.21 22:49
Оценка:
Здравствуйте, rameel, Вы писали:

R>Но это можно обойти, если написать так:


Спасибо, добрый человек!
Re[46]: MS забило на дотнет. Питону - да, сишарпу - нет?
От: Sinclair Россия https://github.com/evilguest/
Дата: 03.09.21 02:57
Оценка: 1 (1)
Здравствуйте, vdimas, Вы писали:

V>Бесполезно, внутри данные сначала зачитываются в массив object[], т.е. боксируются (первое копирование, причём, затратное):

V>https://github.com/dotnet/runtime/blob/main/src/libraries/System.Data.Common/src/System/Data/Common/DataRecordInternal.cs
Вы приводите другую реализацию IDataRecord. Я показал, как обойтись без боксинга/анбоксинга.

V>Причём, независимо от технологии DAL — ORM поверх DataSet, LinqToDB или EF — все они проходят через код по ссылке.

Это зависит от реализации провайдера.
Посмотрите, например, сюда: https://referencesource.microsoft.com/#system.data/Microsoft/SqlServer/Server/SqlDataRecord.cs

V>Под капотом, в драйвере связи с БД будут готовые к использованию табличные данные, но в дотнете сделали так, что их можно лишь зачитать однонаправленно, даже если запрошен тип рекордсета снапшот, а он практически всегда именно такой, бо динамические рекордсеты признаны злейшим злом еще во второй половине 90-х.

Их можно зачитать однонаправленно и отмапить в массив клиентских объектов. Например, через linq2db.

V>В OLEDB и ODBC в похожих случаях можно рассматривать зачитанные данные как коллекцию с произвольным доступом и читать/навигироваться по данным, находящимся непосредственно в приёмном буфере драйвера (эти драйвера юзер-спейсные, ес-но, бо они лишь формируют и парсят потоки байт).

Ну, это может быть нужно только в десктопных приложениях, которые работают напрямую с базой.
Сейчас, как правило, с базой работает сервер приложений — а ему нужно быстро пробежать по данным в одну сторону, готовя ответ для клиента.

V>Собсно, DAO когда-то, хоть его и ругали за отсутствие оптимизирующего движка для сложных запросов, но в относительно простых запросах по локальной БД ему не было равных в шустрости. В т.ч. потому что приложению на его основе не требовалось копировать данные — данные читались прямо в момент обработки события WM_PAINT контрола-грида прямо из буфера драйвера.

V>На той технике и выбора-то другого не было.
А то. Видел я как-то приложение какого-то научного журнала в 1997 году. Эти орлы при показе оглавления на каждый WM_PAINT тащили XML с диска. Вот только журнал поставлялся на компакт-диске, и любой дёрг экраном занимал минуты полторы.

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

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

V>Мы это уже обсуждали.

V>Всё мн-во, скажем так, "структур" запросов в реальных приложениях конечно, кроме специализированных приложений для динамического построения произвольных запросов, которых я видел всего парочку за всю карьеру.
V>То бишь, с клиента в продуманном приложении достаточно послать ID запроса и параметры к нему.
V>И на стороне базы неплохо бы эти запросы оформить в виде сохраненных view или процедур.
Да ну неправда же. В каждом первом приложении, которое мы видим в интернете, есть кнопки "отсортировать" в заголовках колонок.
В каждом втором — кнопки "отфильтровать" в заголовках колонок.
В каждом третьем — кнопки "скрыть/показать столбцы".

Всё это — динамические запросы. Покажите мне приложение, в котором ничего этого нет.

V>А еще есть SQL-хинты, которые порой могут поднять быстродействие нагруженной базы на порядок.

Поэтому нужны не full-blown ORM типа хибернейта, а типизированные генераторы запросов типа linq2db.

S>>Пока в примерах идёт передача SQL Statement в виде тупо строки, про производительность можно вообще не заикаться.

V>А твой LinQ не строку формирует в итоге, что ле?
В итоге — строку. Вот только динамически
V>Тоже строку, только медленнее.
Теряем микросекунды, выигрываем разы.
V>Во все времена быстрее всего было вызвать хранимку в синтаксисе ODBC "{call InsertOrder(, 10, ?, ?, ?)}" c забинженными аргументами.
И это всё ещё динамическое порождение строки

V>У "красных" все планы запросов заведомо удачные, у "белых" заведомо нет, поэтому "наши" всегда побеждают. ))

V>Но ответить на простой вопрос "почему ты так решил?" уже 15-й год не можешь.
Да потому, что белым приходится брать один запрос, а потом динамически делать проекции и фильтрацию на клиенте. Ибо на 2^N вариантов запроса хранимок не напасёшся.
V>Любая локальная база предоставляет нейтивные свои драйвера/библиотеки доступа, которые самые эффективные.

V>Хотя, сервера тоже предоставляют нейтивные драйвера/библиотеки, через которую можно получить максимальные плюшки:

V>https://docs.microsoft.com/en-us/sql/relational-databases/native-client/features/sql-server-native-client-features?view=sql-server-ver15

V>Я же уже писал выше — границы абстракций DAL при таких раскладах выгодней делать чуть выше, т.е. не по OLEDB/ODBC/ADO и провайдерам/адаптерам диалектов SQL, а прямо по всему DAL.

V>Это отличается от принятой в дотнете практики.

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

V>Т.е., в любом случае трудоёмкость сравнимая, т.к. сравнимая степень переиспользования кода.
V>Просто приличная часть абстракций перетекает из runtime в compile-time, как оно принято в плюсах.
И то же самое можно делать в дотнете.

V>Обычно плюсы или джава.

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

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

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

V>Про Dapper:

V>

V>It is very efficient and owns the title of King of Micro ORM in terms of performance.


Я в курсе что такое Dapper, не переживай. Но это не означает целесообразность использования датасетов. И насчет King тут тоже не все так просто — linq2db на большинстве операций не медленнее.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[47]: MS забило на дотнет. Питону - да, сишарпу - нет?
От: vdimas Россия  
Дата: 03.09.21 05:07
Оценка:
Здравствуйте, Sinclair, Вы писали:

V>>Бесполезно, внутри данные сначала зачитываются в массив object[], т.е. боксируются (первое копирование, причём, затратное):

V>>https://github.com/dotnet/runtime/blob/main/src/libraries/System.Data.Common/src/System/Data/Common/DataRecordInternal.cs
S>Вы приводите другую реализацию IDataRecord. Я показал, как обойтись без боксинга/анбоксинга.

Я привожу что есть по факту.
А то, что у тебя — это надо самим делать обертку поверх ODBC и там малость не так тривиально, но это уже подробности.


V>>Причём, независимо от технологии DAL — ORM поверх DataSet, LinqToDB или EF — все они проходят через код по ссылке.

S>Это зависит от реализации провайдера.
S>Посмотрите, например, сюда: https://referencesource.microsoft.com/#system.data/Microsoft/SqlServer/Server/SqlDataRecord.cs

Не ту ссылку дал, надо смотреть сюда:
https://referencesource.microsoft.com/#system.data/fx/src/data/Microsoft/SqlServer/Server/MemoryRecordBuffer.cs,17

Путь данных еще более извилист и всё-равно данные еще дважды копируются.

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


V>>Под капотом, в драйвере связи с БД будут готовые к использованию табличные данные, но в дотнете сделали так, что их можно лишь зачитать однонаправленно, даже если запрошен тип рекордсета снапшот, а он практически всегда именно такой, бо динамические рекордсеты признаны злейшим злом еще во второй половине 90-х.

S>Их можно зачитать однонаправленно и отмапить в массив клиентских объектов. Например, через linq2db.

Можно.
Через миллион телодвижений унутре.
linq2db — это самая вершинка айберга, пока данные до него дойдут — сто раз вспотеют.


V>>В OLEDB и ODBC в похожих случаях можно рассматривать зачитанные данные как коллекцию с произвольным доступом и читать/навигироваться по данным, находящимся непосредственно в приёмном буфере драйвера (эти драйвера юзер-спейсные, ес-но, бо они лишь формируют и парсят потоки байт).

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

А чем рендеринг в HTML/JSON принципиально отличается от рендеринга куда-то еще?


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


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

Просто плотно занимаюсь в т.ч. парсингом протоколов и организацией доступа к данным, приходящим по сетке.
Так вот, не придумано еще ничего быстрее чем разметить входной поток (он в любом случае парсится), а наружу отдавать данные согласно разметке.
Грубо, для каждого поля подготавливается акцессор.
Будут его потом дёргать или нет — дело клиента.

В дотнете эта стадия тоже происходит, только данные еще зачитываются в создаваемое по new хранилище для каждого поля.
А потом уже происходит чтение из этого хранилища.
Индюшатина-с...


S>А то. Видел я как-то приложение какого-то научного журнала в 1997 году. Эти орлы при показе оглавления на каждый WM_PAINT тащили XML с диска. Вот только журнал поставлялся на компакт-диске, и любой дёрг экраном занимал минуты полторы.


Еще не разучился приводить двоечников в пример?


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

V>>И продолжаю уже 20 лет оставаться в недоумении, что с тех пор ничего толком не изменилось.
S>Ну, это как бы неправда.
V>>Т.е., сильно изменилось на верхнем уровне, а на нижнем — такое же мракобесие 20-тилетней давности.
S>Всё ровно наоборот — нижний уровень прилично переделали, но сверху навалены тонны легаси, которые ещё переписывать и переписывать.

Сорри, но ты сейчас малость из пальца насасываешь.
Оно примерно так и было, как есть сейчас.
Разве что написали дотнетный драйвер к MS SQL, изначально это была обычная обёртка над OLEDB, как и для остального ADO.Net.
Но это было сделано еще с выходом 2-го дотнета.

Да и сам OLEDB драйвер к MS SQL когда-то был оберткой над ODBC-кодом их же драйвера, пока не написали родной для OLEDB.


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

V>>И на стороне базы неплохо бы эти запросы оформить в виде сохраненных view или процедур.
S>Да ну неправда же. В каждом первом приложении, которое мы видим в интернете, есть кнопки "отсортировать" в заголовках колонок.

Не аргумент.
Таких отображений в этом "первом приложении" (которое магазин какой-нить) аж одно на каждую группу товаров.


S>В каждом втором — кнопки "отфильтровать" в заголовках колонок.


Попадает в мой случай.


S>В каждом третьем — кнопки "скрыть/показать столбцы".


Где скрыть их можно на любом уровне, особенно если речь шла о клиенте.
Т.е. на уровне запроса к БД, на уровне сервера приложений, прямо на клиенте.

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


S>Всё это — динамические запросы.


Ну вот смотри, я понаставил кучу флажков:
https://www.wildberries.ru/catalog/elektronika/telefony-i-gadzhety/mobilnye-telefony?sort=newly&amp;cardsize=c246x328&amp;page=1&amp;f10466=3722640%3B514814%3B3725238%3B3725535%3B18630%3B18632%3B18640%3B441183%3B3199823&amp;f90746=214513%3B142905%3B140423%3B140427%3B140428%3B172656%3B172657&amp;f75438=3698872%3B3698698%3B3749957%3B130329%3B3698382%3B174305%3B81543%3B81534%3B121020

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

Не, я примерно понимаю, на что ты намекал, на длиннющие условия навроде:
OR WorkTime=T133 OR WorkTime=T160 OR ...
И так по всем группам условий, не только по WorkTime...
И не говори, что ты на это не намекал, потому что только такое представление требует динамического построения. ))

А стоит взять битовый вектор, и запрос динамически строить не придётся — просто подаётся требуемая маска как аргумент.
На стороне базы маска займёт столько integer-полей, сколько требуется.
Допустим, аж пару BigInt, итого у нас комбинаторика по колву масок:
0 0
0 1

1 0
1 1

Всего 4 запроса.
Или два — 0 0 и 1 1, т.е., если есть хоть один бит — формируем маску целиком.


S>Покажите мне приложение, в котором ничего этого нет.


Показал.
Оно всё-равно не доказывает твои утверждения.
Просто показывает твои странные представления о кодировании данных.


V>>А еще есть SQL-хинты, которые порой могут поднять быстродействие нагруженной базы на порядок.

S>Поэтому нужны не full-blown ORM типа хибернейта, а типизированные генераторы запросов типа linq2db.

Но хинты уникальны для различных провайдеров.
Это почему DAL стоит затачивать под конкретного провайдера, а не под абстрактного, бо различные базы уникальны в своих фичах.


S>>>Пока в примерах идёт передача SQL Statement в виде тупо строки, про производительность можно вообще не заикаться.

V>>А твой LinQ не строку формирует в итоге, что ле?
S>В итоге — строку. Вот только динамически

И с чего ты решил, что это быстрее, даже если бы в некоей другой технологии тоже формировали такую же ужасную строку?
Судя по интонации подаваемого тобой — "просто нравится".


V>>Тоже строку, только медленнее.

S>Теряем микросекунды, выигрываем разы.

Если строки запросов те же — ничего не выигрываешь.
Опять выдаёшь желаемое за действительное, бо "просто нравится". (С)
В общем, я еще на прошлой итерации заподозрил, что ты сольёшься в привычную демагогию.

На деле, при адекватном проектировании будет в разы быстрее, конечно.


V>>Во все времена быстрее всего было вызвать хранимку в синтаксисе ODBC "{call InsertOrder(, 10, ?, ?, ?)}" c забинженными аргументами.

S>И это всё ещё динамическое порождение строки

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


V>>У "красных" все планы запросов заведомо удачные, у "белых" заведомо нет, поэтому "наши" всегда побеждают. ))

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

Откуда у тебя "один запрос"?
Можно цитату?


S>Ибо на 2^N вариантов запроса хранимок не напасёшся.


И что помешало тебе самостоятельно пройти дисциплину "теория кодирования информации"?
Там несложно.

Как ты при проектировании борешься с избыточностью, не понимая самого этого понятия?
Блин, 2^N запросов ))

(на моей памяти минимум дважды в разные годы тебя поправляли по теме кодирования информации, надо было заполниь пробел после первого же раза)


V>>Т.е., в любом случае трудоёмкость сравнимая, т.к. сравнимая степень переиспользования кода.

V>>Просто приличная часть абстракций перетекает из runtime в compile-time, как оно принято в плюсах.
S>И то же самое можно делать в дотнете.

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


V>>Обычно плюсы или джава.

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

Как у тебя всё просто...
Если в джавовских программах доходит до эмуляции value-типов массивами байт, то можешь примерно представить степень вылизанности остального.
Хотя, всё-равно нубство, их LMAX Disruptor сосёт рядом с нашими решениями.

Я хорошо в курсе, что в дотнете так вылизывать пока мест не принято, к счастью.
И даже высмеивается.
Но чем громче высмеивается, тем нам лучше.


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

S>Можно посмотреть на прайс-лист?

Много тыщ в год за лицензию на каждую либу.
Прайс публично не висит, продажами занимаются сейлзы индивидуально, дать более точную инфу не могу себе позволить.
Отредактировано 03.09.2021 7:59 vdimas . Предыдущая версия . Еще …
Отредактировано 03.09.2021 5:26 vdimas . Предыдущая версия .
Отредактировано 03.09.2021 5:11 vdimas . Предыдущая версия .
Re[50]: MS забило на дотнет. Питону - да, сишарпу - нет?
От: vdimas Россия  
Дата: 03.09.21 05:17
Оценка:
Здравствуйте, Ночной Смотрящий, Вы писали:

V>>

V>>It is very efficient and owns the title of King of Micro ORM in terms of performance.

НС>Я в курсе что такое Dapper, не переживай.

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

Кароч, НС, сбавь обороты, реально.
Вдох-выдох, прогулки по свежему воздуху...
Бо я ж парень простой, рано или поздно начну называть вещи своими именами.
Re[51]: MS забило на дотнет. Питону - да, сишарпу - нет?
От: Ночной Смотрящий Россия  
Дата: 03.09.21 06:33
Оценка:
Здравствуйте, vdimas, Вы писали:

V>По мне, происходящее кристально чисто — ты за каким-то хреном набросил на вентилятор "да он 15 лет базы не видел", "оно уже давно протухло".


Так ты чуть позже сам написал, что 20 лет этим не занимался, какие претензии?

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


Ничего я не забыл. То что даппер для совместимости его до сих пор поддерживает, и они не стали переписывать его с нуля, как произошло с blt, еще не означает что датасеты в 2021 году еще актуальны.

V>Кароч, НС, сбавь обороты, реально.


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

V>Бо я ж парень простой, рано или поздно начну называть вещи своими именами.


Начнешь?
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[48]: MS забило на дотнет. Питону - да, сишарпу - нет?
От: Ночной Смотрящий Россия  
Дата: 03.09.21 09:45
Оценка:
Здравствуйте, vdimas, Вы писали:

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


И много таких баз данных в реальности?

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


Никогда не была. Изначално это была обертка над низкоуровневоцй библиотекой парсинга протокола (tdslib.dll или как то так).

V> как и для остального ADO.Net.


Из остального вспоминается только мост ADO->ADO.NET и МСовский драйвер оракла, который был актуален пока оракл собственным managed драйвером не разродился.

V>Но хинты уникальны для различных провайдеров.

V>Это почему DAL стоит затачивать под конкретного провайдера, а не под абстрактного, бо различные базы уникальны в своих фичах.

Хинты, как показывает практика, нужны хорошо если в 1% запросов. Это почему DAL не стоит затачивать под конкретного провайдера.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[48]: MS забило на дотнет. Питону - да, сишарпу - нет?
От: Sinclair Россия https://github.com/evilguest/
Дата: 03.09.21 10:26
Оценка: +1
Здравствуйте, vdimas, Вы писали:

V>Я привожу что есть по факту.

V>А то, что у тебя — это надо самим делать обертку поверх ODBC и там малость не так тривиально, но это уже подробности.
Я тоже привожу то, что есть по факту.

S>>Посмотрите, например, сюда: https://referencesource.microsoft.com/#system.data/Microsoft/SqlServer/Server/SqlDataRecord.cs


V>Не ту ссылку дал, надо смотреть сюда:

V>https://referencesource.microsoft.com/#system.data/fx/src/data/Microsoft/SqlServer/Server/MemoryRecordBuffer.cs,17
V>Путь данных еще более извилист и всё-равно данные еще дважды копируются.
Где они там дважды копируются? Там же лежит union.
V>И в любом случае твой пример только для MSSQL, а для любых других баз на основе OLEDB или ODBC будет как я дал ссылку на исходники дотнета.
Вы опять путаете дотнет как платформу и набор библиотек поверх платформы.
В дотнете есть IDataRecord, который можно реализовать очень эффективно. Вы же почему-то пишете про то, как в нативных приложениях мы используем нативные оптимизированные драйвера БД; а в дотнете вы хотите использовать максимально абстрактный код. Давайте тогда я в нативе буду использовать COM через IDispatch и конвертировать все данные через Variant.

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

V>linq2db — это самая вершинка айберга, пока данные до него дойдут — сто раз вспотеют.
Данные в него напрямую попадают из IDataRecord, поверх которого генерируется специфический для возвращаемого типа код. Как я показал, в реализации IDataRecord мы можем избегать копирования и выдавать данные напрямую из того буфера, который нам отдала СУБД в качестве результата.

V>А чем рендеринг в HTML/JSON принципиально отличается от рендеринга куда-то еще?

Тем, что нам не нужно бегать по датасету взад-вперёд. Это прямо противопоказано с точки зрения эффективности.

V>ОК, пусть даже в одну сторону.

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

V>Так вот, не придумано еще ничего быстрее чем разметить входной поток (он в любом случае парсится), а наружу отдавать данные согласно разметке.

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

V>Сорри, но ты сейчас малость из пальца насасываешь.

V>Оно примерно так и было, как есть сейчас.
Я говорю про Span<T>, Memory<T>, MemoryMarshal, ref struct, unmanaged constraint.
Это — революционные изменения в платформе, без которых бессмысленно говорить о каких-либо оптимизациях. Т.к. например банальный парсинг даты из хидера HTTP требовал сначала скопировать байты, затем превратить их в UTF16, и потом мучительно парсить — то есть речь о двойном копировании с двукратным раздуванием объёма.

V>Да и сам OLEDB драйвер к MS SQL когда-то был оберткой над ODBC-кодом их же драйвера, пока не написали родной для OLEDB.

Это всё и так понятно. Я и говорю — верхний уровень всё ещё работает на технологиях типа LayoutKind.Explicit с FieldOffset(0).

V>Не аргумент.

V>Таких отображений в этом "первом приложении" (которое магазин какой-нить) аж одно на каждую группу товаров.
Что такое "аж одно"? У тебя на N колонок 2N вариантов order by

S>>В каждом втором — кнопки "отфильтровать" в заголовках колонок.


V>Попадает в мой случай.

Что именно попадает в ваш случай? У вас будет N параметров к вашей хранимке, из которых M будет NULL?

S>>В каждом третьем — кнопки "скрыть/показать столбцы".


V>Где скрыть их можно на любом уровне, особенно если речь шла о клиенте.

V>Т.е. на уровне запроса к БД, на уровне сервера приложений, прямо на клиенте.

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

Ну вот и я о том же. Будет выполнен тормозной запрос с большим количеством join-ов, а потом половина вытащенных данных будет дропнута перед показом.
И вы ещё спрашиваете меня, почему я считаю белых неэффективными.

S>>Всё это — динамические запросы.


V>Ну вот смотри, я понаставил кучу флажков:

V>https://www.wildberries.ru/catalog/elektronika/telefony-i-gadzhety/mobilnye-telefony?sort=newly&amp;cardsize=c246x328&amp;page=1&amp;f10466=3722640%3B514814%3B3725238%3B3725535%3B18630%3B18632%3B18640%3B441183%3B3199823&amp;f90746=214513%3B142905%3B140423%3B140427%3B140428%3B172656%3B172657&amp;f75438=3698872%3B3698698%3B3749957%3B130329%3B3698382%3B174305%3B81543%3B81534%3B121020

V>Флажки булевские, т.е. кодируются битами.

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

V>Не, я примерно понимаю, на что ты намекал, на длиннющие условия навроде:

V>OR WorkTime=T133 OR WorkTime=T160 OR ...
V>И так по всем группам условий, не только по WorkTime...
V>И не говори, что ты на это не намекал, потому что только такое представление требует динамического построения. ))
Например — да.

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

V>На стороне базы маска займёт столько integer-полей, сколько требуется.
V>Допустим, аж пару BigInt, итого у нас комбинаторика по колву масок:
V>0 0
V>0 1
V>1 0
V>1 1
V>Всего 4 запроса.
V>Или два — 0 0 и 1 1, т.е., если есть хоть один бит — формируем маску целиком.
Отличная идея, коллега. Вы только что заставили движок вашей базы всегда делать table scan.
Потому, что другого способа сделать where (flags & @flags) > 0 не существует.
Это будет прекрасно приемлемо работать ровно до тех пор, пока у вас хватает кэша на то, чтобы держать всю базу в памяти.
Ну, и пока у вас набор всех значений всех атрибутов не очень велик — что позволяет уложить их в битики.

V>Показал.

Может, лучше показать схему БД вайлдберриз? Ну, чтобы посмотреть, как они хранят на самом деле.


V>Но хинты уникальны для различных провайдеров.

V>Это почему DAL стоит затачивать под конкретного провайдера, а не под абстрактного, бо различные базы уникальны в своих фичах.
Ну и прекрасно. Не вижу никаких противоречий.

V>И с чего ты решил, что это быстрее, даже если бы в некоей другой технологии тоже формировали такую же ужасную строку?

V>Судя по интонации подаваемого тобой — "просто нравится".
Быстрее получается тогда, когда мы не пишем внутри нашей хранимки код типа
... inner join customer c on c.id = o.customerID
where (c.Name like '%'&@customerName&'%' or (@customerName is null))

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

V>Если строки запросов те же — ничего не выигрываешь.

Ну так в том то и дело, что не те же.


V>Такую строку обычно не требуется порождать динамически, она обычно константа.

Это так кажется. Можно включить sql server profiler и убедиться, что значения агрументов (те самые ?) передаются не какой-то магией, а просто текстовой строкой. Которая точно так же клеится динамически.
И экономия достигается не за счёт константности, а за счёт того, что используется готовый план запроса из кэша.


V>Откуда у тебя "один запрос"?

V>Можно цитату?
Вы же сами приводите пример с prepared statement с фиксированным списком аргументов


V>И что помешало тебе самостоятельно пройти дисциплину "теория кодирования информации"?

V>Там несложно.
Встречный вопрос: что помешало почитать хоть что-то про устройство СУБД? Ну, то есть помимо понимания, какой именно SQL написать для получения результата, разобраться, как именно этот SQL исполняется?


V>Как ты при проектировании борешься с избыточностью, не понимая самого этого понятия?

V>Блин, 2^N запросов ))
А как вы при проектировании боретесь со стоимостью исполнения, с O(N) и O(N*M) вместо O(log(N))?

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

Повторюсь: для получения одного значения int одного поля при корректной реализации в дотнете будет сделан один виртуальный вызов без промежуточных копирований.
А если реализовать интерфейс в struct типе, то можно избавиться и от вызовов совсем. То есть при перекладывании данных из провайдера в JSON будет напрямую StringBuilder.Append(и вот тут заинлайненный код обрашения к _dataBufferPtr + currentRecordOffset + _fieldOffset).


V>Если в джавовских программах доходит до эмуляции value-типов массивами байт, то можешь примерно представить степень вылизанности остального.

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

V>Хотя, всё-равно нубство, их LMAX Disruptor сосёт рядом с нашими решениями.

А то.

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

V>И даже высмеивается.
V>Но чем громче высмеивается, тем нам лучше.
Понятия не имею, кем и что высмеивается. В дотнете вылизывать очень долго было нельзя. Сейчас появилась возможность; и люди ей пользуются.

V>Много тыщ в год за лицензию на каждую либу.

V>Прайс публично не висит, продажами занимаются сейлзы индивидуально, дать более точную инфу не могу себе позволить.
Ну хоть адрес лендинга ваших либ дайте — почитаю маркетинговые заманухи.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Отредактировано 03.09.2021 11:33 Sinclair . Предыдущая версия .
Re[49]: MS забило на дотнет. Питону - да, сишарпу - нет?
От: vdimas Россия  
Дата: 03.09.21 10:37
Оценка:
Здравствуйте, Ночной Смотрящий, Вы писали:

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

НС>И много таких баз данных в реальности?

А много используют MSSQL в вебе?


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

НС>Никогда не была. Изначално это была обертка над низкоуровневоцй библиотекой парсинга протокола (tdslib.dll или как то так).

Над MS SQL Native Client.
В котором доступны были 3 вида подключения — ODBC, OLEDB и еще не помню название вида подключения — подключаешься к схеме сервера как к таблицам и еще какие-то уникальные плюшки доступны в этом режиме по управлению серваком.

Разумеется, во всех режимах унутре протокол парсится, как и в любом нейтивном OLEDB или ODBC драйвере.
Последние версии этой либы под последние серваки и сегодня доступны.


V>>Но хинты уникальны для различных провайдеров.

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

Как показывает практика, 1% запросов используется в 99% случаев.
В нагруженных сценариях без хинтов база не живёт.
Re[40]: MS забило на дотнет. Питону - да, сишарпу - нет?
От: Sinclair Россия https://github.com/evilguest/
Дата: 03.09.21 11:30
Оценка:
Здравствуйте, vdimas, Вы писали:

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


V>На практике не нарываемся, если класс содержит только примитивные типы в полях.

V>Структуры в АПИ обычно идут по указателю, на стороне C# это ref для struct или просто классы по ссылке.
V>Маршаллер пинит экземпляры таких классов, вот и весь маршаллинг.
Плюс коррекция указателя. Ок.

V>Ограничений, считай, два всего:

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

V>>>В select в линухах подаются битовые массивы, поэтому stackalloc решает вопрос.

V>>>Длина битовых массивов идёт первым аргументом — как максимальный номер хендла.
S>>Ну это тривиальный частный случай. Как только мы встречаем что-то сложнее примитивного массива интов, начинаются танцы с бубном.

V>Такой же stackalloc начинается.

О, это интересно. То есть вы размечаете "структуру" в стеке как серию stackalloc? А есть гарантии их взаимного расположения?

V>Чтобы GC игнорировал стек нейтивных вызовов.

V>Т.е., возможна зебра вглубь всех вызовов:
V>- из управляемого кода в нейтив;
V>- оттуда приходит колбэк в управляемый код (многие перечисления в системных АПИ так работают);
V>- из этого фрейма опять вызывается что-то в нейтиве.
Ну в вашем-то случае весь метод — это инкремент лонга по указателю. Никаких колбэков, никаких зебр.

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

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

S>>Я на своём уровне некомпетентности вижу только одну глобальную проблему: нет, как такового, хотспоттинга. Tiered compilation выглядит как жалкое подобие левой руки.


V>Tiered compilation и есть hotspot.

Ну это очень вольная трактовка. Формально — да, но с тех пор, как появилась сама идея "оптимизируем только узкие места", её стандартная трактовка расширилась до "runtime profile-based dynamic optimization".
То есть "учитываем счётчик вызовов для применения tier1" — это очень, очень примитивный уровень. Чтобы, к примеру, заработал спекулятивный инлайнинг, нужно:
— прикрутить инструментирование кода для определения частотности call targets
— прикрутить умение JIT-тить инлайн ожидаемого таргета с guard condition
— прикрутить умение откатывать оптимизацию (или применять повторный JIT) при превышении порога частоты срабатывания guard condition.
Это одно стоит в разы больше, чем, собственно, tiered compilation. А ведь помимо этого в настоящем хотспоте (т.е. в profile guided optimization) ещё много чего прикручено.

V>В моём примере с IConfig до появления Tiered compilation джит сразу генерил свой оптимизированный, но глупый код.

V>Теперь этот код надо исполнять сколько-то раз в цикле, чтобы джит его соптимизировал, иначе там 1-в-1 с исходником.
Ну, именно поэтому BDN имеет фазу прогрева

V>Средней величины дотнетное приложение до него запускалось по 600-800 ms, сейчас примерно вдвое быстрее.

Ну почему же "ничего более". Прямо в анонсе показывали ускорение steady state, местами почти двукратное.

V>Особенно чувствительны к этой технологии те приложения, где инициализируется много статических данных (например, WPF и прочие, где описаны мильоны dependency property).

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

V>Спасёт только качественный AOT.

Это для десктопа. А для любимого мной бэкенда важен именно хотспот, т.к. никакой AOT не предскажет реальную нагрузку.
V>К нашей пенсии всё будет ОК, не переживай. ))
А вот я уверен, что и тогда будет к чему стремиться.

V>Ммм...

V>В общем, там несравнимые объемы и сложность кода, имелось ввиду это.
V>Если тебе Рослин кажется сложным проектом — ты сильно заблуждаешься.
V>Рослин простой как балалайка, хотя и достаточно объемный.
Ну так его пилили не столько лет, сколько GCC

V>И что странно — нормального оптимизирующего компилятора C# всё еще нет.

V>Т.е. слишком многое оставлено джиту такого, что можно причесать и до джит.
Да, с этим согласен.

V>Например, тот же пример с SomeClass<Config1> c1 = ...; c.Foo();

V>Если все типы объявлены в одной сборке, там нефик делать соптимизировать лишнее компилятором.
Да пусть хотя бы свой dup для value типов уберут А то смешно сказать — два совершенно семантически эквивалентных куска С# кода порождают совершенно разный бинарь. Ну куда это годится?

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

Как раз простые строковые вычисления компилятор делает сразу.

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

V>Итеративность в этом смысле привносит оперативность.
V>Т.е., в чём-то больше работы, да, но в целом происходящее оперативнее, т.к. некоторые бока/недоработки нового кода раньше вскрываются.
Ну о какой итеративности можно тут говорить? Они же не могли выпустить "рослин шарп 1.0" без генериков и прочих плюшек, и потом итеративно его деливерить.
Внутри проекта, понятное дело, были какие-то майлстоуны и дедлайны, но снаружи всё это не могло быть наблюдаемо.

V>Любому "светлому будущему" надо по классике отводить примерно 9 месяцев на что-то средней сложности и не более 18-ти для чего-то совсем кардинального.

V>А тут 5-6 лет возни.
Ну-ну
V>И не переубедишь. ))
Ну, может вам в Теслу пойти? А то с анонса автопилота прошло уже существенно больше 18 месяцев, а он всё ещё не ездит.

S>>Многие компании на этом вообще ломаются.


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

Да, шанс есть. Но надо ещё пилить, пилить, и пилить.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[49]: MS забило на дотнет. Питону - да, сишарпу - нет?
От: vdimas Россия  
Дата: 03.09.21 14:15
Оценка:
Здравствуйте, Sinclair, Вы писали:

V>>Я привожу что есть по факту.

V>>А то, что у тебя — это надо самим делать обертку поверх ODBC и там малость не так тривиально, но это уже подробности.
S>Я тоже привожу то, что есть по факту.

Так я попросил показать, что это за код, где живет, который
GetFieldType(i) == typeof(int)
      ? MemoryMarshal.GetReference(MemoryMarshal.Cast<byte, int>(_rawData.Slice(GetFieldOffset(i)));
      : Convert.ToInt32(GetValue(i));


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


V>>https://referencesource.microsoft.com/#system.data/fx/src/data/Microsoft/SqlServer/Server/MemoryRecordBuffer.cs,17

V>>Путь данных еще более извилист и всё-равно данные еще дважды копируются.
S>Где они там дважды копируются? Там же лежит union.

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


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

S>Вы опять путаете дотнет как платформу и набор библиотек поверх платформы.
S>В дотнете есть IDataRecord, который можно реализовать очень эффективно.

И все пишут в своих проектах собственные дрова к БД?
Или пользуются имеющимся в дотнете?

Куда я тебе показал в первый раз — это реализация ODBC драйвера.
Все базы имеют ODBC-дрова, но далеко не все имеют OLEDB.
Например, нет OLEDB драйвера к самой популярной в вебе базе MySQL.
Следующая по популярности идёт PostgreSQL, к ней тоже живые/актуальные только ODBC-дрова.


S>Вы же почему-то пишете про то, как в нативных приложениях мы используем нативные оптимизированные драйвера БД; а в дотнете вы хотите использовать максимально абстрактный код. Давайте тогда я в нативе буду использовать COM через IDispatch и конвертировать все данные через Variant.


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

"В нейтиве оптимизированные" — это ты только что придумал.
Дрова там обычные, бо парсить разметку любого протокола много ума не надо.
Оптимизированным получается сам сценарий зачитки данных, потому что максимально непосредственный.

Кстате, посмотрел OLEDB-реализацию в последних исходниках дотнета, там, действительно, читают через OLEDB акцессоры.
Но только странная ситуация выходит — под MSSQL свой кривой драйвер, под Oracle свой, популярные остальные базы доступны через ODBC.
Для кого остаётся OLEDB?
Для MS Access! ))

И еще меня крайне смущает объём велосипедостроения в каждом таком драйвере.
Там должно было быть до 80% общего кода, если не больше.


V>>linq2db — это самая вершинка айберга, пока данные до него дойдут — сто раз вспотеют.

S>Данные в него напрямую попадают из IDataRecord

Ну вот я тебе ссылку дал на кишки драйвера к MSSQL.
Это я уже молчу о том, что половину типов по той ссылке можно было сделать value-type.
По-факту там ад и ужас, нагрузка на GC на ровном месте.
Классическое "сделано на отгребись".


S>поверх которого генерируется специфический для возвращаемого типа код.


Не особо специфический — просто набор сгенерённых акцессоров к полям объекта (это "статические" объекты, т.е. для одного целевого entity создаются лишь однажды для каждого его поля), замапленный на колонки конкретного рекордсета. Если маппер достаточно умный, то при надобности вставит свои конвертеры, например где надо из Int16 в Int32, чтобы чтение уже скопированных и закешированных данных обходилось без боксинга/анбоксинга, как оно есть сейчас.

Т.е., в общем случае 3 вложенных виртуальных ф-ии на одно чтение уже закешированных данных.
А нет, это еще не чтение, это всё еще продолжается заполнение "модели" данными.
Чтение клиентским кодом будет когда-нибудь потом.

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

Сравнить с нейтивом, в метаинформации маппинга которого три указателя — смещение источника, смещение приёмника и адрес ф-ии преобразования (в простейшем случае — адрес простого копировщика на известное кол-во байт).

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

Да, в последней версии OLEDB "враппера" увидел похожу схему, с учётом испорченности дотнета — тоже примерно 4 вирутальных вызова на чтение одного поля.
Хотя, еще 2-3 года назад смотрел — был такой же мрак, как и в других провайдерах.


S>Как я показал, в реализации IDataRecord мы можем избегать копирования и выдавать данные напрямую из того буфера, который нам отдала СУБД в качестве результата.


Разумеется, можем.
Если бы принципиально не могли, не было бы обсуждения этой темы.


V>>А чем рендеринг в HTML/JSON принципиально отличается от рендеринга куда-то еще?

S>Тем, что нам не нужно бегать по датасету взад-вперёд. Это прямо противопоказано с точки зрения эффективности.

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

Просто прямое чтение может выполняться в оперативной манере, если размер данных больше, скажем, входного буфера сокета, т.е. данные будут извлечены из потока в несколько приёмов, между извлечениями можно заниматься чем-то полезным.
Что уже странно, кстате, бо обычно данные в современном мире передаются вменяемыми порциями — чтобы на экран вместилось.


V>>ОК, пусть даже в одну сторону.

V>>Я всё-равно не понимаю, зачем унутре реализации в дотнете создают копии данных для каждой строки.
S>"В дотнете" ничего не создают. Конкретные дата провайдеры пишутся в соответствии с представлениями о прекрасном каждой из команд.

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


S>Так и есть. Для обобщённой реализации — см. IDataRecord.


Я и смотрел, и ссылки тебе давал.


S>Для специфичной — делаем враппер, который достаёт данные согласно разметке.


Не так быстро.
Не "враппер", а полноценный драйвер.
Например, полностью своя подмена драйвера ODBC.

То бишь, самим реализовать всё семейство с 0-ля: IDbConnection, IDbTransaction, IDbCommand и еще пару десятков (если не больше) сущностей из модели ADO.Net.


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


Я тебя уже 3-й раз прошу дать координаты этого "нового стиля".
Исходники же дотнета открыты, какие проблемы?

Вот тебе их ODBC "враппер":
https://github.com/dotnet/runtime/blob/main/src/libraries/System.Data.Odbc/src/System/Data/Odbc/DbDataRecord.cs#L43
На Линухах у разработчика будет задействован именно он с вероятностью 99%.

И мне даже несколько странно, что ты не поддерживаешь меня в моём стремлении расстрелять без объяснения причин авторов этого кода. ))
Там всё 5-тилетней давности, т.е. как взяли из исходников дотнета при почковании .Net Core, так оно там и лежит.

OLEDB, смотрю, действительно пару лет назад причесали.
Жаль, в линухах его нет.


V>>Сорри, но ты сейчас малость из пальца насасываешь.

V>>Оно примерно так и было, как есть сейчас.
S>Я говорю про Span<T>, Memory<T>, MemoryMarshal, ref struct, unmanaged constraint.
S>Это — революционные изменения в платформе, без которых бессмысленно говорить о каких-либо оптимизациях. Т.к. например банальный парсинг даты из хидера HTTP требовал сначала скопировать байты, затем превратить их в UTF16, и потом мучительно парсить — то есть речь о двойном копировании с двукратным раздуванием объёма.

Зачем "скопировать"?
StreamReader поверх сокета был всегда.
А потом еще и асинхронный давно.


V>>Таких отображений в этом "первом приложении" (которое магазин какой-нить) аж одно на каждую группу товаров.

S>Что такое "аж одно"? У тебя на N колонок 2N вариантов order by

Не, сортировка в магазинах по ограниченному кол-ву критериев: по цене, популярности, рейтингу.
И почти никогда нет последовательной сортировки, т.е. сортировка практически во всех популярных (проходных) онлайн-магазинах по одному из предоставленных критериев в одну из сторон, на этом с сортировкой всё.


V>>Попадает в мой случай.

S>Что именно попадает в ваш случай? У вас будет N параметров к вашей хранимке, из которых M будет NULL?

ЧТД.
Почему база передаёт NULL-поля в виде битового вектора, и почему я не могу поступать так же?


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

S> Ну вот и я о том же. Будет выполнен тормозной запрос с большим количеством join-ов

Без джоинов.
Онлайн-магазины — это почти всегда розница.
Там карточный учёт, а не средневзвешенный.
Т.е. карточка целиком содержит информацию о товаре.

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


S>а потом половина вытащенных данных будет дропнута перед показом.


Я бы хотел посмотреть на живой пример, где это так.


S>И вы ещё спрашиваете меня, почему я считаю белых неэффективными.


Я считаю, что ты надумываешь условия под ответ.
Предлагаю поступать наоборот.


V>>Всего 4 запроса.

V>>Или два — 0 0 и 1 1, т.е., если есть хоть один бит — формируем маску целиком.
S>Отличная идея, коллега. Вы только что заставили движок вашей базы всегда делать table scan.

Если бы.

Когда партиции булевские, то на десятках-сотнях этих индексов от булевых полей ничего не выигрывается.
Индексам для пользы от них требуется хоть какая-то "ширина" и самих индексов желательно немного.
Т.е., если простая сумма всех уникальных значений всех индексов сравнимо с кол-вом данных — выигрыша от индекса не будет.
(не считая partition key, а он наверняка будет суррогатный ID карточки какой-нить, т.е. не играет рояли в рассматриваемых запросах).

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


S>Потому, что другого способа сделать where (flags & @flags) > 0 не существует.


1. Только надо на не равно нулю проверять.
2. Ставлю на то, что ты заблуждаешься.
3. Десятки-сотни индексов по булевым или совсем "узким" данным всё-равно ничего не дадут в плане эффективности.

Можно поэкспериментировать с планами и посмотреть.


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


И опять эта демагогия.
Во-первых, предложенный тобой способ всё-равно медленнее/затратнее.
Во-вторых, вытесняет кеш скомпиллированных запросов из базы, т.к. каждая из 2^N комбинаций уникальная.
В-третьих, онлайн-базы на жёстких дисках уже много лет как не живут, а обращения к флеш-накопителю, да еще всегда через рейд — это не сильно медленее, чем из оперативы.

И да, кол-во товаров всегда конечное.
"Бесконечные" движения по ним, но в движениях уже никаких 2^N комбинаций изобретать не надо.
Как делать "универсальный движитель" по складу я тебе как-то показывал.


S>Ну, и пока у вас набор всех значений всех атрибутов не очень велик — что позволяет уложить их в битики.


Не у меня, а у всех их немного.
Я тебе дал ссылку на самый продвинутый магазин именно в плане фильтрации-поиска товаров.
На wildberries заходят не за чем-то конкретным, а "просто посмотреть, что в мире эдакого есть", он заточен именно под такой сценарий.
Т.е. вряд ли ты найдёшь магазин с большим кол-вом управляемых признаков выборки.

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


V>>Показал.

S>Может, лучше показать схему БД вайлдберриз? Ну, чтобы посмотреть, как они хранят на самом деле.

А какая разница?
В мире полно плохих и хороших решений.
Предлагаешь положиться в споре на случайность, приводить в пример отдельных двоечников или отличников как док-во чему бы то ни было?
Со мной не прокатит.


V>>И с чего ты решил, что это быстрее, даже если бы в некоей другой технологии тоже формировали такую же ужасную строку?

V>>Судя по интонации подаваемого тобой — "просто нравится".
S>Быстрее получается тогда, когда мы не пишем внутри нашей хранимки код типа

Не-не-не, способ общения с базой ортогонален способу формирования условий на клиенте.
Клиент всё-равно пошлёт по своему внутреннему протоколу серверу приложений всё эти флаги-ограничения для поиска товаров, а как сервак эти флаги преобразует в запрос — про это клиент и знать не будет.
(например, речь об андроидном приложении вайлдберриз)


S>
S>... inner join customer c on c.id = o.customerID
S>where (c.Name like '%'&@customerName&'%' or (@customerName is null))
S>


Я имел ввиду, что если запрос формируется не через Linq, а непосредственной конкатенацией строк, почему это должно работать медленнее?


V>>Если строки запросов те же — ничего не выигрываешь.

S>Ну так в том то и дело, что не те же.

Это ты опять надумываешь себе что-то.
Даже если упомянули самый эффективный и от того самый популярный способ обращения к БД через синтаксис вызова хранимки в ODBC, это не значит, что других способов нет.
Это просто способ, к которому неплохо бы привести всё что приводится.

А я тут пока упражняюсь в дисциплине задействования хранимки для вывода конкретно экрана обзора/выбора товаров, потому что это самый что ни на есть жирный пример против хранимки.
Это ж вызов! ))
Вот и упражняюсь.
На других экранах: корзина, доставка/трекинг, история покупок, платежей, общений с персоналом и т.д. и т.п. всё проще, нифига не вызов.

И что характерно, в моей модели битовых флагов функциональность хорошо повторно-используема для большинства групп товаров.
Просто "метаинформация" относительно семантики бит для каждой группы своя (но это уже очевидные вещи).


V>>Такую строку обычно не требуется порождать динамически, она обычно константа.

S>Это так кажется. Можно включить sql server profiler и убедиться, что значения агрументов (те самые ?) передаются не какой-то магией, а просто текстовой строкой. Которая точно так же клеится динамически.

Хреновый драйвер, значит, или специально для тебя в текст отрендерили, чтобы тебе было понятней.
ODBC и OLEDB умеют передавать именно команды на вызовы процедур с аргументами по своему протоколу.
Да еще с такими, которые в текст не так-то просто перевести, например, binary.


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


Вооот.
Но там на всём экономия.
И на сервере сразу понятно, что это вызов хранимки, и на клиенте формировать команду проще.


V>>Откуда у тебя "один запрос"?

V>>Можно цитату?
S>Вы же сами приводите пример с prepared statement с фиксированным списком аргументов

Я все-равно не понял, почему у меня не может быть несколько хранимок?


S>Встречный вопрос: что помешало почитать хоть что-то про устройство СУБД?


Да, так что помешало?


S>Ну, то есть помимо понимания, какой именно SQL написать для получения результата, разобраться, как именно этот SQL исполняется?


Ага, особенно когда у нас значения комбинируются по OR по десяткам индексов.
Ты эта, поиграй с планами на досуге.


V>>Как ты при проектировании борешься с избыточностью, не понимая самого этого понятия?

V>>Блин, 2^N запросов ))
S>А как вы при проектировании боретесь со стоимостью исполнения, с O(N) и O(N*M) вместо O(log(N))?

В случае OR по M признакам будет O(log(N)*M)
При том что N нифига к бесконечности не стремится, т.к. у нас в работе всегда некая одна группа товаров.

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


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

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

Примерно три в лучшем случае.


S>А если реализовать интерфейс в struct типе, то можно избавиться и от вызовов совсем.


И где можно на такую реализацию посмотреть?


S>То есть при перекладывании данных из провайдера в JSON будет напрямую StringBuilder.Append(и вот тут заинлайненный код обрашения к _dataBufferPtr + currentRecordOffset + _fieldOffset).


Да я ж не против, я ж сам всякие трюки парой сообщений выше показывал.
Я про текущее состояние отрасли спрашиваю.

Про то, откуда взялось "если обращаемся к базе, то экономить на чём бы то ни было бессмысленно".
И ведь верное утверждение для текущего состояния.


V>>Если в джавовских программах доходит до эмуляции value-типов массивами байт, то можешь примерно представить степень вылизанности остального.

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

Возможно.
Но при разогреве такие вылизанные сервера пока мест уделывают дотнетные.
Кроме наших, конечно. ))

Нам сейчас мешает то, что на серверной стороне дотнету пока доверия нет в плане надёжности.
И это не от нас зависит.
Зато клиентские либы раскупаются неплохо.
Т.е., .Net Core, таки, "выстрелил".


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


Ес-но. И код пишется под джит и тщательнейшим образом профилируется, благо в джаве репорты от джита подробные и вменяемые, а в дотнете джит — чёрный ящик.
И я хорошо понимаю — почему.
Стоит отлить из бетона какой-нить публичный АПИ к джиту, и эту гирю на ногах так просто потом не сбросишь.
В общем, на системном уровне дотнет пока мест находится в стадии поиска себя.


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

V>>И даже высмеивается.
V>>Но чем громче высмеивается, тем нам лучше.
S>Понятия не имею, кем и что высмеивается.

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


S>В дотнете вылизывать очень долго было нельзя. Сейчас появилась возможность; и люди ей пользуются.


Да можно было.
Реинтерпретация памяти и до Span-времён работала неплохо.
Просто теперь к ней подключили реинтерпретацию управляемой памяти.
Раньше в этих сценариях тупо пинили.
Но всё-равно в дотнете всегда всё пинится.
Создаётся копия строки (именно копия), или строка копируется в буфер — пинятся оба конца.
Преобразуется из UTF16 в UTF8 — тоже пинятся источник и приёмник.
Под капотом там обычный unsafe и указатели.
Именно поэтому теперь моя либа по конверсии строк стала не нужна. ))


V>>Много тыщ в год за лицензию на каждую либу.

V>>Прайс публично не висит, продажами занимаются сейлзы индивидуально, дать более точную инфу не могу себе позволить.
S>Ну хоть адрес лендинга ваших либ дайте — почитаю маркетинговые заманухи.

Не уверен, что стоит публично это делать, в той области know how — понятие очень нервное.
Если прям сильно интересно — стукни в личку, дам под честное слово.
Отредактировано 04.09.2021 8:38 vdimas . Предыдущая версия . Еще …
Отредактировано 04.09.2021 8:37 vdimas . Предыдущая версия .
Отредактировано 03.09.2021 18:14 vdimas . Предыдущая версия .
Отредактировано 03.09.2021 18:08 vdimas . Предыдущая версия .
Отредактировано 03.09.2021 18:05 vdimas . Предыдущая версия .
Отредактировано 03.09.2021 17:53 vdimas . Предыдущая версия .
Re[47]: MS забило на дотнет. Питону - да, сишарпу - нет?
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 03.09.21 14:43
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Здравствуйте, Ночной Смотрящий, Вы писали:


V>>>Dapper (который маппер поверх ADO.Net) для несложных/одноразовых вещей используют аж бегом,

НС>>Dapper был написан в те самые времена, когда оно было актуально. Так и осталось.

V>Ты озвучил "протухло", я показал пример, где отнюдь.

На самом деле DataTable в начале на Core не полностью работал ибо не была прикручена xml сериализация десериализация.
Поэтому был не полностью совместим. Сама то идея интересная на малых объемах учитывая обмен данными на xml.
Проблемы были в огромных размеров данных.
и солнце б утром не вставало, когда бы не было меня
Re[5]: MS забило на дотнет. Питону - да, сишарпу - нет?
От: koandrew Канада http://thingselectronic.blogspot.ca/
Дата: 03.09.21 15:08
Оценка: :)
Здравствуйте, Sharov, Вы писали:

S>Динамика + много библиотек.


Рецепт для write-only говнокода, коим и является большинство питоновских, прости Господи, "приложений".
[КУ] оккупировала армия.
Re[41]: MS забило на дотнет. Питону - да, сишарпу - нет?
От: vdimas Россия  
Дата: 03.09.21 15:11
Оценка:
Здравствуйте, 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 неплохо заходит для "ручных" парсеров и т.д.
Отредактировано 03.09.2021 15:13 vdimas . Предыдущая версия . Еще …
Отредактировано 03.09.2021 15:12 vdimas . Предыдущая версия .
Re[48]: MS забило на дотнет. Питону - да, сишарпу - нет?
От: vdimas Россия  
Дата: 03.09.21 15:23
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>На самом деле DataTable в начале на Core не полностью работал ибо не была прикручена xml сериализация десериализация.

S>Поэтому был не полностью совместим. Сама то идея интересная на малых объемах учитывая обмен данными на xml.

Даже без XML DataSet подкупает быстрым временем холодного старта.
Для клиентского приложения это критично, плюс на клиенте собственных данных обычно немного.
Re[42]: MS забило на дотнет. Питону - да, сишарпу - нет?
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 03.09.21 15:26
Оценка:
Здравствуйте, vdimas, Вы писали:


V>Случаев сразу несколько.

V>Раньше был доступен stackalloc только под примитивные blit-типы.
V>Например, делаешь byte * b = stackallock byte[N], затем приводишь к указателю на структуру.

На самом деле есть public fixed byte
https://stackoverflow.com/questions/13001327/id-like-to-use-union-as-c-sharp-with-byte-array

А из него можно уже десериализовать сериализовать объекты
ну и юнионы тоже
[StructLayout(LayoutKind.Explicit, Size = 12)]
public unsafe struct UnionPacket
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)]
    [FieldOffset(0)]
    public fixed byte data[12];

    [FieldOffset(0)]
    public float Time;
    [FieldOffset(4)]
    public Int16 CoordX;
    [FieldOffset(6)]
    public Int16 CoordY;
    [FieldOffset(8)]
    public byte Red;
    [FieldOffset(9)]
    public byte Green;
    [FieldOffset(10)]
    public byte Blue;
    [FieldOffset(11)]
    public byte Alpha;
}


https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/unsafe-code

Кстати для интереса какая разница вызова localMultiply,Combine, UnsafeCombine и из натива
public static T Combine<T>(Func<T, T, T> combinator, T left, T right) => 
    combinator(left, right);

public static T UnsafeCombine<T>(delegate*<T, T, T> combinator, T left, T right) => 
    combinator(left, right);


static int localMultiply(int x, int y) => x * y;
int product = UnsafeCombine(&localMultiply, 3, 4);
и солнце б утром не вставало, когда бы не было меня
Отредактировано 03.09.2021 16:32 Serginio1 . Предыдущая версия . Еще …
Отредактировано 03.09.2021 15:33 Serginio1 . Предыдущая версия .
Re[6]: MS забило на дотнет. Питону - да, сишарпу - нет?
От: Sharov Россия  
Дата: 03.09.21 15:32
Оценка:
Здравствуйте, koandrew, Вы писали:

S>>Динамика + много библиотек.

K>Рецепт для write-only говнокода, коим и является большинство питоновских, прости Господи, "приложений".

Рецепт для небольших приложений\скриптов, коими является как минимум большиyство приложений ds.
Едва ли будет более 1000 строк кода. А в среднем 100-200 строк кода, чтобы разобраться с данными
и обучить модель. Самое то.
Кодом людям нужно помогать!
Re[49]: MS забило на дотнет. Питону - да, сишарпу - нет?
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 03.09.21 15:39
Оценка:
Здравствуйте, vdimas, Вы писали:

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


S>>На самом деле DataTable в начале на Core не полностью работал ибо не была прикручена xml сериализация десериализация.

S>>Поэтому был не полностью совместим. Сама то идея интересная на малых объемах учитывая обмен данными на xml.

V>Даже без XML DataSet подкупает быстрым временем холодного старта.

V>Для клиентского приложения это критично, плюс на клиенте собственных данных обычно немного.
Поверь мне идея DataTable очень понравилась и я его использовал везде!
Ну сейчас Source generator так любимый С++ compile tyme code generator.
Просто можно заранее делать структуру со свойствами над массивом байтов.
и солнце б утром не вставало, когда бы не было меня
Re[43]: MS забило на дотнет. Питону - да, сишарпу - нет?
От: vdimas Россия  
Дата: 03.09.21 16:51
Оценка:
Здравствуйте, Serginio1, Вы писали:

V>>Случаев сразу несколько.

V>>Раньше был доступен stackalloc только под примитивные blit-типы.
V>>Например, делаешь byte * b = stackallock byte[N], затем приводишь к указателю на структуру.
S> На самом деле есть public fixed byte
S>https://stackoverflow.com/questions/13001327/id-like-to-use-union-as-c-sharp-with-byte-array
S> А из него можно уже десериализовать сериализовать объекты

Речь шла о структурах переменного размера.
У таких структур обычно только некий заголовок известен.

S>ну и юнионы тоже


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


S> Кстати для интереса какая разница вызова localMultiply,Combine, UnsafeCombine и из натива


Я исходники бенчмарка давал, можно добавлять свои тесты.

Например, я забыл про UnmanagedCallersOnly.
Т.е. в тест надо добавить статический метод, помеченный этим атрибутом, получить unmanaged-указатель на этот метод и завернуть по аналогии с остальными случаями в обертку для тестирования.

Хотя, подозреваю, что быстродействие вызова UnmanagedCallersOnly-метода должно быть примерно на уровне DllImport.
Отредактировано 04.09.2021 8:07 vdimas . Предыдущая версия .
Re[50]: MS забило на дотнет. Питону - да, сишарпу - нет?
От: Sinclair Россия https://github.com/evilguest/
Дата: 03.09.21 18:01
Оценка: +2
Здравствуйте, vdimas, Вы писали:

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


V>>>Я привожу что есть по факту.

V>>>А то, что у тебя — это надо самим делать обертку поверх ODBC и там малость не так тривиально, но это уже подробности.
S>>Я тоже привожу то, что есть по факту.

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

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

Примерно такой код и живёт
V>И да, если читать из поля Int16, например, Int32, то опять пляшем через боксинг, верно?
Смотря как делать. Но это — не очень важный аспект: поля int16 в базах встречаются нечасто. А там, где встречаются, разработчики знают, зачем их применяют, и не будут делать конверсию на том конце, который сделает её медленнее.

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

Вот не вижу этого момента "из приёмного буфера".
У меня такое ощущение, что показано не всё. По крайней мере, в документации указано, что SmiRecordBuffer подразумевает эффективные реализации для in-proc сценариев, но в референс сорцах виден только один наследник, который помечен как "используем для out-of-proc".

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

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

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

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

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

V>Все базы имеют ODBC-дрова, но далеко не все имеют OLEDB.
V>Например, нет OLEDB драйвера к самой популярной в вебе базе MySQL.
Эмм, причём тут OLEDB? Мы же вроде бы про дотнет говорим. И кто вам сказал, что драйвера OLEDB внутри устроены как-то сильно эффективнее, чем MemoryRecordBuffer? Это же COM — значит, никакого инлайнинга вызовов GetData; да и сигнатура у его методов подразумевает копирование в caller-allocated storage. Ну, и где тут хвалёный С++ с его возможностью "вернуть сразу данные из буфера без промежуточных копирований"?
Так что давайте мы не будем рассматривать OLE DB в качестве какой-то особенно быстрой альтернативы дотнету.
А если мы посмотрим на дотнет, то есть, к примеру, System.Data.Sqlite.org. Он работает поверх SQLite, безо всякого ODBC или OLEDB. Или, скажем, вряд ли же какой-нибудь ДевАртовский дотконнект SQLite написан как-то сильно плохо. А если разработчика устраивает дефолтный System.Data.Sqlite, у которого в аннотации написано "простенький провайдер поверх ADO с изрядными ограничениями", то кто ему виноват?
V>Следующая по популярности идёт PostgreSQL, к ней тоже живые/актуальные только ODBC-дрова.
Для PostgreSQL живы/актуальны нативные дотнетные дрова, 100% написанные на C#. Судя по всему — тоже не очень плохо написанные: https://www.npgsql.org/

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

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

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

Со временем, может быть, и будет. А может быть — нет. Потому что какой-нибудь SQLite вообще буфера клиенту не отдаёт — зачем ему? У него все манипуляции делаются прямыми вызовами в inproc DLL.
А устройство протоколов обмена данными для каждого вендора, мягко говоря, очень своё.
Если мы захотим сделать что-то более оптимальное, чем дефолтный драйвер поверх универсального коннектора типа OLE DB или ODBC, то придётся погрузится вот в эти подробности.

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

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

S>>поверх которого генерируется специфический для возвращаемого типа код.


V>Не особо специфический — просто набор сгенерённых акцессоров к полям объекта (это "статические" объекты, т.е. для одного целевого entity создаются лишь однажды для каждого его поля), замапленный на колонки конкретного рекордсета. Если маппер достаточно умный, то при надобности вставит свои конвертеры, например где надо из Int16 в Int32, чтобы чтение уже скопированных и закешированных данных обходилось без боксинга/анбоксинга, как оно есть сейчас.

Имеется в виду, что для каждого пользовательского типа QueryResult и набора колонок исходного запроса должен порождаться примерно вот такой вот код:
public record QueryResult(string, decimal);
public IEnumerable<QueryResult> GetQueryResult<R>(R r) where R: IDataReader
{
  while(r.Read())
    yield return new QueryResult(r.GetString(1), r.GetDecimal(0));
}

Примерно это делает Linq2db. Если IDataReader реализован так, как вы хотите (путём парсинга протокола), то всё будет близко к максимально оптимальному. В суровом настоящем для того, чтобы заинлайнить тут вызовы, нужно делать IDataReader структ-типом; в неопределённом светлом будущем (которое в джаве уже наступило) инлайнинг выполняется спекулятивно благодаря тому, что реальный тип R виден среде исполнения.

V>Т.е., в общем случае 3 вложенных виртуальных ф-ии на одно чтение уже закешированных данных.

V>А нет, это еще не чтение, это всё еще продолжается заполнение "модели" данными.
V>Чтение клиентским кодом будет когда-нибудь потом.
Ну так в OLE DB — то же самое.

V>Это для динамических противопоказано, а для снапшотных до фени.

Не шибко дружественно к кэшу, да и потребности такой нет.

V>Что уже странно, кстате, бо обычно данные в современном мире передаются вменяемыми порциями — чтобы на экран вместилось.

Ну, так на современный экран вмещаются мегабайты — мы ж не только текст показываем

V>По-идее, дефолтная реализация должна была давать пример остальным, то бишь, представлять из себя самое лучшее референсное воплощение...

По идее — да. Но не получится, т.к. в OLE DB и ODBC нет подобной возможности. Поверх чего будем строить "референсное воплощение"?

S>>Для специфичной — делаем враппер, который достаёт данные согласно разметке.


V>Не так быстро.

V>Не "враппер", а полноценный драйвер.
V>Например, полностью своя подмена драйвера ODBC.

V>То бишь, самим реализовать всё семейство с 0-ля: IDbConnection, IDbTransaction, IDbCommand и еще пару десятков (если не больше) сущностей из модели ADO.Net.

Ну, это мы как раз говорим об обобщённом доступе — когда в компайл-тайм мы как бы не знаем настоящих типов данных и их разметки.

V>Я тебя уже 3-й раз прошу дать координаты этого "нового стиля".

V>Исходники же дотнета открыты, какие проблемы?
Пока что во взаимодействии с базами ничего не произошло. В основном оптимизируют HTTP стек.
V>Вот тебе их ODBC "враппер":
V>https://github.com/dotnet/runtime/blob/main/src/libraries/System.Data.Odbc/src/System/Data/Odbc/DbDataRecord.cs#L43
V>На Линухах у разработчика будет задействован он с вероятностью 99%.
V>И мне даже несколько странно, что ты не поддерживаешь меня в моём стремлении расстрелять без объяснения причин авторов этого кода. ))
Вот в данном месте я не понимаю, что именно там можно улучшить.
С учётом того, что ODBC не даёт читать данные дважды, а IDataReader — даёт.
То есть если бы мы писали ORM прямо поверх ODBC, а не поверх ADO.NET поверх ODBC, то можно было бы попытаться взять конкретный пользовательский тип, unsafe способом раскопать его layout, прибиндить к его экземпляру колонки фиксированных размеров и делать SQLFetch на каждый next. Да и то, я сходу не вижу способа сделать это всё достаточно надёжным способом — так, чтобы это работало с приемлемой скоростью и не взрывалось от незначительного обновления в используемом у клиента драйвере.

V>>>Сорри, но ты сейчас малость из пальца насасываешь.

V>>>Оно примерно так и было, как есть сейчас.
S>>Я говорю про Span<T>, Memory<T>, MemoryMarshal, ref struct, unmanaged constraint.
S>>Это — революционные изменения в платформе, без которых бессмысленно говорить о каких-либо оптимизациях. Т.к. например банальный парсинг даты из хидера HTTP требовал сначала скопировать байты, затем превратить их в UTF16, и потом мучительно парсить — то есть речь о двойном копировании с двукратным раздуванием объёма.

V>Зачем "скопировать"?

V>StreamReader поверх сокета был всегда.
V>А потом еще и асинхронный давно.
И? Ну вот где-то "в сокете" лежит (предположительно) дата в формате RFC2616. Надо получить DateTime. У вас — фреймворк версии 4.5. Что именно вы собрались делать со StreamReader-ом?

V>Не, сортировка в магазинах по ограниченному кол-ву критериев: по цене, популярности, рейтингу.

V>И почти никогда нет последовательной сортировки, т.е. сортировка практически во всех популярных (проходных) онлайн-магазинах по одному из предоставленных критериев в одну сторону, на этом с сортировкой всё.
Ну так и ветер дует вовсе не потому, что деревья качаются. Просто разработчики магазинов велосипедят именно такой вот код, в котором невозможно сделать последовательную сортировку.
А не потому, что их пользователи попросили "нельзя ли сделать вот так, через жопу, а?"

V>ЧТД.

V>Почему база передаёт NULL-поля в виде битового вектора, и почему я не могу поступать так же?
Потому что надо смотреть в query plan, который будет получаться.

V>Без джоинов.

V>Онлайн-магазины — это почти всегда розница.
Я что-то не понял, каким это способом мы от всех UI всех приложений вообще внезапно перешли к единственному гую "список товаров".
V>Речь может идти только о проекциях, т.к., например, подробные технические характеристики в обзорный список товаров обычно не тащат, это уже когда открывают карточку выбранного товара на экране.
Ну так их зачастую и в основную таблицу не тащат, потому что нет желания её раздувать.

S>>а потом половина вытащенных данных будет дропнута перед показом.


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



S>>И вы ещё спрашиваете меня, почему я считаю белых неэффективными.


V>Я считаю, что ты надумываешь условия под ответ.

V>Предлагаю поступать наоборот.


V>Если бы.


V>Когда партиции булевские, то на десятках-сотнях этих индексов от булевых полей ничего не выигрывается.

Смелая идея. Во-первых, далеко не все партиции — булевские.
Во-вторых, когда их много, и они рассеянные — как раз таки выигрывается дофига. Потому что каждая отдельная редкая фича встречается у малой доли товаров.
Следите за руками: я беру список без фильтра, https://www.wildberries.ru/catalog/elektronika/smartfony-i-telefony/vse-smartfony. 1401 товар.
Включаю, скажем, объём памяти 8GB: https://www.wildberries.ru/catalog/elektronika/smartfony-i-telefony/vse-smartfony?page=1&amp;f4424=12864 — уже 8 товаров.
Селективность — 150x! Как бы есть за что побороться, не так ли?

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

V>Т.е., если простая сумма всех уникальных значений всех индексов сравнимо с кол-вом данных — выигрыша от индекса не будет.
Выигрыш от индекса определяется его селективностью. Количество различных значений — это, конечно, здорово, но слишком грубо. Нужно анализировать распределение.
То есть когда я попросил 8GB памяти, я искал не по булевому полю "Is8Gb", а по полю "RAM Size", у которого значительно больше значений, чем 2, и они распределены по Зипфу.
Даже если мы возьмём какое-нибудь честное булевое поле, типа "Есть беспроводная зарядка", то селективность может запросто оказаться значительно лучше, чем 50%.

V>Тут наоборот, все биты надо собрать в одно-два поля и по ним сделать индекс.

V>И тогда будет столько прыжков по индексу, сколько значащих бит в маске.
Кажется, я догадался, что вы имеете в виду. Вы хотите построить bitmap index? Ну, там "прыжки по индексу" выглядят вовсе не так дёшево, как кажется. Их не особо рекомендуют для случаев, когда битов сильно много.
Я ручаюсь, что у wildberries нет никаких битмап индексов внутри — там явно банальный EAV в сочетании с well-known attributes. Но это всё опять — от бедности, когда у нас не данные, а слабоструктурированная помойка.

V>1. Только надо на не равно нулю проверять.

Ну мы же здоровые люди — у нас битовые маски хранятся в unsigned
V>2. Ставлю на то, что ты заблуждаешься.
V>3. Десятки-сотни индексов по булевым или совсем "узким" данным всё-равно ничего не дадут в плане эффективности.
Конечно.
V>Можно поэкспериментировать с планами и посмотреть.
Естественно. Никто в здравом уме не будет собирать честные таблицы из сотен колонок и заводить тысячи индексов. Но и заводить отдельный блок битмап-индекса размером в мегабайт каждый раз, как криворукий продавец напишет очередной "blueooh" в значение атрибута тоже никто не будет.

Рулит вдумчивый анализ структуры данных и подготовка таблиц. А затем, в процессе эксплуатации — подтюнивание индексов. И вот тут очень важно, чтобы клиентское приложение давало СУБД простор для манёвра.
\
S>>Это будет прекрасно приемлемо работать ровно до тех пор, пока у вас хватает кэша на то, чтобы держать всю базу в памяти.

V>Во-вторых, вытесняет кеш скомпиллированных запросов из базы, т.к. каждая из 2^N комбинация уникальная.

Конечно уникальная. Но они — вовсе не равновероятны. В реальности одномоментно живут не так уж много комбинаций.

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

Это всё в теории. На практике HDD никуда не делись. И чем больше объём ворочаемых данных, тем выгоднее оптимизировать софт, чем заливать его железом.

V>"Бесконечные" движения по ним, но в движениях уже никаких 2^N комбинаций изобретать не надо.

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

V>Не у меня, а у всех их немного.

V>Я тебе дал ссылку на самый продвинутый магазин именно в плане фильтрации-поиска товаров.
Эта голимая убогость? Вайлдберриз как раз знаменит тем, что в нём адски сложно искать.
V>На wildberries заходят не за чем-то конкретным, а "просто посмотреть, что в мире эдакого есть", он заточен именно под такой сценарий.
Совершенно точно — их убогие поисковые возможности не дают найти что-то конкретное.
V>Т.е. вряд ли ты найдёшь магазин с большим кол-вом управляемых признаков выборки.
Относительно нормальный поиск по товарам был на яндекс.маркете. Я туда давно не ходил, не знаю — может, тоже слили в говно. Но, когда ходил, там порнографии типа "38 вариантов написания слова bluetooth" не было.
И если обувь ищешь по размеру, то нет риска нарваться на то, что поставщик указал "34 " и товар не попал в поисковую выдачу.
V>В общем, этих флагов всегда относительно немного, иначе этим магазином невозможно будет пользоваться.
V>Человеку показывается такое кол-во информации и органов управления, которые он принципиально сможет переварить.
V>И желательно до того как психанёт и закроет сайт. ))
Клёво вы придумываете. А можно же не фантазировать, а пойти и посмотреть: https://market.yandex.ru/catalog--vstraivaemye-posudomoechnye-mashiny-v-novosibirske/58608/filters?cpa=0&amp;hid=90584&amp;onstock=1&amp;local-offers-first=0

V>Предлагаешь положиться в споре на случайность, приводить в пример отдельных двоечников или отличников как док-во чему бы то ни было?

Ну, вы же приводите в пример двоечников типа вайлдберриз.

V>Не-не-не, способ общения с базой ортогонален способу формирования условий на клиенте.

С чего это?
V>Клиент всё-равно пошлёт по своему внутреннему протоколу серверу приложений всё эти флаги-ограничения для поиска товаров, а как сервак эти флаги преобразует в запрос — про это клиент и знать не будет.
V>(например, речь об андроидном приложении вайлдберриз)
Не, это у вас какое-то смещение в сознании произошло. Клиент-то понятное дело на сервер это всё отправляет примерно в том же виде, как и браузер.
Но мы же говорим не про веб-клиента какого-то REST API. Мы говорим про взаимодействие с базой — то место, где сервер приложений читает входящие параметры, формирует SQL запрос, отправляет его в базу, а потом обрабатывает и шлёт обратно какие-то JSONы.

V>Я имел ввиду, что если запрос формируется не через Linq, а непосредственной конкатенацией строк, почему это должно работать медленнее?

Для начала — потому, что вы уже выбрали "prepared statement для вызова хранимки", интуитивно полагая, что это и есть оптимальное решение.
А надо было готовить именно честный select statement, т.к. только он донесёт до сервера ваши предпочтения в нужном виде. И прямо все-все колоночки запихать внутрь bitmap index не получится — было б по-другому, так производители СУБД и без вас бы применили этот трюк. Увы.

V>Даже если упомянули самый эффективный и от того самый популярный способ обращения к БД через синтаксис вызова хранимки в ODBC, это не значит, что других способов нет.

V>Это просто способ, к которому неплохо бы привести всё что приводится.


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

Не, самый жирный пример против хранимки — это какой-нибудь экран "order history". С десятком колонок, из которых половина подтащена через inner join, и ещё парочка — через outer join. А полный список, доступный для выбора — так и вовсе полсотни. Потому как пользовательских ролей — много, сценариев — ещё больше. И там нужны и фильтры по диапазонам, и сортировки произвольные. И данные нужны прямо из OLTP, потому что люди в этом списке, в частности, смотрят, какие заказы сфейлились, а какие — прошли. Так что идея "а давайте мы это будем по ночам складывать в OLAP" не проходит.
V>На других экранах: корзина, доставка/трекинг, история покупок, платежей, общений с персоналом и т.д. и т.п. всё проще, нифига не вызов.
Это с т.з. конечного пользователя — да. У него там возможности урезаны, и контекст очень узки. У него история покупок — от силы пара десятков строк, и всего остального — тоже. Тут даже думать не надо, запросы фиксированные, объёмы маленькие, индексы очевидные.

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

V>Просто "метаинформация" относительно семантики бит для каждой группы своя (но это уже очевидные вещи).
Ну пока что проблемой будет то, что предлагаемый вами способ хранения и обработки данных напрямую ни в одной СУБД не поддерживается. Можно что-то подобное навелосипедить на Redis, но он, как известно, не совсем СУБД. То есть он больше подойдёт в качестве кэша между настоящей базой и сервером приложений.
Изо всех известных мне СУБД битмэпы поддерживает только Оракл, но и он не сможет разложить на биты единственную колонку — он работает не так.
Вам придётся заводить в базе честные колонки под каждый из атрибутов, и уже для них создавть bitmap-индексы. Ну, и да, про where bits & @mask > 0 придётся забыть — вам по-прежнему придётся отправлять в сервер вот этот вот where ram = "256 GB" or ram = "265 ГБ", чтобы уже у себя сервер это превратил в сканирование битмапов.
То есть мы вернулись к тому, с чего начали — как сформировать такой запрос, чтобы сервер понял, как его выполнить.

V>Хреновый драйвер, значит, или специально для тебя в текст отрендерили, чтобы тебе было понятней.

V>ODBC и OLEDB умеют передавать именно команды на вызовы процедур с аргументами по протоколу.
V>Да еще с такими, которые в текст не так-то просто перевести, например, binary.
Ну, вопрос, конечно, интересный. Может, действительно — рендерят для понятности. Надо бы посмотреть через wireshark.

V>Я все-равно не понял, почему у меня не может быть несколько хранимок?

Потому что 2^N — это слишком много для "несколько". Поэтому на практике так никто не делает — эти хранимки же надо маинтейнить потом, при каждом мелком изменении в таблице будь любезен весь набор подпилить.

V>Да, так что помешало?


V>Ага, особенно когда у нас значения комбинируются по OR по десяткам индексов.
V>Ты эта, поиграй с планами на досуге.
Я эту домашнюю работу выполнил ещё примерно во времена моих первых десяти постов на этом сайте.
И не просто преподавателю показал, что я умею реляционное исчисление в реляционную алгебру переводить, а натурально сидел и разбирался с планами. И Гарсиа-Молина зачитал до дыр.
Поэтому я, конечно, обрадуюсь, если вы мне что-то новое сможете про это рассказать, но скорее удивлюсь.

V>В случае OR по M признакам будет O(log(N)*M)

Каким образом вы получаете log(N)?

V>Ты планы-то покрути.



V>И где можно на такую реализацию посмотреть?

Пока — нигде, увы.

V>Ес-но. И код пишется под джит и тщательнейшим образом профилируется, благо в джаве репорты от джита подробные и вменяемые, а в дотнете джит — чёрный ящик.

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

V>Да хотя бы на этом форуме.

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

V>Да можно.

V>Реинтерпретация памяти и до Span-времён работала неплохо.
V>Просто теперь к ней подключили реинтерпретацию управляемой памяти.
V>Раньше в этих сценариях тупо пинили.
V>Но всё-равно в дотнете всегда всё пинится.
V>Создаётся копия строки (именно копия), или строка копируется в буфер — пинятся оба конца.
V>Преобразуется из UTF16 в UTF8 — тоже пинятся источник и приёмник.
V>Под капотом там обычный unsafe и указатели.
V>Именно поэтому теперь моя либа по конверсии строк стала не нужна. ))
Реинтерпретация памяти — бессмысленна, если у вас парсинг даты выполняется через DateTime.TryParseExact(string s, ...). Что вы там будете интерпретировать, если методу нужна полноценная отдельная строка, со своим length и буфером?
Только изобретение Span<T> позволило начать пилить разнообразные API, построенные поверх "фрагмента памяти", а не поверх тяжеловесных конструкций типа той же string или byte[].

V>Не уверен, что стоит публично это делать, в той области know how — понятие очень нервное.

А, вы боитесь, что на форуме поделились слишком многими секретами? И теперь вся общественность узнает, как ваши либы устроены внутри? Ну вы даёте. Я NDA даже во сне не нарушаю.
Не, мне не настолько интересно, чтобы вас под монастырь подводить. Вы лучше продолжайте делиться know how — эдак оно полезнее для общества будет.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.