Здравствуйте, Serginio1, Вы писали:
_>>Изначальный мой тезис (с которого началась вся эта бесконечная дискуссия) звучал так: "ORM на базе Linq — тормоза (относительно аналогичного кода на голых sql строках)". И этот тезис был полностью доказан в теме соответствующими тестами. Дальнейшей же обсуждение свелось к двум слегка холиварным направлениям: S>Еще раз в 100200 раз повторю, что запросы могут компилироваться и кэшироваться (если это не динамические запросы). Да и десериализация может компилироваться например с помощью Рослина или деревьев выражений (примеры я тебе уже кучу приводил) . Там много затрат идет на поиск объектов при не использовании AsNoTracking и еще куча подводных камней. S>Это будет ничем не медленнее голых SQL строк. Я тебе уже кучу примеров приводил.
Это всё слова. А тесты показывают другое. Да и не только тесты, но и примеры настоящих специалистов (а не форумных теоретиков), имеющих дело с сильно нагруженными системами.
Здравствуйте, alex_public, Вы писали:
_>http://ormbattle.net — "Домен ormbattle.net выставлен на продажу!". Угу, авторитетно. ) Да и на гитхабе тоже сразу видны все графики и таблицы измерений. ))) В общем я "поверил", да. )))
Этот проект уже лет 5 как не живой. Где-то был клон на github.
Что касается производительности linq2db, то "ха-ха" здесь не уместно. Производительности всегда уделялось самое пристальное внимание в том числе с помощью тяжёлой артиллерии типа Red-Gate ANTS Performance Profiler. При этом внимательно изучался код того же dapper, linq2sql, EF, были бессовестно украдены некоторые идеи, правда не касающиеся производительности. Выше упомянутый OrmBattle тоже сподвиг на работу над производительностью. В linq2db вошло лучшее из bltoolkit и RFD, а это как минимум 13 лет опыта разработки и использования подобных технологий.
Но главное — первыми пользователями linq2db, bltoolkit и RFD всегдя являлись сами разработчики этих библиотек. А халтурить для себя как-то непонятно зачем
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, ·, Вы писали:
_>>Подобный код можно принять за базу, относительно которой рассчитывать накладные расходы. Причём это на любом языке. А вот потом уже на некоторых языках мы можем попытаться записать тоже самое удобнее, но чтобы при этом не добавлять тормозов. ·>Неверно при условии "на любом языке". За базу надо брать код в маш-кодах.
Ты похоже не осознаёшь масштабы временных задержек, о которых говоришь. Даже самый тормознутые мейнстрим языки (Python и PHP) выполнят указанный пример на порядки быстрее времени обработки запроса в СУБД. Т.е. разница между языками вообще тут не принципиальна.
_>>Да это практически мгновенные операции на фоне времени исполнения самого запроса. А вот тормоза linq (происходящие от рефлексии) вполне себе сравнимы и даже частенько превосходят время исполнения запроса. ·>А почему ты всё время говоришь что в linq используется рефлексия? Можно поподробнее где она там используется и для чего? Мои скромные познания .net говорят, что там используется expression tree, который компилируется на ходу в IL, а потом даже может JIT-ится в маш-коды, склеивающие текст запроса.
Ты похоже путаешь просто linq и linq2database. В первом случае у нас действительно исполняется само выражение. А во втором случае совсем другая схема — выражение используется по сути как набор данных (дерево) из которых генерируется sql текст.
_>>Возможны ли какие-то решения близкие к linq (т.е. со статической проверкой sql кода, а не в виде sql строк), но без накладных расходов в рантайме. ·>Ты не показал решений без каких-либо накладных расходов в рантайме (Нулевой Оверхед™), или я что-то не знаю и супер-мета-пупер-языки query+=" and ..." умеют выполнять в компайл-тайме? Хочется услышать что конкретно ты называешь накладными расходами.
Эм, тебе надо в тысячный раз повторить, что это как раз и рассматривается за базис от которого отсчитываются накладные расходы? ) Но кстати даже если бы и рассчитывать от формально заданной на этапе компиляции строки, то всё равно почти ничего не изменилось бы. Подобный код склейки строк выполняется за пренебрежимо малое время в сравнение с выполнением даже самого быстрого запроса в РСУБД.
_>>Подобное легко реализуется на любом языке с развитым метапрограммированием. И даже в языке с не самым лучшим МП (типа C++) это тоже вполне реализуемо. ·>Можно пожалуйста объяснить и показать какая там происходит мета-магия? ·>Твоё query.where.add() как я понимаю строит дерево выражения (ничего не напоминает?) из контейнеров и палок, которое потом интерпретируется для склейки текста запроса. Можно пояснить за счёт чего интерпретация работает быстрее маш-кодов?
Нет, дерево выражений строится на этапе компиляции. Но отдельные его элементы могут быть доступны для модификации. )
Здравствуйте, TK, Вы писали:
_>>Ничего подобного. Linq делает не это. Точнее не только это. В начале он анализирует (через тормозную рефлексию) выражение и генерирует из него код склейки и только потом запускает собственно этот код. Причём как раз этот анализ занимает во много раз больше времени, чем собственно склейка строк. И именно оттуда и идут тормоза. TK>Дерево в Linq анализируется обычно через ExpressionVisitor. С каких это пор для обхода дерева нужна рефлексия?
Здравствуйте, alex_public, Вы писали:
_>Ну а по Dapper'у я видел похоже цифры (что он на пару процентов медленнее прямого доступа).
Есть подозрение, что dapper сильно стал хуже в плане производительности с тех пор как появилась первая информация о его быстродействии. linq2db после соответсвующих оптимизаций шёл с ним практически вровень. Но последние тесты показывают серьёзное падение производительности dapper. Видимо парни на это дело забили.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, alex_public, Вы писали:
_>Не надо мне какие-то исходники показывать. Покажи просто один linq запрос и генерируемые из него оптимизированные sql строки под разные СУБД. Всё. Такая простая вещь будет 100% доказательством.
Мне на слово поверишь как разработчику?
Оптимизируются. В несколько подходов. От обработки Expression Tree, до генерации SQL для различных дата провайдеров.
Главная проблема не в оптимизации как таковой, а в наличиствующих инструментах. Дайте паттерн матчинг в C# и отжать всё лишнее из генерируемого SQL вообще будет не проблема.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, alex_public, Вы писали:
_>Да, да, тут у некоторых товарищей с помощью лёгкого волшебства linq2db ещё и заметно быстрее прямого доступа к БД получался. Помним помним. )))
Это объясняется просто. В обычном рукописном запросе через ADO.NET изпользуется выборка данных из полей по имени. linq2db это делает по индексам. В рукописном запросе данные запрашиваются через GetValue, которое возращает object, дальше кастинг, боксинг и все дела. linq2db один раз запрашивает у ридера типы полей и использует наиболее оптимальный способ получения данных, вплоть до вызова нативных методов провайдера. Потом это всё кешируется и используется повторно.
Поэтому в общем случае linq2db будет работать быстрее, чем рукописный код, если этот рукописный код специально не использует индексы и правильные GetXXX методы. Но делается это в основном в тестах и только. В жизни этим никто не занимается по причине невменяемости поддержки таких решений.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, alex_public, Вы писали:
_>Здравствуйте, TK, Вы писали:
_>>>Ничего подобного. Linq делает не это. Точнее не только это. В начале он анализирует (через тормозную рефлексию) выражение и генерирует из него код склейки и только потом запускает собственно этот код. Причём как раз этот анализ занимает во много раз больше времени, чем собственно склейка строк. И именно оттуда и идут тормоза. TK>>Дерево в Linq анализируется обычно через ExpressionVisitor. С каких это пор для обхода дерева нужна рефлексия? _>Отвечу так http://stackoverflow.com/questions/5396290/does-entity-framework-use-reflection-and-hurt-performance просто чтобы не тратить самому время на поиск доказательств (без этого в данной темке уже не верят) в документации и исходниках. )
Это вообще-то об EF, а значит "LINQ to Entities".. Мы вообще-то о другом.
Да и вообще, ты опять привёл доказательство против своей же точки зрения:
main performance hit is at the time of reflecting aka binding which is reading the type metadata into xxxInfo (such as MethodInfo) and this happens at the application startup
.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, IT, Вы писали:
_>>Не надо мне какие-то исходники показывать. Покажи просто один linq запрос и генерируемые из него оптимизированные sql строки под разные СУБД. Всё. Такая простая вещь будет 100% доказательством.
IT>Мне на слово поверишь как разработчику? IT>Оптимизируются. В несколько подходов. От обработки Expression Tree, до генерации SQL для различных дата провайдеров.
Linq2db raw SQL — что это такое, напрямую выполняется SQL или же это блудняк навроде Entity SQL ?
На что уходит основное время выполнения запроса(кроме самой БД, естественно) ? По тестам получается так, что запрос готовится от 10 до 100% времени общего времени. Вот хочется подробностей. Какая там структура расходов, визиторы, рефлексия, маппинги, оптимизации ?
Здравствуйте, alex_public, Вы писали:
_>>>Подобный код можно принять за базу, относительно которой рассчитывать накладные расходы. Причём это на любом языке. А вот потом уже на некоторых языках мы можем попытаться записать тоже самое удобнее, но чтобы при этом не добавлять тормозов. _>·>Неверно при условии "на любом языке". За базу надо брать код в маш-кодах. _>Ты похоже не осознаёшь масштабы временных задержек, о которых говоришь. Даже самый тормознутые мейнстрим языки (Python и PHP) выполнят указанный пример на порядки быстрее времени обработки запроса в СУБД. Т.е. разница между языками вообще тут не принципиальна.
Нет, я свожу к абсурду твою точку зрения. Хотя ты опять сам справляешься лучше. Вот сейчас сказал, что PHP тормоза не оказывают ощутимое влияние в случае работы с СУБД, а linq рефлексия (которой как выяснилось нет) вдруг стала источником тормозов?
_>·>А почему ты всё время говоришь что в linq используется рефлексия? Можно поподробнее где она там используется и для чего? Мои скромные познания .net говорят, что там используется expression tree, который компилируется на ходу в IL, а потом даже может JIT-ится в маш-коды, склеивающие текст запроса. _>Ты похоже путаешь просто linq и linq2database. В первом случае у нас действительно исполняется само выражение. А во втором случае совсем другая схема — выражение используется по сути как набор данных (дерево) из которых генерируется sql текст.
Ок. Допустим. И причём тут рефлексия?
И чем принципиально этот набор данных отличается от такого if(categoryName) q.where.add(Category.name.like(*categoryName+"%"))?
_>>>Возможны ли какие-то решения близкие к linq (т.е. со статической проверкой sql кода, а не в виде sql строк), но без накладных расходов в рантайме. _>·>Ты не показал решений без каких-либо накладных расходов в рантайме (Нулевой Оверхед™), или я что-то не знаю и супер-мета-пупер-языки query+=" and ..." умеют выполнять в компайл-тайме? Хочется услышать что конкретно ты называешь накладными расходами. _>Эм, тебе надо в тысячный раз повторить, что это как раз и рассматривается за базис от которого отсчитываются накладные расходы? ) Но кстати даже если бы и рассчитывать от формально заданной на этапе компиляции строки, то всё равно почти ничего не изменилось бы. Подобный код склейки строк выполняется за пренебрежимо малое время в сравнение с выполнением даже самого быстрого запроса в РСУБД.
И чем этот базис отличается от linq-овского? Что конкретно делает linq такое, что создаёт этот самый оверхед?
_>·>Можно пожалуйста объяснить и показать какая там происходит мета-магия? _>·>Твоё query.where.add() как я понимаю строит дерево выражения (ничего не напоминает?) из контейнеров и палок, которое потом интерпретируется для склейки текста запроса. Можно пояснить за счёт чего интерпретация работает быстрее маш-кодов? _>Нет, дерево выражений строится на этапе компиляции. Но отдельные его элементы могут быть доступны для модификации. )
Как и в linq по сути. В чём отличие-то?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали:
·>Это вообще-то об EF, а значит "LINQ to Entities".. Мы вообще-то о другом.
Да, он местами под linq имеет ввиду EF, местами — вообще все linq-провайдеры. Где именно какой вариант он пояснять отказывается.
Ктото ему сказал, что компиляция SQL по дереву выражений есть чистая рефлексия, вот он и повторяет эту мантру. До кучи, у него Питон, что медленее любой рефлексии в квадрате, по умолчанию считается неплохим инструментом для работы с БД.
·>Да и вообще, ты опять привёл доказательство против своей же точки зрения:
main performance hit is at the time of reflecting aka binding which is reading the type metadata into xxxInfo (such as MethodInfo) and this happens at the application startup
.
"примеры настоящих специалистов" @ alex_public
На самом деле EF это жесточайший тормоз и уродец. Но дело в том что этот блудняк есть тяжелый full-blown ORM с change-tracking, блекджеком и шлюхами. Многие вещи на его базе пишутся практически мышом. То есть, он скорее RAD-инструмент.
Здравствуйте, IT, Вы писали:
IT>Есть подозрение, что dapper сильно стал хуже в плане производительности с тех пор как появилась первая информация о его быстродействии. linq2db после соответсвующих оптимизаций шёл с ним практически вровень. Но последние тесты показывают серьёзное падение производительности dapper. Видимо парни на это дело забили.
"Сильно стал хуже" — это имеется ввиду деградация перформанса или конкуренты стали быстрее ?
Здравствуйте, Ikemefula, Вы писали:
I>Linq2db raw SQL — что это такое, напрямую выполняется SQL или же это блудняк навроде Entity SQL ?
Сотвственно вот:
public IEnumerable<T> Query<T>(Func<IDataReader,T> objectReader)
{
DataConnection.InitCommand(CommandType, CommandText, Parameters, null);
if (Parameters != null && Parameters.Length > 0)
SetParameters(DataConnection, Parameters);
using (var rd = DataConnection.ExecuteReader(CommandBehavior))
while (rd.Read())
yield return objectReader(rd);
}
I>На что уходит основное время выполнения запроса(кроме самой БД, естественно) ? По тестам получается так, что запрос готовится от 10 до 100% времени общего времени. Вот хочется подробностей. Какая там структура расходов, визиторы, рефлексия, маппинги, оптимизации ?
Инициализация команды, установка параметров и вызов DataConnection.ExecuteReader. objectReader отдельная вещь, которая представляет собой сгенерированную с помощью ExpressionTree фунцию.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, ·, Вы писали:
·>Нет, я свожу к абсурду твою точку зрения. Хотя ты опять сам справляешься лучше. Вот сейчас сказал, что PHP тормоза не оказывают ощутимое влияние в случае работы с СУБД, а linq рефлексия (которой как выяснилось нет) вдруг стала источником тормозов?
Уже месяц хочу от него пояснений, так что может еще один-другой месяц и выясним все секреты
_>>Ты похоже путаешь просто linq и linq2database. В первом случае у нас действительно исполняется само выражение. А во втором случае совсем другая схема — выражение используется по сути как набор данных (дерево) из которых генерируется sql текст. ·>Ок. Допустим. И причём тут рефлексия? ·>И чем принципиально этот набор данных отличается от такого if(categoryName) q.where.add(Category.name.like(*categoryName+"%"))?
Принципиально по выражениям выполняется компиляция запроса в SQL с оптимизациями, как говорит IT, на нескольких уровнях. От устранения лишних полей, до изменения структуры запросов.
alex_public утверждает, что
1 разница недопустимо велика (10-100% в замерах на синтетических вусмерть закешированых запросах)
2 весь оверхед чистая рефлексия
_>>Нет, дерево выражений строится на этапе компиляции. Но отдельные его элементы могут быть доступны для модификации. ) ·>Как и в linq по сути. В чём отличие-то?
Он считает, что ручная оптимизация сборет любое linq-решение. То есть, все что надо, это склейка-на-стероидах, своего рода DSL на шаблонах С++, который выдаст качественный код склейки строк.
Здравствуйте, alex_public, Вы писали:
_>Ты имеешь в виду компилируемым? ) Но тогда он будет уже зависеть от особенностей языка и соответствующих библиотек в нём. Так это просто идея была универсальная... Ну да ладно, чтобы не придирались (а то бывают тут неадекваты, которые сразу начнут орать "ааа не можешь", хотя задачка для школьника) покажу пример на той же самой библиотечке C++: _>
И где обещанные compile-time проверки? Такую байду я и на PHP могу ваять. Как compiler проверит, что условие в первом if верное и ты не нарвёшься на unknown column из-за забытого join?
Да и вообще. Ты уже заметил разницу? Вместо трёх тривиальных if тебе пришлось забабахать ещё один магический новый (кстати надо было два, т.к. у меня было Category.Group.Name — т.е. ещё один join). А ведь мы ещё проекции не добавили, а ещё сортировки есть... Сложность кода растёт экспоненциально.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, Ikemefula, Вы писали:
I>"Сильно стал хуже" — это имеется ввиду деградация перформанса или конкуренты стали быстрее ?
Первое. Он реально был очень быстр. Мне пришлось серьёзно повозиться, чтобы с ним сравняться и хоть чуть-чуть стать побыстрее. А сейчас судя по тестам, толи с тестами что-то не то, толи с даппером. Его код давно уже не смотрел, но не исключено, что парни увлёкшись новыми фичами где-то сломали производительность.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
I>>Linq2db raw SQL — что это такое, напрямую выполняется SQL или же это блудняк навроде Entity SQL ?
IT>Сотвственно вот: IT>
IT> public IEnumerable<T> Query<T>(Func<IDataReader,T> objectReader)
IT> {
IT> DataConnection.InitCommand(CommandType, CommandText, Parameters, null);
IT> if (Parameters != null && Parameters.Length > 0)
IT> SetParameters(DataConnection, Parameters);
IT> using (var rd = DataConnection.ExecuteReader(CommandBehavior))
IT> while (rd.Read())
IT> yield return objectReader(rd);
IT> }
IT>
Я это видел, но не понимаю этот код. Извини, поменял стек четыре года назад. Что тут происходит ? Что с чем связано и где тот raw-sql и как он уходит в базу ?
I>>На что уходит основное время выполнения запроса(кроме самой БД, естественно) ? По тестам получается так, что запрос готовится от 10 до 100% времени общего времени. Вот хочется подробностей. Какая там структура расходов, визиторы, рефлексия, маппинги, оптимизации ?
IT>Инициализация команды, установка параметров и вызов DataConnection.ExecuteReader. objectReader отдельная вещь, которая представляет собой сгенерированную с помощью ExpressionTree фунцию.
Я некорректно выразился. Нужно что бы ты подсказал на счет разницы между Linq2db vs Linq2db-raw-SQL. Она довольно большая, судя по тесту, 10-100%. Нужна структура расходов вот этой разницы.
Здравствуйте, gandjustas, Вы писали: _>>Строка то всем очевидная и я её озвучил только потому, что ты признался что "Я по-разному пробовал, результат не меняется", т.е. что даже такая банальщина тебе уже не по силам. Ну так теперь, когда ты узнал как писать вменяемый код, ты понимаешь что все твои разговоры об оптимизации в той статье были бредовыми? ) Как раз из-за того что ты рассматривал только варианты хранимки vs linq. G>Ты хочешь сказать что подобные хранимки никто никогда не пишет, а все клеят строки в прикладном коде? Ты упоротый чтоли? G>Как раз склейкой никто не занимается, потому что это ад, а хранимки пишут гораздо чаще, чем это стоит делать.
Что-то у тебя в двух последовательных фразах прямое противоречие. Так хранимки надо чаще использовать или реже? ))) _>>Мне не очевидно из данного кода что там за join, зачем он и вообще о чём речь. Или показывай подробнее про задачу, таблицы и т.п. или просто покажи какой генерируется итоговый sql. G>Почему тебе не очевидно? G>Есть товары и категории, одна категория — много товаров. У нас в каталоге товаров фильтры — один из них по категории. Небольшой кусок кода, который фильтры обрабатывает. G>Вообще-то все поняли что я написал, даже те кто с linq не работали.
Ну например неясно как фильтр задаёт категорию. Т.е. если у нас ситуация, когда любой конфигурации фильтра (ну кроме null) соответствует ровно одна категория (а в твоём примере было сравнение по полю name категории, что как раз очень похоже на такой случай), то это принципиально меняет наиболее эффективный путь решения задачи.
Ну и да, для случая обязательного join решение уже давно было в одном из моих сообщений выше. G>Понятно. Считаем что говноподелка на C++ медленнее EF пока не доказано обратное.
Считай как хочешь, тебя всё равно не переубедить даже явными тестами, так что мне всё равно.
Но вот лично для себя меня этот вопрос всё же заинтересовал, т.к. подобных тестов я что-то не видел (что впрочем не удивительно, учитывая концепцию решения). Всё же интересно о каком порядке величины накладных расходов можно говорить в случае подобной библиотечки. Для проверки этого я набросал простенький тестовый пример для sqlite, работающий на самом базовом уровне (т.е. точно без всяких накладных расходов):
row sqlite
constexpr auto count=1000;
int main()
{
sqlite3* db;
if(sqlite3_open("test.db", &db)) return -1;
// sqlite3_exec(db, "create table Test(id integer primary key autoincrement, name text not null, fvalue real not null)", nullptr, nullptr, nullptr);
// sqlite3_exec(db, "create index fvalue on Test (fvalue)", nullptr, nullptr, nullptr);auto start=high_resolution_clock::now();
sqlite3_exec(db, "begin", nullptr, nullptr, nullptr);
sqlite3_exec(db, "delete from Test", nullptr, nullptr, nullptr);
sqlite3_stmt* stmt;
if(sqlite3_prepare(db, "insert into Test (name, fvalue) values (?, ?)", -1, &stmt, nullptr)==SQLITE_OK){
for(auto i=0.0; i<2*M_PI; i+=0.000001*count){
sqlite3_bind_text(stmt, 1, ("sin("+to_string(i)+")").c_str(), -1, SQLITE_TRANSIENT);
sqlite3_bind_double(stmt, 2, sin(i));
sqlite3_step(stmt);
sqlite3_reset(stmt);
}
sqlite3_finalize(stmt);
}
sqlite3_exec(db, "commit", nullptr, nullptr, nullptr);
cout<<"delete and insert: "<<duration_cast<microseconds>(high_resolution_clock::now()-start).count()<<" mcs"<<endl;
int64_t id;
string name;
double fvalue;
start=high_resolution_clock::now();
if(sqlite3_prepare(db, "select * from Test where fvalue>? order by fvalue limit 10 offset 10", -1, &stmt, nullptr)==SQLITE_OK){
for(auto i=0.0; i<0.5; i+=0.5/count){
sqlite3_bind_double(stmt, 1, i);
while(sqlite3_step(stmt)==SQLITE_ROW){
id=sqlite3_column_int64(stmt, 0);
name=(const char*)sqlite3_column_text(stmt, 1);
fvalue=sqlite3_column_double(stmt, 2);
}
sqlite3_reset(stmt);
}
sqlite3_finalize(stmt);
}
cout<<"prepared select: "<<duration_cast<microseconds>(high_resolution_clock::now()-start).count()/count<<" mcs"<<endl;
start=high_resolution_clock::now();
for(auto i=0.0; i<0.5; i+=0.5/count){
if(sqlite3_prepare(db, ("select * from Test where fvalue>"+to_string(i)+" order by fvalue limit 10 offset 10").c_str(), -1, &stmt, nullptr)==SQLITE_OK){
while(sqlite3_step(stmt)==SQLITE_ROW){
id=sqlite3_column_int64(stmt, 0);
name=(const char*)sqlite3_column_text(stmt, 1);
fvalue=sqlite3_column_double(stmt, 2);
}
sqlite3_finalize(stmt);
}
}
cout<<"select: "<<duration_cast<microseconds>(high_resolution_clock::now()-start).count()/count<<" mcs"<<endl;
sqlite3_close(db);
return 0;
}
И его аналог с помощью sqlpp11:
sqlpp sqlite
SQLPP_DECLARE_TABLE((Test),
(id , int , SQLPP_PRIMARY_KEY, SQLPP_AUTO_INCREMENT)
(name , varchar(255), SQLPP_NOT_NULL)
(fvalue, double , SQLPP_NOT_NULL))
constexpr auto test=Test::Test{};
constexpr auto count=1000;
int main()
{
sqlite::connection db({"test.db", SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE});
auto start=high_resolution_clock::now();
auto tr=start_transaction(db);
db(remove_from(test).unconditionally());
auto qi=db.prepare(insert_into(test).set(test.name=parameter(test.name), test.fvalue=parameter(test.fvalue)));
for(auto i=0.0; i<2*M_PI; i+=0.000001*count){
qi.params.name="sin("+to_string(i)+")";
qi.params.fvalue=sin(i);
db(qi);
}
tr.commit();
cout<<"delete and insert: "<<duration_cast<microseconds>(high_resolution_clock::now()-start).count()<<" mcs"<<endl;
int64_t id;
string name;
double fvalue;
start=high_resolution_clock::now();
auto q=db.prepare(select(all_of(test)).from(test).where(test.fvalue>parameter(test.fvalue)).order_by(test.fvalue.asc()).limit(10).offset(10));
for(auto i=0.0; i<0.5; i+=0.5/count){
q.params.fvalue=i;
for(const auto& row: db(q)){
id=row.id;
name=row.name;
fvalue=row.fvalue;
}
}
cout<<"prepared select: "<<duration_cast<microseconds>(high_resolution_clock::now()-start).count()/count<<" mcs"<<endl;
start=high_resolution_clock::now();
for(auto i=0.0; i<0.5; i+=0.5/count){
for(const auto& row: db(select(all_of(test)).from(test).where(test.fvalue>i).order_by(test.fvalue.asc()).limit(10).offset(10))){
id=row.id;
name=row.name;
fvalue=row.fvalue;
}
}
cout<<"static select: "<<duration_cast<microseconds>(high_resolution_clock::now()-start).count()/count<<" mcs"<<endl;
start=high_resolution_clock::now();
auto qd=dynamic_select(db).columns(all_of(test)).from(test).dynamic_where().order_by(test.fvalue.asc()).limit(10).offset(10);
for(auto i=0.0; i<0.5; i+=0.5/count){
auto q=qd;
if(true) q.where.add(test.fvalue>i);
for(const auto& row: db(q)){
id=row.id;
name=row.name;
fvalue=row.fvalue;
}
}
cout<<"dynamic select: "<<duration_cast<microseconds>(high_resolution_clock::now()-start).count()/count<<" mcs"<<endl;
}
Так вот результаты тестирования (естественно измерение производилось не один раз и это уже обработанные результаты) с выключенными индексами (эмуляция реальной работы, с большой БД) дали такой результат:
Т.е. разница явно где-то за пределами погрешности измерения. И только введение индекса (и соответственно приведение времени запроса к смешным значениям) позволило выявить реальный размер накладных расходов данной библиотечки:
Имеем где-то порядка 2-12 микросекунд накладных расходов на простом запросе классического вида ради получения статически типизированного кода. Конечно не идеал, но уже вполне терпимо, а не дикие 12 миллисекунд как у твоих примеров с linq.
P.S. С удовольствием глянул бы на аналогичное измерение для любой версии linq2database. ) А ещё интереснее было бы сравнить абсолютные цифры при измерение на одном компьютере, но для компиляции linq варианта по идее нужны какие-то зависимости на сторонние библиотеки .net, что мне уже сложно и лень. )))
Здравствуйте, IT, Вы писали:
_>>система может заработать намного быстрее в случае оптимизации простенького, но постоянно используемого запроса. IT>Для таких запросов существуют CompiledQuery. Там всё закешировано до предела и вполне может состязаться с ручным кодом.
"Простенький и постоянно используемый" совершенно не означает "статический". ) Хотя в определённых случаях да, сработает.
Здравствуйте, alex_public, Вы писали:
G>>Ты хочешь сказать что подобные хранимки никто никогда не пишет, а все клеят строки в прикладном коде? Ты упоротый чтоли? G>>Как раз склейкой никто не занимается, потому что это ад, а хранимки пишут гораздо чаще, чем это стоит делать.
_>Что-то у тебя в двух последовательных фразах прямое противоречие. Так хранимки надо чаще использовать или реже? )))
Хранимки слишком часто пишут для того, что бы уйти от склейки строк в вызывающем коде. То есть, чаще, чем это собственно необходимо. Я видел даже генерацию html прямо в хранимой процедуре
_>Имеем где-то порядка 2-12 микросекунд накладных расходов на простом запросе классического вида ради получения статически типизированного кода. Конечно не идеал, но уже вполне терпимо, а не дикие 12 миллисекунд как у твоих примеров с linq.
Ты прочитал данные даже не из локального файла, а из кеша операционной системы. 2-12микросекунд это чтение данных из прогретого кеша операционной системы, фактически — обращение к виртуальной памяти. База то у тебя вся в памяти, алё !
Отключал ты индексы или нет, значения почти не имеет, ибо ты перепахиваешь виртуальную память, но сравниваешь с чтением по сети
Те самые 12мс — это именно обращение и передача данных по сети. Кроме того, если ты помнишь, то там даже прямой доступ к бд давал почти те же милисекунды.
На С++ аналог того самого теста, скажем, через драйвер odbc, не cможет дать меньше чем милисекунды, ну никак.