28.01.2004, 15:29 | #1 |
Участник
|
Почему такое расхождение в скорости выполнения
Я не понимаю механизм доступа к данным в Axapta 2.5 Сделал простенький тест
PHP код:
Собственно, изначально вопрос возник в попытке ускорить выброс результата выборки в текстовый файл. Выполнение Query с последующим циклом оказалось сопоставимо по времени с выбросом из ResulSet (примерно одинаково). Т.е. наиболее оптимальным по скорости оказалась такая технология: -) Создать таблицу на сервере -) Выполнить хранимую процедуру сервера по наполнению этой таблицы -) Средствами Axapta сделать выброс из этой таблицы в текстовый файл Но это достаточно сложно по исполнению, поэтому не хотелось бы этого делать. Меня интересует, почему есть такое явное расхождение в скорости обработки казалось бы одинаковых операций? |
|
28.01.2004, 15:51 | #2 |
Участник
|
А что здесь удивительного ?
При использовании класса UserConnection вы же фактически напрямую обращаетесь к SQL-Server. Ведь Аксапта - это же по сути скрипт. Ваш код написанный на X++ должен пройти всю цепочку обработки бизнес-логики, компиляции, преобразований прежде чем запрос уйдет к SQL-серверу. |
|
28.01.2004, 15:57 | #3 |
Участник
|
Это Вы о чем?
Результат запроса я уже получил. Расхождение в скорости имеет место быть именно в цикле while. Т.е. это не собственно выполнение запроса, а выборка информации из результирующей выборки. Какое отношение ко всему этому имеет бизнес-логика и компиляция? Или компилируется каждая строка отдельно по мере исполнения? |
|
28.01.2004, 16:10 | #4 |
NavAx
|
Цитата:
Изначально опубликовано Владимир Максимов
уже получил P.S. next обычно ставят в конце while, т.к. select на автомате дает первую запись |
|
28.01.2004, 16:30 | #5 |
Участник
|
Думаете - это первый вариант? Это упрощение моего оригинального запроса, где связаны 4 таблицы по INNER JOIN, да еще несколько промежуточных вычислений внутри цикла.
Я же писал, что тормозит именно цикл while. Все что вне него - это разовые операции принципиального влияния на общее время выполнения не оказывают Хорошо, переставил timenowBegin = timenow() после select и R=... Перекинул next в конец цикла. На результат это никак не повлияло! У меня 3-х звенка. Где здесь предмет кэширования я не понимаю. Точнее, если есть кэширование для табличной переменной, то почему его нет для ResultSet или Query? |
|
28.01.2004, 16:54 | #6 |
Участник
|
Может, дело в классе (методах класса) ResultSet?
|
|
28.01.2004, 17:06 | #7 |
Участник
|
Вообще-то, как я уже писал, я делал то же самое через QueryRun, т.е. цикл имел вид:
while (qr.next()) { inventTable = qr.get(tablenum(InventTable)); fileOut.write(inventTable.ItemGroupID,inventTable.ItemID,...); } Скорость осталась примерно такая же как и при ResulSet. Т.е. явно медленнее, чем прямое сканирование табличной переменной. Конечно R.getString() влияет, только какое-то непонятное влияние. |
|
28.01.2004, 17:36 | #8 |
Участник
|
Владимир Максимов, я совсем запутался.
Так кто на ком стоял? Какой метод быстрее, а какой медленнее? Вы можете привести время для способа 1 и способа 2? |
|
28.01.2004, 17:45 | #9 |
Banned
|
ResultSet у него работает медленнее. С самого начала он так написал.
Я другого не понимаю: чего автор тут добивается? От гневных реплик кэширование, выполняемое ядром Аксапта в 2-уровневом ли, в 3-уровневом режимах для таблиц, явно описанных в AOT, в ResultSet не появится. И исходных кодов у нас нет, чтобы посмотреть, через сколько COM-компонентов и промежуточных интерфейсов работает ResultSet. |
|
28.01.2004, 17:48 | #10 |
Участник
|
Самый быстрый способ - это сканирование таблицы, т.е.
while select inventTable ... ну или как я написал в примере select * from inventTable; while (i<=1000) ... Выбрасывает около 5тыс строк из 40 полей разного типа примерно за 5 секунд Использование ResultSet или QueryRun на той же выборке дает время на порядок медленнее (примерно 50 секунд). Есть некоторое расхождение между использованием ResultSet и QueryRun, но в пределах погрешности (несколько секунд). Вот я и пытаюсь понять: почему собственно? Подчеркну - кроме собственно commaIo.write() внутри цикла while ничего нет (ну, кроме QueryRun.get()) |
|
28.01.2004, 17:49 | #11 |
Участник
|
Скорее всего чтение из табличной переменной действительно быстрее потому что
resultSet табличной переменной кэширован. А ResultSet полученный путем класса userConnection не кэшируется. Не предусмотрено наверное. |
|
28.01.2004, 18:04 | #12 |
----------------
|
Посмотрите в Profiler как обрабатываются ваши запросы SQL-сервером
|
|
28.01.2004, 18:09 | #13 |
Участник
|
Wamr
Может быть я чего-то не понимаю, но Вы хотите сказать, что на каждом шаге цикла, по команде next запрос SQL выполняется заново? Время выполнения собственно запросов (на 5 тыс строк) на самом MS SQL 2000 составляет менее секунды |
|
28.01.2004, 18:14 | #14 |
Участник
|
Цитата:
Изначально опубликовано andreynikolai
Скорее всего чтение из табличной переменной действительно быстрее потому что resultSet табличной переменной кэширован. select ... where этим свойством пользуется. ResultSet вроде не пользуется. Всегда считал, что query тоже этим свойством пользуется... Владимир Максимов, скорость выполнения сравнивалась при первом после входа запуске или при повторных запусках? Что будет, если отключить кэширование, выйти, зайти и протестировать еще раз? |
|
28.01.2004, 18:25 | #15 |
Участник
|
Видимо, я ненамеренно ввел в заблуждение тем, что использовал в примере таблицу InventTable. На самом деле, при проверке использовалась собственная таблица (в том смысле, что не основная) и у этой таблицы CachLookup = None (данные по времени приведены именно для такой настройки). Хотя использование InventTable не противоречит полученным результатам.
Результаты проверок от повторных запусков зависят слабо. Разница в пределах погрешности (несколько секунд). Сейчас убегаю, завтра продолжим |
|
28.01.2004, 19:12 | #16 |
----------------
|
Я знаю, что while select работает через курсоры и вытягивает записи с сервера (fetch) кусками по несколько записей. Как отрабатывает ResultSet я не знаю. Но раз уж вы заинтересовались этой темой, то было бы логично посмотреть именно работу SQL-сервера.
Так же было замечено, что ResultSet тормозит при определении конца цикла . Можно попробовать сделать top 110 а по счетчику оборвать цикл на 100 записях. |
|
28.01.2004, 19:56 | #17 |
Модератор
|
слушайте Wamr'a, Wamr плохого не посоветует
достаточно посмотреть на то, что попадает в профайлер в каждом проходе в первом цикле и что во втором а) Connection, Statement аксапта "честно" отправляет запрос "select top 1000 from myTable" -exec sp_cursoropen ... открывает курсор и фетчит записи ПО ОДНОЙ -exec sp_cursorfetch 180150058, 2, 1, 1 - по одному вызову на запись обратите внимание на последний параметр (1) и вот это б) WHILE SELECT и табличная переменная -exec sp_cursorexecute - у нас есть уже есть "откомпилированный" MSSQL запрос, так что мы его используем повторно. MSSQL не парсит его повторно, не строит план исполнения (он уже есть), это хорошо, что-то мы на этом сэкономили. но все это фигня -exec sp_cursorfetch 180150050, 2, 1, 16 - по одному вызову на несколько записей обратите внимание на последний параметр, он равен 16 (у меня так с последней версией MDAC, сколько будет у вас - не знаю) т.е. мы получаем recordset кусками по 16 записей в конечном счете мы немного выиграли на суммарном траффике но и это тоже фигня мы сделали В РАЗЫ меньше fetch() по сравнению с первым вариантом ну а раз время выполнение SELECT * FROM MYTABLE у нас мало по сравнению со временем на то, чтобы отослать результаты клиенту (запрос тривиальный), выигрыш у нас идет в разы, если не на порядок а кэширование тут не при чем, SELECT * FROM MYTABLE, даже с условием WHERE, но не по уникальному ключу у нас не кэшируются, разве что с CacheLookup=EntireTable, но этот случай мы рассматривать не будем.. З.Ы. вот уж дорвался до форума так дорвался З.З.Ы. до QueryRun не добрался, как-нибудь потом подумалось.. насчет зависимости числа fetch-ащихся записей от версии MDAC я похоже того.. немного погорячился для одинаковых версий аксапты оно скорее всего будет одинаковым |
|
28.01.2004, 20:00 | #18 |
Участник
|
Цитата:
Изначально опубликовано Vadik
а кэширование тут не при чем, SELECT * FROM MYTABLE, даже с условием WHERE, но не по уникальному ключу у нас не кэшируются |
|
29.01.2004, 10:38 | #19 |
Участник
|
Vadik
Как Вы получили эти числа в профайлере? У меня как я ни кручу профайлер ничего подобного не наблюдается (Axapta 2.5). В профайлере я вижу только дерево вызовов. Т.е. сколько времени на какую строку ушло. Если судить по времени обработке строк, получается, что Statment фетчит по одной записи, а While Select по 24 за раз. НО! Если просуммировать время фетча для 24 строк Statmenta и сравнить с временем фетча для одной строки While Select, то я получаю примерно одинаковое значение. Принципиальное расхождение по времени наблюдается именно на команде записи в текстовый файл! В моем случае на 2 порядка. Опять же, судя по плану выполнения запроса в профайлере индексы используются в обеих случаях. Точнее используется кластерный индекс. Собственно, этого и следовало ожидать. Здесь же идет голое сканирование строк в соответствии именно с кластерным индексом. И еще, опять же судя по результатам профайлера, почему-то при выполнении Statment наблюдается повышенный обмен данными с сервером. У меня получились такие данные (только собственно цикл While): Satment Вызовов от клиента к серверу 0 Байт, от клиента к серверу 571969 Вызовов от сервера к клиенту 21001 Байт, от сервера к клиенту 1040040 While Select Вызовов от клиента к серверу 0 Байт, от клиента к серверу 425451 Вызовов от сервера к клиенту 41 Байт, от сервера к клиенту 1435 Т.е. каким-то образом оптимизируется объем передаваемой информации. Единственная причина, которая приходит в голову - это информация о типах данных. О типах данных "родной" таблицы и так все известно, а вот о типах Satament - ничего не ясно. Хотя тут я не уверен. |
|
29.01.2004, 18:57 | #20 |
Модератор
|
Вообще-то я имел в виду профайлер от MSSQL, события
RPC:Completed SQL:BatchCompleted в нем все чудесно видно по поводу использования индексов: речь вроде шла о кэшировании, при чем тут индексы? По поводу траффика - ну да, по объему выигрыш около 25 процентов. Зато какая разница в количестве вызовов! Похоже, AOS и клиент тоже обмениваются пачками записей, а каждое обращение к Resultset, который живет на сервере - это лишний вызов, а на него тоже время требуется (и траффик). За счет этого и получается такой проигрыш По мне, так Connection если и нужен, то для того, чтобы выполнить в нем что-то короткое типа exec myproc, truncate mytable. Работать с ним как-то по-другому - только лишний головняк |
|