Здравствуйте, alex_public, Вы писали:
_>Необходимость создавать дерево, обходить его с помощью рефлексии и генерировать нужный нам код при каждом вызове.
Для обхода expression tree рефлексия не нужна, нужен визитор.
Здравствуйте, alex_public, Вы писали: _>Необходимость создавать дерево, обходить его с помощью рефлексии и генерировать нужный нам код при каждом вызове. Как минимум это должно было бы происходить один раз при старте приложения, а по нормальному вообще на стадии компиляции (с помощью метапрограммирования и статической интроспекции).
Совсем всегда делать всё на старте приложения не выйдет — expression tree может замыкать локальные переменные и прочие штуки, которые меняются от вызова к вызову.
Но да, expression tree можно было бы строить заранее — грубо говоря, сделать его не переменной, а полем класса, статическим либо экземплярным.
А вот построение кода из expression tree делать на стадии компиляции — идея плохая. Скажем, тот код, который я написал для linq2d, в такой системе невозможен.
Потому, что в зависимости от того, какую версию AVX поддерживает процессор, порождается разный код. И внутри этого кода уже нет никаких ветвлений. Порождение кода в рантайме — хорошая, правильная идея.
А накладные расходы на компиляцию устраняются при помощи кэширования. Надёжнее всего — явное кэширование: мы получаем из выражения некоторый transform — делегат, который удерживает ссылку на готовый код.
Чуть менее надёжно неявное кэширование — это когда мы автоматически складываем все порождаемые функции в таблицу, индексированную исходным выражением.
Уменьшается надёжность за счёт того, что любое кэширование имеет риск выбросить то, что нужно, или хранить то, чего не нужно.
Опять же, неявное кэширование проигрывает потому, что надо сравнивать expression trees. Если бы у нас expression tree порождались статически, то можно было бы сравнивать их по ссылке за O(1).
А поскольку они порождаются динамически, их надо честно сравнивать. В реальности, порождение expression tree — крайне дешёвая операция, как и сравнение двух деревьев.
Относительно дорогостоящей является собственно компиляция — а там всё зависит от того, насколько важна оптимизация. Наивный кодеген — это миллисекунды; дорого стоят всякие упражнения типа constant folding, common subexpression evaluation, и прочие loop invariant detection.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, alex_public, Вы писали:
S>>Все-таки у linq есть поддержка компилятора, т.е. встроен в язык, а у этих библиотек нет. _>С учётом того как встроено, то лучше уж была бы библиотека, а не такая кривизна.
Здравствуйте, alex_public, Вы писали:
_>Ой, ну вот уж с тобой я точно не буду дискутировать на эту тему. Просто потому что мы это уже делали и очень подробно, с примерами кода и т.п., обсудили это всё. И ты даже вполне себе согласился с большинством моих примеров. Странно, что ты забыл это и начинаешь по второму кругу. Если так и не вспомнишь, то я постараюсь найти на форуме ту темку и кинуть сюда ссылку на неё. Помнится там должны быть такие ключевые слова: произвольный доступ, двоичный поиск, рекурсии, двухмерные массивы (и их свёртки с ядром) и т.д.
Эмм, лучше посмотреть сразу вот сюда
Здравствуйте, alex_public, Вы писали:
_>Необходимость создавать дерево, обходить его с помощью рефлексии и генерировать нужный нам код при каждом вызове. Как минимум это должно было бы происходить один раз при старте приложения,
Здравствуйте, alex_public, Вы писали:
_>Необходимость создавать дерево, обходить его с помощью рефлексии и генерировать нужный нам код при каждом вызове. Как минимум это должно было бы происходить один раз при старте приложения, а по нормальному вообще на стадии компиляции (с помощью метапрограммирования и статической интроспекции).
Вы немало пишете на этом форуме, и я хорошо помню ваши споры про linq. Может быть вы тогда займётесь разработкой инструментария вокруг Nemerle, чтобы оное метапрограммирование наконец-то заработало и в дотнете?
Здравствуйте, Ночной Смотрящий, Вы писали:
_>>произвольный доступ, двоичный поиск, рекурсии, двухмерные массивы (и их свёртки с ядром) и т.д. НС>В IEnumerable?
Да, я вот точно так же делаю, когда кто-то говорит, что Linq хорошо подходит для работы с коллекциями (перечисленное в цитате выше — это как раз примеры такой работы).
Здравствуйте, vorona, Вы писали:
_>>Необходимость создавать дерево, обходить его с помощью рефлексии и генерировать нужный нам код при каждом вызове. V>Для обхода expression tree рефлексия не нужна, нужен визитор.
Ну раз ты так разбираешься в теме, то конечно же без проблем покажешь место в исходниках linq2db (вроде как лучший представитель из всего семейства linq2database решений), где используется visitor, не так ли?
Здравствуйте, alex_public, Вы писали:
_>Да, я вот точно так же делаю, когда кто-то говорит, что Linq хорошо подходит для работы с коллекциями (перечисленное в цитате выше — это как раз примеры такой работы).
linq как набор фич языка никак не завязан на IEnumerable, это только библиотека. Никто не мешает тебе добавить сво IMySuperPuperEnumerable и использовать linq с ним. Я уж не говорю о том, что и стандартная библиотека часть методов реализует не только для IEnumerable, а и для ICollection и массивов.
Здравствуйте, alex_public, Вы писали:
_>Ну раз ты так разбираешься в теме, то конечно же без проблем покажешь место в исходниках linq2db (вроде как лучший представитель из всего семейства linq2database решений), где используется visitor, не так ли?
Здравствуйте, alex_public, Вы писали:
_>Необходимость создавать дерево, обходить его с помощью рефлексии и генерировать нужный нам код при каждом вызове. Как минимум это должно было бы происходить один раз при старте приложения, а по нормальному вообще на стадии компиляции (с помощью метапрограммирования и статической интроспекции).
Ну вот уж нет, спасибо. Это конец динамическим Linq запросам и декомпозиции сложных запросов на несколько мелких.
Здравствуйте, alex_public, Вы писали:
_>Здравствуйте, vorona, Вы писали:
_>>>Необходимость создавать дерево, обходить его с помощью рефлексии и генерировать нужный нам код при каждом вызове. V>>Для обхода expression tree рефлексия не нужна, нужен визитор.
_>Ну раз ты так разбираешься в теме, то конечно же без проблем покажешь место в исходниках linq2db (вроде как лучший представитель из всего семейства linq2database решений), где используется visitor, не так ли?
Деревья как правило обходятся визиторами, так мелкософт придумал. Игорь же придумал другой способ работы с деревьями, ну в тыщу раз удобней. Я как ни смотрю на код EF, плакать хочется от обилия визиторов и их сложности.
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>Здравствуйте, alex_public, Вы писали:
_>>Ну раз ты так разбираешься в теме, то конечно же без проблем покажешь место в исходниках linq2db (вроде как лучший представитель из всего семейства linq2database решений), где используется visitor, не так ли?
НС>Хочешь сказать что его там нет или что?
Если ты про наследников ExpressionVisitor, то да, нету ни одного.
Здравствуйте, alex_public, Вы писали: _>Да, я вот точно так же делаю, когда кто-то говорит, что Linq хорошо подходит для работы с коллекциями (перечисленное в цитате выше — это как раз примеры такой работы).
Ну, то есть вы будете продолжать игнорировать аргументы. На всякий случай напомню, что наш предыдущий разговор, на который вы тут ссылаетесь, закончился вовсе не тем, что "я согласился, что линк плохо подходит для 2d-массивов", а тем, что вы сбежали через выход "но ведь то же самое можно было бы сделать, просто вызывая обычные методы".
Как впоследствии оказалось, даже это не совсем так. К примеру, бинаризацию по Sauvola переписать на "обычные методы" будет крайне громоздко — конструкция let сильно сокращает "процедурный" код.
А без неё сделать нельзя — expression trees не позволяют заводить временные переменные и делать присваивания. Это ограничение было бы дефектом, если бы оно не упрощало компилятор и не гарантировало отсутствие побочных эффектов, в отличие от настоящих присваиваний.
Про двоичный поиск и рекурсию, емнип, всё было сказано ещё в 2018 — если коллекция позволяет делать двоичный поиск (или вообще хеш-обращение за O(1)), то нет никаких проблем превратить то же самое linq-выражение в поиск по индексу. И такой проект есть, я давал на него ссылку. Он работает быстрее, чем прямой перебор, на коллекциях от сотни элементов.
Преимущество же у linq перед рукопашным вызовом бинарного поиска очевидно — локализация изменений.
Пишем идиоматический код; смотрим на производительность. Если нам кажется, что можно наиграть на бинарном поиске — заменяем List<string> на SortedList<string>, замеряем производительность.
Может быть, мы проиграем — поддержание списка в отсортированном состоянии окажется дороже, чем экономия на поисках.
Ок, заменим на какое-нибудь сбалансированное дерево — и снова посмотрим. У нас есть гарантия того, что код продолжит работать корректно. В вашем подходе придётся кропотливо ползать по коду и следить, чтобы случайно не получилось нарушить инварианты, на которые опирается binary search.
Впрочем, мы всё это уже обсудили ещё в прошлый раз. Странно, что вы вытаскиваете по второму кругу все те же аргументы, которые были опровергнуты ещё три года тому назад.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Danchik, Вы писали:
_>>>>Необходимость создавать дерево, обходить его с помощью рефлексии и генерировать нужный нам код при каждом вызове. V>>>Для обхода expression tree рефлексия не нужна, нужен визитор. _>>Ну раз ты так разбираешься в теме, то конечно же без проблем покажешь место в исходниках linq2db (вроде как лучший представитель из всего семейства linq2database решений), где используется visitor, не так ли? D>Деревья как правило обходятся визиторами, так мелкософт придумал. Игорь же придумал другой способ работы с деревьями, ну в тыщу раз удобней.
Здравствуйте, mrTwister, Вы писали:
_>>Необходимость создавать дерево, обходить его с помощью рефлексии и генерировать нужный нам код при каждом вызове. Как минимум это должно было бы происходить один раз при старте приложения, а по нормальному вообще на стадии компиляции (с помощью метапрограммирования и статической интроспекции). T>Ну вот уж нет, спасибо. Это конец динамическим Linq запросам и декомпозиции сложных запросов на несколько мелких.
Вот именно. Тем более что если кому то надо — он может сам закешировать запрос при старте, получая в довесок все соотв. ограничения. Идея все это запихнуть в компилятор — крайне странная, если не сказать больше.
Здравствуйте, Sinclair, Вы писали:
S>Впрочем, мы всё это уже обсудили ещё в прошлый раз. Странно, что вы вытаскиваете по второму кругу все те же аргументы, которые были опровергнуты ещё три года тому назад.