Здравствуйте, alex_public, Вы писали:
_>И в чём проблема? У тебя в приложение N запросов. При сборке под одну СУБД у тебя сгенерируется N кусков кода. А если захочешь поддерживать 3 СУБД, будет 3*N таких кусков. Непонятны твои затруднения. Или ты пожалел лишний килобайт на диске под исполняемый файл? )))
Их може быть значительно больше N. В зависимости от количества условий, группировок, соединений, выводимых полей.
Есть универсальные отчеты, пользователь сам выбирает интерактивно какой запрос сделать и как вывести
Здравствуйте, alex_public, Вы писали:
_>Для таких простых алгоритмов самый обычный компилятор должен с гарантией делать автовекторизацию... Но это я так, к слову.
Должен, да, должен. Отож.
_>И для таких целей в нормальных языках имеется перегрузка операторов. Которая позволяет именно что читабельным образом задавать математику любого уровня сложности. Например если говорить про этот алгоритм, то на каком-нибудь numpy (конечно же скомпилированном с mkl) это и запишется проще и работать будет скорее всего быстрее. )))
Очень может быть. С нетерпением ждём примера С4 с nearest neighbour на языке с перегрузкой операторов.
_>Да, а по поводу выбора между производительностью и инвариантами — это у тебя будет в любом языке. Т.е. или ты доверяешь входным данным (позволяешь делать двоичный поиск по произвольным массивам, факт сортировки которых гарантируется "административно") или же не доверяешь (постоянно пересортировываешь их на всякий случай). Третьего варианта нет.
Конечно же есть. Третий вариант — отслеживать отсортированность данных при помощи системы типов. То есть sort(array) возвращает sortedArray; insert(sortedArray) возвращает просто array.
Можно даже степень неупорядоченности отслеживать, для выбора оптимального алгоритма сортировки
binarySearch требует sortedArray.
А в мире linq, реализация where поверх sortedArray пользуется двоичным поиском, а поверх просто array — линейным.
_>Эмм, какой ещё linq2d или i4o, если мы говорили про двоичный поиск? )))
Двоичный поиск — это малоинтересный частный случай. Общий подход — такой, как я сказал. Вас интересовал поиск в коллекции? Вот вам i4o. Он заточен не на массивы целых, а на коллекции "тяжёлых" объектов. Поэтому вместо сортировки он строит индекс. Тем не менее, это прекрасно иллюстрирует концепцию linq — декларация запроса и его выполнение разделены.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, alex_public, Вы писали:
_>Здравствуйте, Ночной Смотрящий, Вы писали:
_>>>Т.е. ты сам даже не в курсе вопроса, НС>>Я в курсе. И Danchik рядом тебе ответил на твой вопрос. Что дальше то?
_>У меня был не вопрос, а предложение показать visitor в коде linq2db. Могу и тебе предложить тоже самое, ведь "Ты думаешь ему об этом раньше не говорили?" (c) Ночной Смотрящий.
Все это "визиторы", каждый оптимизирован под свой сценарий
Здравствуйте, alex_public, Вы писали:
_>Здравствуйте, Ночной Смотрящий, Вы писали:
НС>>>>https://linq2db.github.io/api/LinqToDB.CompiledQuery.html _>>>А что, оно уже научилось делать динамические (зависящие по структуре от рантайм параметра) запросы? ) НС>>Не понял вопроса. На момент компиляции да — вся динамика доступно в полном объеме.
_>Давай возьмём простейший пример: _>
_>Я могу с помощью Linq добиться того, чтобы для такого кода проход по дереву выражений был ровно один раз, при старте приложения (лучше бы конечно вообще на этапе компиляции, ну да ладно)?
Можешь но качество SQL будет никакое.
_>>>>>а по нормальному вообще на стадии компиляции (с помощью метапрограммирования и статической интроспекции). НС>>>>Ага, причем сразу под все поддерживаемые БД. _>>>Если требуется динамическое переключение между ними, то да. А в чём проблема то? ) НС>>Да никакой. Давай статически скомпилируем все возможные комбинации.
_>И в чём проблема? У тебя в приложение N запросов. При сборке под одну СУБД у тебя сгенерируется N кусков кода. А если захочешь поддерживать 3 СУБД, будет 3*N таких кусков. Непонятны твои затруднения. Или ты пожалел лишний килобайт на диске под исполняемый файл? )))
Вот наивный ты человек. Сходу скажу что linq2db еще и по параметрам может оптимизировать отбрасывая целые куски ненужного SQL. Можешь дальше умножать, дай бог памяти тебе хватит на все вариации.
Уже какой год ты подымаешь эту ненужную полемику с доводами числодробильщика, а никак не человека которому нужны качественные быстрые SQL запросы с минимумом оверхеда и максимально возможным maintainability.
Хотел бы я видеть как ты OData бакенд напишешь со своими закидами.
D>Хотел бы я видеть как ты OData бакенд напишешь со своими закидами.
насколько оно вообще популярно за пределами .net? ну да, кто-то не поленился сделать поддержку на яве, но интересно — кто-то из не-шарпистов на rsdn с этим сталкивался?
Здравствуйте, Je suis Mamut, Вы писали:
D>>Хотел бы я видеть как ты OData бакенд напишешь со своими закидами.
JSM>насколько оно вообще популярно за пределами .net? ну да, кто-то не поленился сделать поддержку на яве, но интересно — кто-то из не-шарпистов на rsdn с этим сталкивался?
Да в 1С часто используют
Просто на C# удобно работать через Linq https://infostart.ru/1c/articles/403524/
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Je suis Mamut, Вы писали:
D>>Хотел бы я видеть как ты OData бакенд напишешь со своими закидами.
JSM>насколько оно вообще популярно за пределами .net? ну да, кто-то не поленился сделать поддержку на яве, но интересно — кто-то из не-шарпистов на rsdn с этим сталкивался?
Разве что просто в сабкверю обворачивать основной запрос да и строить недо-SQL. Так да, можно клеить запросы.
Здравствуйте, StatujaLeha, Вы писали:
SL>Здравствуйте, Danchik, Вы писали:
D>>Деревья как правило обходятся визиторами, так мелкософт придумал. Игорь же придумал другой способ работы с деревьями, ну в тыщу раз удобней. Я как ни смотрю на код EF, плакать хочется от обилия визиторов и их сложности.
SL>Хотелось бы узнать, кто такой Игорь и что за способ работы с деревьями он придумал?
Здравствуйте, Danchik, Вы писали:
SL>>Хотелось бы узнать, кто такой Игорь и что за способ работы с деревьями он придумал?
D>Я тут на хабре как-то коментировал D>https://habr.com/ru/post/486972/#comment_21261632
Здравствуйте, alex_public, Вы писали:
_>И в чём проблема? У тебя в приложение N запросов. При сборке под одну СУБД у тебя сгенерируется N кусков кода.
А если запрос динамически собирается? При этом оптимизатору принципиально важно именно собранный запрос, его куски оптимизировать бессмысленно. Значит придется собирать все возможные вариации итогового запроса, причем для каждой БД отдельно, потому что оптимизаторы у разных БД отчасти разные.
Здравствуйте, Je suis Mamut, Вы писали:
D>>Хотел бы я видеть как ты OData бакенд напишешь со своими закидами. JSM>насколько оно вообще популярно за пределами .net? ну да, кто-то не поленился сделать поддержку на яве, но интересно — кто-то из не-шарпистов на rsdn с этим сталкивался?
Здравствуйте, Sinclair, Вы писали:
_>>А после отработки (на этапе компиляции) метакода, мы получим такой код: _>>
_>>q='select'+' from'
_>>if(p) q+=' where'
_>>
S>В общем случае это невозможно. Либо генератор SQL будет совершенно убогим, либо результирующий код будет значительно сложнее. То есть прямо придётся идти по дереву и анализировать его в рантайме. Слишком много нюансов есть, которые не сводятся к if(p) q+=' where'.
Ну приведи конкретный пример невозможного. А то уже много лет как есть вполне себе работающая именно по такому принципу библиотека...
S>>>Стадия создания и анализа дерева выражений для типичных случаев — это микросекунды, которые не имеет смысла экономить. _>>Ой, вот не надо этих сказок. На этом форуме уже выкладывались подробные измерения этого вопроса. Причём сделанные, не такими как я, а профессионалами в .net. И там были вполне себе дикие цифры. Например для самого популярного linq фреймворка (EF) там встречались цифры порядка +500% от времени голого (ado.net) sql запроса, а для самого лучшего (Linq2db) встречалось +60% от времени запроса. S>Дорогостоящей является стадия порождения целевого кода, будь это SQL или MSIL. Чем больше оптимизаций, тем дороже. S>Само порождение дерева, которое вы мечтаете оптимизировать — микросекунды. Обход дерева, как таковой — тоже микросекунды.
Насколько я помню в реальности там были накладные расходы в доли миллисекунды (а не микро!), причём это как раз для очень простых запросов. Да, и причина там была именно в linq, потому как в том же тесте помнится участвовал и linq2db в режиме голого sql и показывал ровно 0 накладных расходов.
_>>См. пример выше. S>Это воображаемый пример. В реальном примере вот этот вот лишний 'where' может привести к добавлению таблицы в список from и join-clause. Это означает что нельзя просто сделать q+='...', надо перепахать весь запрос. S>То есть к моменту вызова if(p) у вас на руках будет не строка, а какое-то дерево (AST-представление SQL запроса).
О, а вот подобное является следствием ещё одного недостатка linq: что он на самом деле не равносилен sql. Преобразования там достаточно не тривиальное, причём даже для человека. Помнится тут кидали примеры каких-то запросов и я не всегда сразу видел сколько же там внутри на самом деле join'ов и каких.
А вот если у нас dsl полностью равносильный нормальному sql, то никаких подобных проблем возникнуть просто не может.
S>Дальше нужно это дерево оптимизировать — после того, как вся пользовательская логика по ветвлению отработала. Невозможно провести глобальную оптимизацию "покусочно", в каждой из веток исполнения.
Конечно. Но кто тут говорил про оптимизацию? Нормальная оптимизация возможна внутри СУБД (т.к. там у нас доступна вся статистика) и в голове программиста (т.к. он может использовать знания о модели данных в СУБД). А оптимизация запросов на стадии передачи их в СУБД — это какая-то ерунда, которая способна только на какие-то совершенно тривиальные действия.
S>И только потом это дерево можно сериализовать в строку. S>Построить тупую клеилку SQL, которая будет работать примерно со скоростью if(p) q+=' where' не очень трудно; но она встревает при встрече с реальными сценариями.
Ну покажи пример реального сценария. )
_>>Да? ) И какой же индекс ты предлагаешь использовать для этого своего кэша? ) S>Никакого индекса: S>
S>static private Function<byte[,], int[,]> _c4; // = from d in (new byte[0,0]).With(OutOfBoundsStrategy.NearestNeighbour) select (d[-1, 0] + d[0, -1] + d[1, 0] + d[0, 1]) / 4).Transform;
S>public static int[,] C4(byte[,] data)
S>{
S> if(_c4==null)
S> {
S> var c4 = from d in data.With(OutOfBoundsStrategy.NearestNeighbour)
S> select (d[-1, 0] + d[0, -1] + d[1, 0] + d[0, 1]) / 4;
S> _c4 = c4.Transform;
S> }
S> return _c4(data);
S>}
S>
S>Закомментированный кусок соответствует однократному построению фильтра при старте приложения, тогда ветвления в функции C4 можно избежать и получить вызов метода за примерно нулевую стоимость. S>Чтобы сделать стоимость совсем нулевой, потребуется заменить тип C4 с делегата на интерфейс. Быстрее уже будет некуда — мы говорим о стоимости одного косвенного вызова.
А, ну т.е. использование в роли кэша глобальной переменной. Это возможно имеет какой-то смысл для твоей библиотечки, однако совсем не коррелирует с озвученными требованиями на динамические запросы (если говорить о link2databse).
_>>А если у нас задача, где реально надо выжать всё возможное, то всегда используется компиляция (статическая естественно) под конкретную машину (чаще всего прямо на ней же самой). S>Ну вот CLR позволяет сделать компиляцию под конкретную машину на ней же самой. И это всё ещё будет быстрее, чем статическая компиляция С++ на этой машине.
Компиляция то будет быстрее, а вот работать код будет намного медленнее. )))
А так я в целом конечно же не против самой идее. Надо только чтобы реализация была адекватной. Вот например у WebAssembly практически всё адекватно в реализации (ну когда эта https://v8.dev/features/simd фича зарелизится во всех браузерах).
_>>Что за разные варианты? Ты вообще о чём? S>О том самом if(p), и о разных диалектах SQL.
Ну и то и то работает и при работе на стадии компиляции. Это так сказать банальный экспериментальный факт.
Здравствуйте, Sinclair, Вы писали:
_>>И для таких целей в нормальных языках имеется перегрузка операторов. Которая позволяет именно что читабельным образом задавать математику любого уровня сложности. Например если говорить про этот алгоритм, то на каком-нибудь numpy (конечно же скомпилированном с mkl) это и запишется проще и работать будет скорее всего быстрее. ))) S>Очень может быть. С нетерпением ждём примера С4 с nearest neighbour на языке с перегрузкой операторов.
Так зачем мелочиться, вот https://github.com/manuelaguadomtz/pythreshold/blob/master/pythreshold/local_th/sauvola.py тебе пример целиком sauvola, причём для произвольного размера окна. Если же усреднять только по соседям, то код ещё упростится.
_>>Да, а по поводу выбора между производительностью и инвариантами — это у тебя будет в любом языке. Т.е. или ты доверяешь входным данным (позволяешь делать двоичный поиск по произвольным массивам, факт сортировки которых гарантируется "административно") или же не доверяешь (постоянно пересортировываешь их на всякий случай). Третьего варианта нет. S>Конечно же есть. Третий вариант — отслеживать отсортированность данных при помощи системы типов. То есть sort(array) возвращает sortedArray; insert(sortedArray) возвращает просто array.
Нет, это ты всё как раз обсуждаешь только вариант с недоверием и плохой производительностью. А для нормальной производительности у тебя должен быть стандартный способ получить sortedArray из array без сортировки. Да, это возможно будет такая "unsafe" операция, но она необходима для возможности написания производительного ПО.
_>>Эмм, какой ещё linq2d или i4o, если мы говорили про двоичный поиск? ))) S>Двоичный поиск — это малоинтересный частный случай. Общий подход — такой, как я сказал. Вас интересовал поиск в коллекции? Вот вам i4o. Он заточен не на массивы целых, а на коллекции "тяжёлых" объектов. Поэтому вместо сортировки он строит индекс. Тем не менее, это прекрасно иллюстрирует концепцию linq — декларация запроса и его выполнение разделены.
Таких библиотек полно и без linq и в более удобном формате. Но они работают только "со своим типом данных". Здесь же речь шла именно о стандартной работе с коллекциями.
_>>Я могу с помощью Linq добиться того, чтобы для такого кода проход по дереву выражений был ровно один раз, при старте приложения (лучше бы конечно вообще на этапе компиляции, ну да ладно)? D>Можешь но качество SQL будет никакое.
Прямо вот никакое? )))
_>>И в чём проблема? У тебя в приложение N запросов. При сборке под одну СУБД у тебя сгенерируется N кусков кода. А если захочешь поддерживать 3 СУБД, будет 3*N таких кусков. Непонятны твои затруднения. Или ты пожалел лишний килобайт на диске под исполняемый файл? ))) D>Вот наивный ты человек. Сходу скажу что linq2db еще и по параметрам может оптимизировать отбрасывая целые куски ненужного SQL. Можешь дальше умножать, дай бог памяти тебе хватит на все вариации.
Угу, так и представил себе, что крутой оптимизатор из СУБД пропускает какой-то ненужный код, а мелкая прослойка в приложение не пропускает.
D>Уже какой год ты подымаешь эту ненужную полемику с доводами числодробильщика, а никак не человека которому нужны качественные быстрые SQL запросы с минимумом оверхеда и максимально возможным maintainability.
Вообще то я тут общался на совершенно другую тему. А именно, на тему пользы linq для работы с коллекциями. Однако некоторые коллеги начали усиленно говорить о пользе linq для работы с СУБД, хотя совершенно непонятно какое это имело отношение к теме.
Да, и кстати, что самое забавное, я с этими коллегами даже согласился, т.к. полностью поддерживаю данную идеологию (статически проверяемых запросов и т.п.). Ну разве что реализацию можно было бы и получше сделать (это я говорю про MS), что я и отметил мимоходом, совершенно не собираясь это обсуждать в дальнейшем.
D>Хотел бы я видеть как ты OData бакенд напишешь со своими закидами.
А это вообще где-то используется? Я лично никогда не встречался. )))
Здравствуйте, Ночной Смотрящий, Вы писали:
_>>У меня был не вопрос, а предложение показать visitor в коде linq2db. НС>Ты можешь сказать зачем тебе оно? И что принципиально поменяется, если вместо визитора для диспетчеризации по типу узла там используется PM?
К теме дискуссии это уже давно никакого отношения не имеет. А относилось исключительно к деградации кое-кого тут из технического специалиста в неграмотного тролля. Причём уже до такой степени, когда начинается вещание фантазий.
Здравствуйте, alex_public, Вы писали:
_>Ну приведи конкретный пример невозможного. А то уже много лет как есть вполне себе работающая именно по такому принципу библиотека...
public IQueryable<OrderLine> GetOrderLines(DataConnection conn, int productID, DateTime? orderDateMin)
{
var q = from line in conn.GetTable<OrderLine> where line.ProductID == productID select line;
if(orderDateMin.HasValue)
q = from line in q where line.Order.OrderDate>=orderDateMin.Value select line;
}
_>Насколько я помню в реальности там были накладные расходы в доли миллисекунды (а не микро!)/
Всё верно. Доли миллисекунды. _>причём это как раз для очень простых запросов. Да, и причина там была именно в linq, потому как в том же тесте помнится участвовал и linq2db в режиме голого sql и показывал ровно 0 накладных расходов.
_>О, а вот подобное является следствием ещё одного недостатка linq: что он на самом деле не равносилен sql. Преобразования там достаточно не тривиальное, причём даже для человека. Помнится тут кидали примеры каких-то запросов и я не всегда сразу видел сколько же там внутри на самом деле join'ов и каких.
Это не недостаток. Писать на SQL я могу и без помощи "библиотек". Linq даёт выразительность настоящего языка программирования — в том числе и по декомпозиции запросов на более простые части.
Мы не любим SQL как раз за то, что у него всё очень плохо с повторным использованием. _>А вот если у нас dsl полностью равносильный нормальному sql, то никаких подобных проблем возникнуть просто не может.
_>Конечно. Но кто тут говорил про оптимизацию? Нормальная оптимизация возможна внутри СУБД (т.к. там у нас доступна вся статистика) и в голове программиста (т.к. он может использовать знания о модели данных в СУБД). А оптимизация запросов на стадии передачи их в СУБД — это какая-то ерунда, которая способна только на какие-то совершенно тривиальные действия.
Нет конечно. Семантическая оптимизация — устранение, к примеру, left outer join, или inner join по верифицированному foreign key, если в результате не используются поля присоединяемой таблицы. Такие штуки делают далеко не все СУБД, несмотря на то, что для них не нужна статистика. Большинство движков СУБД рассчитывают на то, что автор запроса не пишет фигни; они лучше всего работают тогда, когда им даётся предварительно урезанный до минимума запрос — тогда у оптимизатора хватает сил и времени на выбор оптимальной стратегии исполнения. _>Ну покажи пример реального сценария. )
Один пример приведён выше. _>>>Да? ) И какой же индекс ты предлагаешь использовать для этого своего кэша? )
_>А, ну т.е. использование в роли кэша глобальной переменной. Это возможно имеет какой-то смысл для твоей библиотечки, однако совсем не коррелирует с озвученными требованиями на динамические запросы (если говорить о link2databse).
В linq2db другая ситуация по соотношению времён компиляции и исполнения запросов. Там можно делать кэш по ExpressionTree — это упражнение выполнено в классической серии статей Matt Warren "Building an IQueryable provider".
_>Компиляция то будет быстрее, а вот работать код будет намного медленнее. )))
Пруф можно?
_>А так я в целом конечно же не против самой идее. Надо только чтобы реализация была адекватной. Вот например у WebAssembly практически всё адекватно в реализации (ну когда эта https://v8.dev/features/simd фича зарелизится во всех браузерах).
Ну, и слава байту. _>Ну и то и то работает и при работе на стадии компиляции. Это так сказать банальный экспериментальный факт.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.