Здравствуйте, alex_public, Вы писали:
_>Здравствуйте, Sinclair, Вы писали:
S>>Ну вот я тоже не вижу "всех нужных примеров" на первых страницах описания. S>>Вижу const auto& row : db(select(foo.name, foo.hasFun).from(foo).where(foo.id > 17 and foo.name.like("%bar%"))).
_>А требуется ещё что-то?
Ну да — в частности, требуется пример, в котором структура запроса меняется в зависимости от рантайм-параметров.
_>По поводу этого примера вообще непонятно откуда такой интерес, т.к. тут собственно интересная часть (так называемая оптимизация) происходит не в linq, а просто в C#, причём с использованием банального оператора if — это где угодно можно повторить. Если говорить конкретно о C++ и указанной мною библиотеке, то буквально такими же if'ми всё и записывается. При этом во время компиляции под каждую ветку ветвления создастся своя версия запроса
Да ладно! Т.е. во время компиляции этого примера (переведённого на С++) создастся 16 версий запроса? Станиславский смотрит на топик с недоумением.
_>Примеры имеются прямо по ссылкам, которые я показал. Зачем копировать их ещё и сюда? )
Меня настораживает, что "прямо по ссылкам", которые вы показали, приводятся примеры, в которых запросы с переменной структурой используют другие операторы, чем запросы с известной структурой. К сожалению, у меня нет локальных средств скопировать и поиграться с примерами, но есть опасения, что авторы библиотеки предлагают выбирать между статической типизацией и динамическим конструированием. А ваше нежелание воспроизводить нужный пример только подтверждает эти опасения
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, gandjustas, Вы писали:
G>Еще раз. Нужны в типизированном виде: G>1) Проекции G>2) Джоины G>3) Группировки.
Ну, в примерах к sqlpp есть такое:
97 #if 0
98 auto s = select(p.id, p.name, f.id.as(cheesecake))
99 .from(p, f)
100 .where(p.name == any(select(f.name)
101 .from(f)
102 .where(true)))
103 .group_by(f.name)
104 .having(p.name.like("%Bee%"))
105 .order_by(p.name.asc())
106 .limit(3).offset(7);
107
108 auto x = s.as(sqlpp::alias::x);
109 for (const auto& row : db(select(p.id, p.name, all_of(x).as(x))
110 .from(p.join(x).on(p.feature == x.cheesecake))
111 .where(true)))
112 {
113 int64_t id = row.id;
114 std::string name = row.name;
115 std::string x_name = row.x.name;
116 int cheesecake = row.x.cheesecake;
117 }
118 #endif
Т.е. проекции, джойны, и группировки вроде бы есть G>То есть чтобы я не мог обратиться к полю А, если я его не указал в select.
А вот можно ли обратиться к полю А, не указанному в select, из примеров непонятно. Там нет ни одного контрпримера — т.е. нарушений типизации с сообщениями компилятора.
Полагаю, что всё же в этом примере типизация работает — там, правда, объём шаманства для подготовки типов зашкаливает, но это, наверное, можно какими-то тулзами по автогенерации кода из метаданных зарулить.
Интереснее, работает ли типизация в динамических запросах.
Т.е. станет ли компилятор ругаться, если я укажу вместо dysel.where.add(p.id > 17) что-то типа dysel.where.add(f.fatal==true)(
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Так с "нормальным SQL" также возможны проблемы с версионированием
С нормальным SQL логика обработки данных содержится в одном месте, которое меняется согласованно.
Например, если она вся сосредоточена в хранимках, то откат базы ко вчерашнему дню вернёт и код хранимок.
Если она вся сосредоточена в сервере приложений, то откат фолдера App_Code сервера приложений ко вчерашнему дню вернёт назад и код отчётов.
А если половина логики реализована в сервере приложений, а половина — в базе, то нужно как-то обеспечить одновременность откатов того и другого. Встроенных механизмов для этого нет.
Конечно же даже в положительном случае есть проблема с изменениями схемы базы. Но эти изменения происходят гораздо реже, чем смена логики.
Грубо говоря, правила начисления скидок в заказе меняются еженедельно. А структура самого заказа как была зафиксирована в стандарте TPC-C сто лет назад, так никуда и не изменилась.
Поэтому в первую очередь нужно избегать размазывания логики и неявных зависимостей внутри неё.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, gandjustas, Вы писали:
G>А что у нас в /*projection*/? G>В C# там будет код G>
G>x => new { x.A, x.B, x.Y.C }
G>
G>Последнее выражение вызовет добавление join_а в запрос G>А в C++ что туда написать для подобного эффекта?
Например:
_1.A, _1.B, _1.Y.C
G>И как это все можно по разным функциям и сборкам (dll) растащить?
Как я уже сказал — достаточно передавать текущее значение запроса с его полным типом.
G>Ведь суть linq в том, что собирать запрос по кускам можно в разных местах, а не надо выписывать все условия в одном месте и гонять туда-сюда все параметры.
В примере выше, например между q2 и q3 может быть много кода, в том числе и границы dll.
EP>>Если не нравится экспоненциальное количество запросов в исполняемом файле, то можно добавить динамики. Да, при этом запрос будет частично строится в runtime (с возможным кэшированием), но при этом не будет дорогой runtime reflection — в ней тут нет принципиальной необходимости, достаточно compile-time reflection. G>В C# на compiled query можно добиться того же эффекта.
Так там разве Expression Tree не будет каждый раз в runtime обходится?
EP>>Если же хочется больше динамики (например q5 генерируется в отдельной динамической библиотеке и нужно иметь возможность изменять запрос без переборки всех зависимых компонентов) — то можно частично стереть информацию о типе (type erasure), но тогда как минимум появятся косвенные вызовы, а максимум — построение части запроса в runtime (но опять же, без runtime reflection). G>Ну приведи работающий пример.
Изначально речь шла про принципиальную возможность. А возможно это не только в C++, но и например в D или Nemerle.
Что же касается конкретного примера — я с СУБД вообще не работаю. Для создания полного примера мне придётся разбираться с одной из упомянутых выше библиотек — на первый взгляд то о чём я говорю там вполне реализуемо, но могут быть некоторые детали которые будут препятствовать реализации, что впрочем никак не будет означать принципиальную невозможность, и соответственно к изначальному тезису не имеет отношения.
Если же есть сомнения в принципиальной реализуемости — то готов ответить на конкретные вопросы.
Здравствуйте, gandjustas, Вы писали:
G>Ты не смог привести пример, который имеет те же возможности (в том числе типизацию), но быстрее linq. Зачем нужен "шаблонный код (в котором после прохода компилятора вообще ни одного вызова функции не остаётся)" если он не умеет банальный джоин? Да и какой смысл за этими микросекундами гоняться если запрос в базе выполняется на 2-3 порядка дольше? Банальное кеширование даст гораздо более заметный прирост к скорости.
Да, ты писал... Не смотря на то, что на первой же странице сайта библиотеки ясно написано, что всё это присутствует. Более, того, в предыдущем письме уже я сам сказал тебе, что присутствует. Но ты продолжаешь говорить, что этого нет. Теперь надеюсь сумел увидеть или и этого мало? )))
G>Они даже близко не валялись с linq, ни в одном из них нет типизированной проекции. Просто напиши 5 строк кода с любой из библиотек, которые ты приводил, чтобы получить пример, эквивалентный тому, который я приводил. Твои рассуждения о скорости крайне неинтересны, тем более они крайне далеки от жизни.
Пример уже показал Евгений, хотя на мой взгляд показывать настолько очевидные вещи даже странно. Или кто-то на этом форуме не владеет if'ми? )))
Здравствуйте, Sinclair, Вы писали:
EP>>Так с "нормальным SQL" также возможны проблемы с версионированием S>С нормальным SQL логика обработки данных содержится в одном месте, которое меняется согласованно. S>Например, если она вся сосредоточена в хранимках, то откат базы ко вчерашнему дню вернёт и код хранимок. S>Если она вся сосредоточена в сервере приложений, то откат фолдера App_Code сервера приложений ко вчерашнему дню вернёт назад и код отчётов. S>А если половина логики реализована в сервере приложений, а половина — в базе, то нужно как-то обеспечить одновременность откатов того и другого. Встроенных механизмов для этого нет.
То есть обычно ORM используют третий (смешанный) вариант, отсюда и проблемы? Опять же, видимо это проблемы конкретных ORM, а не подхода в целом.
Например в ODB вся логика обработки данных содержится в одном месте. И если, например, произойдёт откат базы — то при доступе к ней произойдёт обновление схемы и миграция данных (причём есть опция постепенного перехода).
Здравствуйте, alex_public, Вы писали:
_>Здравствуйте, gandjustas, Вы писали:
G>>Ты не смог привести пример, который имеет те же возможности (в том числе типизацию), но быстрее linq. Зачем нужен "шаблонный код (в котором после прохода компилятора вообще ни одного вызова функции не остаётся)" если он не умеет банальный джоин? Да и какой смысл за этими микросекундами гоняться если запрос в базе выполняется на 2-3 порядка дольше? Банальное кеширование даст гораздо более заметный прирост к скорости.
_>Угу, кэширование — это сила. Только вот в каком месте? ) Очень эффективным (и причём не требующим никаких усилий при реализации) оно является при реализации в БД. И как раз именно при такой реализации (на фоне мгновенно возвращаемых запросов) тормознутость linq будет особенно видна.
Оо новое слово в архитектуре прям. Кеширование всегда делается ближе к потребителю, чтобы сократить не только затраы на доставание данных, но и на передачу по сети и обработку датасета на клиенте. Кстати на уровне БД кеширования запросов нет, угадай почему.
G>>Еще раз. Нужны в типизированном виде: G>>1) Проекции _>https://github.com/rbock/sqlpp11/blob/master/include/sqlpp11/select_column_list.h
G>>2) Джоины _>https://github.com/rbock/sqlpp11/blob/master/include/sqlpp11/join.h
G>>3) Группировки. _>https://github.com/rbock/sqlpp11/blob/master/include/sqlpp11/group_by.h
А где пример? Так чтобы и динамический запрос был, и проекции с джоинами и типизацией.
G>>То есть чтобы я не мог обратиться к полю А, если я его не указал в select. G>>Я это уже писал, ты упорно игнорируешь.
_>Да, ты писал... Не смотря на то, что на первой же странице сайта библиотеки ясно написано, что всё это присутствует. Более, того, в предыдущем письме уже я сам сказал тебе, что присутствует. Но ты продолжаешь говорить, что этого нет. Теперь надеюсь сумел увидеть или и этого мало? )))
Так покажи пример если присутствует. Пока я вижу тонны заголовочных файлов, а не пример использования.
G>>Они даже близко не валялись с linq, ни в одном из них нет типизированной проекции. Просто напиши 5 строк кода с любой из библиотек, которые ты приводил, чтобы получить пример, эквивалентный тому, который я приводил. Твои рассуждения о скорости крайне неинтересны, тем более они крайне далеки от жизни.
_>Пример уже показал Евгений, хотя на мой взгляд показывать настолько очевидные вещи даже странно. Или кто-то на этом форуме не владеет if'ми? )))
У него пример абстрактый, основанный на предположении что это можно сделать. Я тоже могу предположить, что linq перестанет использовать рефлексию и будет запросы в compile time генерить. Этого, кстати, довольно легко добиться в C#6 с его возможностями переписывания кода.
Но это все предположения, а фактического примера, который эквивалентен тому, что я написал, мы так и не увидли.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Здравствуйте, gandjustas, Вы писали:
G>>А что у нас в /*projection*/?
EP>Например: EP>
EP>_1.A, _1.B, _1.Y.C
EP>
G>>И как это все можно по разным функциям и сборкам (dll) растащить? EP>Как я уже сказал — достаточно передавать текущее значение запроса с его полным типом.
G>>Ведь суть linq в том, что собирать запрос по кускам можно в разных местах, а не надо выписывать все условия в одном месте и гонять туда-сюда все параметры. EP>В примере выше, например между q2 и q3 может быть много кода, в том числе и границы dll.
Осталось увидеть рабочий пример.
EP>>>Если не нравится экспоненциальное количество запросов в исполняемом файле, то можно добавить динамики. Да, при этом запрос будет частично строится в runtime (с возможным кэшированием), но при этом не будет дорогой runtime reflection — в ней тут нет принципиальной необходимости, достаточно compile-time reflection. G>>В C# на compiled query можно добиться того же эффекта.
EP>Так там разве Expression Tree не будет каждый раз в runtime обходится?
Конечно нет, в этом и суть compiled query.
EP>>>Если же хочется больше динамики (например q5 генерируется в отдельной динамической библиотеке и нужно иметь возможность изменять запрос без переборки всех зависимых компонентов) — то можно частично стереть информацию о типе (type erasure), но тогда как минимум появятся косвенные вызовы, а максимум — построение части запроса в runtime (но опять же, без runtime reflection). G>>Ну приведи работающий пример.
EP>Изначально речь шла про принципиальную возможность. А возможно это не только в C++, но и например в D или Nemerle.
Так никто и не говорит про реализацию фреймворка уровня linq, но "принципиальная" возможность является возможностью именно тогда, когда есть хотя бы прототип.
Ибо принципиально практически в любом яызке можно прикрутить кодогенератор, который будет что-то делать. Даже внешний.
Но нужно не просто прикрутить, а сделать его юзабельным.
Или даже лучше. "Принципиально" ничего не мешает не использовать тяжелую рефлексию для Expression Tree и сделать кеширование выражений, компилятор C# то доступен и средства его расширения есть.
То есть принципиально C# может все что C++ и даже больше.
Здравствуйте, Sinclair, Вы писали:
_>>А требуется ещё что-то? S>Ну да — в частности, требуется пример, в котором структура запроса меняется в зависимости от рантайм-параметров.
Ну так мы же можем без проблем реализовать такое на уровне склейки sql строк, не так ли? ) Тогда почему возникают сомнения, что можно написать статически типизируемый код, который компилируется в полный аналог такой склейки строк? ) Это же вот прямо классическая задачка для обычного метапрограммирования.)
S>Да ладно! Т.е. во время компиляции этого примера (переведённого на С++) создастся 16 версий запроса? Станиславский смотрит на топик с недоумением.
Гмммм, насколько я помню, ты же вроде в курсе C++. Тогда откуда такие вопросы? Это же вот прямо самый базисный принцип программирования на шаблонах C++, из-за которого разбухают итоговые бинарники, но при этом достигается непревзойдённая производительность.
S>Меня настораживает, что "прямо по ссылкам", которые вы показали, приводятся примеры, в которых запросы с переменной структурой используют другие операторы, чем запросы с известной структурой. К сожалению, у меня нет локальных средств скопировать и поиграться с примерами, но есть опасения, что авторы библиотеки предлагают выбирать между статической типизацией и динамическим конструированием. А ваше нежелание воспроизводить нужный пример только подтверждает эти опасения
Здравствуйте, Sinclair, Вы писали:
S>Полагаю, что всё же в этом примере типизация работает — там, правда, объём шаманства для подготовки типов зашкаливает, но это, наверное, можно какими-то тулзами по автогенерации кода из метаданных зарулить.
Здравствуйте, VladCore, Вы писали:
VC>Весь критический по требованиям производительности тормозам код приходится переписывать выкидывать с помощью Dapper.
VC>EntityFramework не нужен. Кто будет спорить тот или нехорошо себя ведёт, или sql-код писАть не умеет.
Проводили анализ.
1. На типовых запросах а-ля select * from... скорость выполнения в EF 4+ идентична прямому запроу к базе. Про более ранние версии не говорю, ибо там реально дикие тормоза.
2. Сложные конструкции — как повезет. Были ситуации (на count), когда время выполнения могло раз в 10 быть медленнее прямого запроса. Были ситуации когда человек, не являющийся экспертом в БД, писал код, работавший медленнее, чем сгенерированный EF.
В целом я бы сказал, что ситуация примерно та же, что и с компиляторами 10-15 летней давности, когда опытный спец (не гуру ассемблера, а просто хороший спец) еще может, напрягаясь, писать ассемблерный код круче, чем оптимизирует компилятор и вопрос только в том, стоит ли овчинка выделки, а вот новичку уже лучше бить по рукам за кривые попытки оптимизации.
"Пишите код так, как будто сопровождать его будет склонный к насилию психопат, который знает, где вы живете". (с) Макконнелл, "Совершенный код".
Здравствуйте, gandjustas, Вы писали:
EP>>>>Если не нравится экспоненциальное количество запросов в исполняемом файле, то можно добавить динамики. Да, при этом запрос будет частично строится в runtime (с возможным кэшированием), но при этом не будет дорогой runtime reflection — в ней тут нет принципиальной необходимости, достаточно compile-time reflection. G>>>В C# на compiled query можно добиться того же эффекта. EP>>Так там разве Expression Tree не будет каждый раз в runtime обходится? G>Конечно нет, в этом и суть compiled query.
compiled query к чему привязывается? То есть на основании чего выбирается тот или иной закэшированный запрос?
EP>>Изначально речь шла про принципиальную возможность. А возможно это не только в C++, но и например в D или Nemerle. G>Так никто и не говорит про реализацию фреймворка уровня linq, но "принципиальная" возможность является возможностью именно тогда, когда есть хотя бы прототип.
Речь идёт про аналог LINQ, но с разбором выражений и генерацией запросов в compile-time, то есть с нулевыми накладными расходами по сравнению с полностью ручной версией, о чём и говорил alex_public выше по ветке
_>Ты не совсем понял мою позицию. Я не сторонник работы через склейку sql строк (хотя в этом тоже ничего ужасного нет, но всё же не очень удобно и безопасно) — естественно удобнее использовать специальные автоматические инструменты. Только вот они могут быть построены на очень разных принципах. И я соответственно считаю приемлемыми только те, которые вносят 0 накладных расходов. Так вот все производные от linq в этой области таковыми не являются. Как раз потому, что кому-то захотелось пристроиться к linq, а не создать отдельный инструмент специально для БД.
G>Ибо принципиально практически в любом яызке можно прикрутить кодогенератор, который будет что-то делать. Даже внешний. G>Или даже лучше. "Принципиально" ничего не мешает не использовать тяжелую рефлексию для Expression Tree и сделать кеширование выражений, компилятор C# то доступен и средства его расширения есть.
Так, давай проясним:
1. Ты согласен с тем что мой пример выше можно реализовать на основе внешнего кодогенератора? (после кодогенерации в коде появятся 16 уже готовых запросов) То есть та самая принципиальная возможность, с нулевыми накладными расходами в runtime.
2. Ты согласен с первым пунктом, но сомневаешься в том что это возможно в одном из следующих языков без внешнего кодогенератора? C++, D, Nemerle.
G>То есть принципиально C# может все что C++ и даже больше.
Если брать язык + внешний кодогератор, то вообще-то у C++ возможностей больше, так как нет ограничений платформы
Здравствуйте, gandjustas, Вы писали:
G>Ну ок, возьми не C#, а любой другой статически типизированный язык. В любом языке все придет к формированию деревьев выражений на основе конструкций языка, которые потом надо будет отобразить на метаданные классов и на базе этого построить SQL. Так что накладные расходы также будут присутствовать.
В compile-time делается анализ строки, и на основе этого генерируется определённая последовательность вызовов, результирующий ASM код получается идентичным тому, который соответствует полностью ручному оптимальному коду.
То есть из вот этого:
{
int counter = 2;
char character = '!';
double value = 0.5;
for_each_part
(
auto x,
"val = $value$, cnt = $counter$, ch = $character$, again v=$value$;\n",
counter, character, value
)
{
print_it(x);
};
}
Получается ASM код идентичный этому:
{
int counter = 2;
char character = '!';
double value = 0.5;
print_it("val = ");
print_it(value);
print_it(", cnt = ");
print_it(counter);
print_it(", ch = ");
print_it(character);
print_it(", again v=");
print_it(value);
print_it(";\n");
}
Можно производить и более сложные трансформации (в том числе и производить оптимизацию SQL) — compile-time вычисления на C++ полны по Тьюрингу.
Причём формирование деревьев выражений на основе конструкций языка это более простая задача чем анализ строк — и в C++ это было доступно аж с первой версии стандарта C++98. И на основе этого (compile-time обход деревьев выражений на основе конструкций языка) создано уже много разных EDSL (Embedded Domain Specific Language) библиотек.
Здравствуйте, gandjustas, Вы писали:
_>>Угу, кэширование — это сила. Только вот в каком месте? ) Очень эффективным (и причём не требующим никаких усилий при реализации) оно является при реализации в БД. И как раз именно при такой реализации (на фоне мгновенно возвращаемых запросов) тормознутость linq будет особенно видна. G>Оо новое слово в архитектуре прям. Кеширование всегда делается ближе к потребителю, чтобы сократить не только затраы на доставание данных, но и на передачу по сети и обработку датасета на клиенте. Кстати на уровне БД кеширования запросов нет, угадай почему.
Нууу, если у тебя нет, то это не значит, что ни у кого нет. ))) Например вот http://dev.mysql.com/doc/refman/5.7/en/query-cache.html и кстати при попадание в кэш время запроса становится меньше 1 мс (хотя такой же первый запрос мог секунду обрабатываться). Как ты думаешь, будут ли на таком фоне заметны тормоза linq или нет? )
G>А где пример? Так чтобы и динамический запрос был, и проекции с джоинами и типизацией.
Вот Sinclair догадался сделать два лишних клика мышкой (один на папку examples и второй на cpp файл) и всё увидеть сам. А тебе несколько раз подсказывали сделать это и то не можешь...
_>>Пример уже показал Евгений, хотя на мой взгляд показывать настолько очевидные вещи даже странно. Или кто-то на этом форуме не владеет if'ми? ))) G>У него пример абстрактый, основанный на предположении что это можно сделать. Я тоже могу предположить, что linq перестанет использовать рефлексию и будет запросы в compile time генерить. Этого, кстати, довольно легко добиться в C#6 с его возможностями переписывания кода. G>Но это все предположения, а фактического примера, который эквивалентен тому, что я написал, мы так и не увидли.
Зато в библиотечке примеры вполне даже компилируемые. Но ты их похоже принципиально не замечаешь. )
Здравствуйте, alex_public, Вы писали:
_>Ну лично мне нравится например такое https://github.com/rbock/sqlpp11 решение. Потому как оно является полным отражением sql, только статически типизированным и т.п. Вообще предпочитаю "лёгкие" решения. Не в смысле быстродействия (в этом смысле все указанные мною примеры хороши), а в смысле функциональности. А если брать "тяжёлые" решения, то можно глянуть например на такое http://www.codesynthesis.com/products/odb/, но мне как-то приятнее иметь дело с "sql запросами". )))
А почему предпочитаешь "лёгкие" решения?
Я понимаю если база данных уже есть сама по себе, и всё что нужно это сделать несколько запросов — то тут конечно "лёгкие" решения оптимальны.
Но если же нам нужно отображать развесистую модель данных на БД, которая при этом ещё и изменяется во времени — то ведь в этом случае больше подходит "тяжёлый комбайн", нежели рукопашная реализация его фич поверх "лёгких" решений.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Причём формирование деревьев выражений на основе конструкций языка это более простая задача чем анализ строк — и в C++ это было доступно аж с первой версии стандарта C++98. И на основе этого (compile-time обход деревьев выражений на основе конструкций языка) создано уже много разных EDSL (Embedded Domain Specific Language) библиотек. EP>compile-time обход деревьев выражений на основе конструкций языка
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Здравствуйте, gandjustas, Вы писали:
EP>>>>>Если не нравится экспоненциальное количество запросов в исполняемом файле, то можно добавить динамики. Да, при этом запрос будет частично строится в runtime (с возможным кэшированием), но при этом не будет дорогой runtime reflection — в ней тут нет принципиальной необходимости, достаточно compile-time reflection. G>>>>В C# на compiled query можно добиться того же эффекта. EP>>>Так там разве Expression Tree не будет каждый раз в runtime обходится? G>>Конечно нет, в этом и суть compiled query.
EP>compiled query к чему привязывается? То есть на основании чего выбирается тот или иной закэшированный запрос?
Ни к чему не привязывается. Тебе просто возвращает функцию. https://msdn.microsoft.com/ru-ru/library/Bb399335(v=vs.100).aspx
EP>>>Изначально речь шла про принципиальную возможность. А возможно это не только в C++, но и например в D или Nemerle. G>>Так никто и не говорит про реализацию фреймворка уровня linq, но "принципиальная" возможность является возможностью именно тогда, когда есть хотя бы прототип.
EP>Речь идёт про аналог LINQ, но с разбором выражений и генерацией запросов в compile-time, то есть с нулевыми накладными расходами по сравнению с полностью ручной версией, о чём и говорил alex_public выше по ветке
G>>Ибо принципиально практически в любом яызке можно прикрутить кодогенератор, который будет что-то делать. Даже внешний. G>>Или даже лучше. "Принципиально" ничего не мешает не использовать тяжелую рефлексию для Expression Tree и сделать кеширование выражений, компилятор C# то доступен и средства его расширения есть.
EP>Так, давай проясним: EP>1. Ты согласен с тем что мой пример выше можно реализовать на основе внешнего кодогенератора? (после кодогенерации в коде появятся 16 уже готовых запросов) То есть та самая принципиальная возможность, с нулевыми накладными расходами в runtime. EP>2. Ты согласен с первым пунктом, но сомневаешься в том что это возможно в одном из следующих языков без внешнего кодогенератора? C++, D, Nemerle.
Ты и alex_public — не один и тот же человек? Что-то манера ведения дискуссии у вас совпадает.
Я согласен что можно реализовать вообще все с использованием внешнего кодогенератора\препроцессора. Но очень сомневаюсь, что получившееся будет лучше linq в c#.
Давай по пунктам:
1) Динамическая композиция — то есть можно взять запрос (любой, который можно сгенерировать, из другой сборки (dll), скомпилированной совершенно отдельно) и навесить на него фильтр, проекцию, группировку итд.
2) Поддержка как минимум операторов проекции, соединений, фильтров и группировки.
3) Все типизировано, то есть отсутствует возможность обратиться к полям, которые отсутствуют в таблице\подзапросе. Ошибки подсвечиваются прямо в IDE.
4) Компактный синтаксис, не требует указания одних и тех же данных в разных местах.
Причем все возможности должны быть одновременно. Пока мы видели, что статическая типизация и динамическая композиция в C++ взаимоисключающие.
G>>То есть принципиально C# может все что C++ и даже больше.
EP>Если брать язык + внешний кодогератор, то вообще-то у C++ возможностей больше, так как нет ограничений платформы
У C# уже тоже нет. Причем в браузере C# может работать в виде Silverlight, а C++ — нет. Так что последний по возможностям уже отстает.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Здравствуйте, gandjustas, Вы писали:
G>>Ну ок, возьми не C#, а любой другой статически типизированный язык. В любом языке все придет к формированию деревьев выражений на основе конструкций языка, которые потом надо будет отобразить на метаданные классов и на базе этого построить SQL. Так что накладные расходы также будут присутствовать.
EP>Вот конкретный пример: compile-time template engine
EP>В compile-time делается анализ строки, и на основе этого генерируется определённая последовательность вызовов, результирующий ASM код получается идентичным тому, который соответствует полностью ручному оптимальному коду.
А при чем тут Linq? Он как раз может не имея целого запроса в одном месте собрать его по кускам. Обратная задача не нужна.
EP>Причём формирование деревьев выражений на основе конструкций языка это более простая задача чем анализ строк — и в C++ это было доступно аж с первой версии стандарта C++98. И на основе этого (compile-time обход деревьев выражений на основе конструкций языка) создано уже много разных EDSL (Embedded Domain Specific Language) библиотек.
Давай уже работающий пример (аналог Linq, не уступающий по мощности и с типизацией) в студию. То что в теории все хорошо — я верю, на практике ничего пока не видно.
Здравствуйте, alex_public, Вы писали:
_>Здравствуйте, gandjustas, Вы писали:
_>>>Угу, кэширование — это сила. Только вот в каком месте? ) Очень эффективным (и причём не требующим никаких усилий при реализации) оно является при реализации в БД. И как раз именно при такой реализации (на фоне мгновенно возвращаемых запросов) тормознутость linq будет особенно видна. G>>Оо новое слово в архитектуре прям. Кеширование всегда делается ближе к потребителю, чтобы сократить не только затраы на доставание данных, но и на передачу по сети и обработку датасета на клиенте. Кстати на уровне БД кеширования запросов нет, угадай почему.
_>Нууу, если у тебя нет, то это не значит, что ни у кого нет. ))) Например вот http://dev.mysql.com/doc/refman/5.7/en/query-cache.html и кстати при попадание в кэш время запроса становится меньше 1 мс (хотя такой же первый запрос мог секунду обрабатываться).
Отличный пример. Запрос секунду обрабатывался почему? Наверное потому что данные были на диске, а не в памяти. И какбы идеально, чтобы меньше запросов требовало чтения с диска. Но если у тебя часть памяти сервера скушано кешем запросов, то чтения с диска будут происходить чаще, что приведет к общему снижению быстродействия.
Если же у тебя кеш получился высокоэффективный, и большая часть отдается из кеша, то в случае отсуствия кеша у тебя будут данные самих таблиц находиться в памяти и эффективно обслуживать все запросы, а не только те, которые совпадают буква-в-букву.
Вообще MySQL изобилует сомнительными решениями, я бы не стал его в пример приводить.
_>Как ты думаешь, будут ли на таком фоне заметны тормоза linq или нет? )
Нет, даже если у тебя приложение только и делает, что получает одну и ту же строку запроса. А если приложение более сложное и делает более сложные запросы, то даже под микроскопом не различишь.
Накладыне расходы на Linq видны при масштабе. Когда у тебя тысячи запросов в секунду, убрав linq можешь получить пару процентов прироста быстродействия. А если у тебя приложение только и делает, что ждет ответ от СУБД, то отказ от Linq ничего не даст вообще.
G>>А где пример? Так чтобы и динамический запрос был, и проекции с джоинами и типизацией.
_>Похоже у тебя какие-то проблемы с чтением...
Похоже это у тебя проблемы с пониманием.
_>Я тебе уже несколько раз написал, что в данной библиотеке вообще нет ничего без типизации. Что касается примеров, то чем тебя не устраивают примеры из самой библиотеки? ) Типа такого https://github.com/rbock/sqlpp11/blob/master/examples/sample.cpp или такого https://github.com/rbock/sqlpp11/blob/master/examples/select.cpp — чего
Я что-то не вижу как компилятор проверяет, что нельзя обращаться к row.feature если не выполнилась строка dysel.selected_columns.add(p.feature). Более того, мои знания языка C++ говорят что тип row в цикле for (const auto& row : db(dysel)) статический и проекцию никак не учитывает.
Я не очень понимаю как компилятор будет проверять, что указанное в select реально есть в таблицах, указанных во from. Аналогичная проблема в предикатом джоина, неясно как проверяется что выражение в предикате соответствует таблицам во from и join.
Так что я не верю что такм такой же уровень типизации, как в Linq. Покажи пример что будет если попробовать составить неправильный запрос.
А также приведи пример как навесить джоин на уже существующий запрос.
G>>Так покажи пример если присутствует. Пока я вижу тонны заголовочных файлов, а не пример использования. _>Вот Sinclair догадался сделать два лишних клика мышкой (один на папку examples и второй на cpp файл) и всё увидеть сам. А тебе несколько раз подсказывали сделать это и то не можешь...
Примеры я смотрел, но ответов на вопросы, которые я тебе уже 4 раза написал, в них нет.
_>>>Пример уже показал Евгений, хотя на мой взгляд показывать настолько очевидные вещи даже странно. Или кто-то на этом форуме не владеет if'ми? ))) G>>У него пример абстрактый, основанный на предположении что это можно сделать. Я тоже могу предположить, что linq перестанет использовать рефлексию и будет запросы в compile time генерить. Этого, кстати, довольно легко добиться в C#6 с его возможностями переписывания кода. G>>Но это все предположения, а фактического примера, который эквивалентен тому, что я написал, мы так и не увидли. _>Зато в библиотечке примеры вполне даже компилируемые. Но ты их похоже принципиально не замечаешь. )
Только там от силы 10% того, что умеет linq.
Тут два варианта: или больше не умеет, или не все так хорошо, как ты описываешь.
Поэтому приведи аналог того примера, что я писал и мы поверим. А пока — увы. Аналогов linq и близко нет.
Здравствуйте, gandjustas, Вы писали:
EP>>compiled query к чему привязывается? То есть на основании чего выбирается тот или иной закэшированный запрос? G>Ни к чему не привязывается. Тебе просто возвращает функцию. https://msdn.microsoft.com/ru-ru/library/Bb399335(v=vs.100).aspx
А, в этом смысле. А я думал ты про то, что можно это как-то сделать прозрачно — то есть в коде Expression Tree, а под капотом оно автоматом кэшируется и не обрабатывается второй раз.
Ок, как тогда будет выглядеть твой пример с четырьмя условиями, если туда добавить compiled query? Предполагаю что там тоже появятся 16 разных compiled query.
EP>>Речь идёт про аналог LINQ, но с разбором выражений и генерацией запросов в compile-time, то есть с нулевыми накладными расходами по сравнению с полностью ручной версией, о чём и говорил alex_public выше по ветке
Я уже выше говорил что полный рабочий пример приводить не собираюсь, так как это займёт для меня приличное время.
Если же тебе непонятен какой-то конкретный аспект, или ты считаешь что реализация какой-то из частей невозможна — то так и говори, постараюсь объяснить.
EP>>Так, давай проясним: EP>>1. Ты согласен с тем что мой пример выше можно реализовать на основе внешнего кодогенератора? (после кодогенерации в коде появятся 16 уже готовых запросов) То есть та самая принципиальная возможность, с нулевыми накладными расходами в runtime. EP>>2. Ты согласен с первым пунктом, но сомневаешься в том что это возможно в одном из следующих языков без внешнего кодогенератора? C++, D, Nemerle. G>Ты и alex_public — не один и тот же человек? Что-то манера ведения дискуссии у вас совпадает.
Нет Часто замечаю что в дискуссиях отстаиваем похожие тезисы — это видимо потому что релевантные знания ощутимо пересекаются
G>Я согласен что можно реализовать вообще все с использованием внешнего кодогенератора\препроцессора. Но очень сомневаюсь, что получившееся будет лучше linq в c#. G>Давай по пунктам: G>1) Динамическая композиция — то есть можно взять запрос (любой, который можно сгенерировать, из другой сборки (dll), скомпилированной совершенно отдельно) и навесить на него фильтр, проекцию, группировку итд.
Да, можно — достаточно передать полный тип и значение запроса. При любом изменении запроса, будет меняется и его тип.
Если нужно чтобы тип не менялся — то придётся прибегать к стиранию, что повлечёт за собой дополнительную runtime стоимость.
Если же динамическая композиция со стиранием типа не нужна (а она ведь нужна отнюдь не всегда, так?) — то получится совсем без runtime overhead'а, в отличии от LINQ.
G>2) Поддержка как минимум операторов проекции, соединений, фильтров и группировки.
Да, без проблем.
G>3) Все типизировано, то есть отсутствует возможность обратиться к полям, которые отсутствуют в таблице\подзапросе. Ошибки подсвечиваются прямо в IDE.
Конечно — без типизации быстродействия бы не получилось.
G>4) Компактный синтаксис, не требует указания одних и тех же данных в разных местах.
Само собой.
G>Причем все возможности должны быть одновременно. Пока мы видели, что статическая типизация и динамическая композиция в C++ взаимоисключающие.
Это не в C++ взаимоисключающие, а по сути. То есть даже при использовании внешнего генератора (с любым языком), в случае с .dll из пункта 1, есть выбор:
* либо выносить в интерфейс "жёсткий" тип запроса, который при изменении запроса будет меняться (и требовать переборки зависимого кода), но что позволит в месте вызова запроса производить полную compile-time обработку, без всякого runtime overhead'а.
* либо же делать стирание типа, что даст "мягкий" тип запроса, который не будет зависеть от его мелких деталей, но при этом мы неизбежно получаем runtime penalty — так как в месте вызова запроса мы уже не знаем всех его деталей во время компиляции, и полностью построить его в compile-time не сможем.
Этот выбор продиктован не какими-то недостатками C++, а самой задачей, и не зависит от языка/технологии. Там где нужна динамика приходится платить в рантайме.
G>>>То есть принципиально C# может все что C++ и даже больше. EP>>Если брать язык + внешний кодогератор, то вообще-то у C++ возможностей больше, так как нет ограничений платформы G>У C# уже тоже нет.
То есть, например, C# код можно запустить на восьмибитном микроконтроллере?
G>Причем в браузере C# может работать в виде Silverlight, а C++ — нет. Так что последний по возможностям уже отстает.
C++ вообще-то может компилироваться под .NET, и вроде как под Silverlight. Да и зачем этот Silverlight, если можно компилировать прямо в JS
Например движок Unreal был портирован на JS за 4 дня http://www.youtube.com/watch?v=BV32Cs_CMqo