Меню

Практические рекомендации по оптимизации программ на ABAP

|

В статье изложены практические принципы по оптимизации работы программ на ABAP, приведено сравнения быстродействия вариантов программ.

Практические рекомендации по оптимизации программ на ABAP

Практические рекомендации по оптимизации программ на ABAP


 

Точенюк Олег занимал должности консультанта по SAP ММ в различных компаниях, с 1997 года, занимался внедрением модулей FI-FM (контроль исполнения бюджета), Управления материальными потоками (ММ), Управление складом СУС (WMS), был консультантом по интеграции MM<->ТОРО, занимался разработками расширений системы на языке ABAP. Связаться со мной можно по адресу uukrul@hotmail.com

 

В статье изложены практические принципы по оптимизации работы программ на ABAP, приведено сравнения быстродействия вариантов программ.

Категории: Язык ABAP

«Ну и запросы у вас, сказала база данных и повисла».

(Саперская мудрость)

Как поется в песне: «Не прожить на этом свете» без ABAP; приходится писать отчеты/обработки/экзиты, которые долго «гудят», «съедают» память, загружают процессор, и в результате мы имеем недовольных пользователей и медленную систему. Много есть консультантов - функциональщиков, которые писать на ABAP ничего не собираются, этот их выбор мы уважаем (хотя и не одобряем). Настоящая статья предназначена как раз для тех, кто пишет или хочет научиться писать на ABAP правильно. Итак, поговорим об оптимизации или как писать «быстрые» программы. Но учтите: иногда достаточно одной, не совсем правильно написанной, проверки в экзите, чтобы «тормознуть» работу системы в разы.

Некоторые вопросы оптимизации очень специфичны, и если у меня в тесте, что-то работает быстрее, это не значит, что этот эффект повторится у вас. Но большинство операций описанных ниже, будут корректными в общем случае (для всех систем).

В нашем случае под оптимизацией будем понимать написание оптимального по исполнению кода на языке ABAP, а не оптимизацию способа выбора данных из таблиц системы. Конечно же, второй из указанных, тип оптимизации зачастую будет быстрее, но это при наличии следующего условия: вы знаете, в какой таблице находятся нужные вам данные, в требуемом разрезе. Выбрать их из этой таблицы будет быстрее, чем найти, обработав десяток таблиц. Но это, как говорится, другая история; для каждой функциональности она имеет свои особенности.

Часть информации, содержащейся в тексте, приведенном ниже, есть в справке к системе но тем не менее смысл всего изложенного и опыт ивместе добыт «кровью и железом», поэтому ценен.

Для понимания текста, желательны хотя бы минимальные знания языка ABAP, хотя бы в рамках обзора книги "Разработка приложений SAP R/3" авторы Рюдигер Кречмер и Вольфганг Вейс.

1. Выбор данных для обработки во внутренние таблицы.

Время диалоговых процессов в системе, как известно, ограничено. Стандартно диалоговых процесс в системе может длиться не более 600 секунд и дальше ваша задача будет завершена принудительно, правда на практике, администраторы системы это время обычно увеличивают, но и мы можем тоже, кое-что «докрутить» в своих запросах, чтобы они работали чуть быстрее. Итак, есть таблица MKPF — заголовки документов материала. Вариантов выбора и обработки значений – два; первый: в цикле оператора SELECT.- ENDSELECT., второй: выбор во внутреннюю таблицу и затем обработка данных в цикле оператора LOOP AT <внутренняя таблица>. ENDLOOP. Кроме того, для этих операторов есть различные варианты вызова.

Для тестирования будет использоваться внутренняя таблица.

Варианты выборки данных.

Если структура, куда выбирают данные, совпадает со структурой из выбираемой таблицы, то можно выбрать данные наиболее быстрым способом:

При реальных запросах, не все поля находящиеся в таблице MKPF, нужны во внутренней таблице LT_MKPF, тогда такую структуру надо объявить с перечислением только используемых полей. Если это сделано так, то нужно использовать расширение операции INTO в виде «INTO CORRESPONDING FIELDS OF» т. е., система сама разложит результат, сделав правильное соответствие выбираемых полей из таблицы, полям в структуре. При этом если выбираются все поля, то время выполнения различается в пределах 0,1%, но при этом всегда время выполнения первого запроса меньше чем второго.

Как видим, разница в быстродействии практически отсутствует, но если выжимаем максимум, то запрос A1 предпочтительнее, опять же, «копеечка рубль бережет».

Как уже отмечалось выше, более предпочтительнее выглядит данный запрос, если нам нужны не все поля из таблицы, например, хотим выбрать следующие поля: MBLNR — номер документа, MJAHR — год документа, XBLNR — ссылочный номер, BKTXT — краткий текст заголовка документа. Тут уже время отличается и существенно.

Как видим скорость выборки стала в среднем в 4 раза быстрее. Однако если сделать следующий вариант вызова, когда внутренняя таблица содержит только необходимый набор полей, то, как видим, операция «INTO TABLE» будет быстрее, в среднем от 15% до 20%.

На скорость выборки, в случае перечисления выбираемых полей, влияет также порядок их следования. Если порядок следования полей внутренней таблицы такой же, как и в таблице базы данных, то скорость выбора будет быстрее, если вы выбираете все поля. Например, требуется выбрать все поля из таблицы MKPF.

Как видим перечисление всех полей с оператором «CORRESPONDING FIELDS», работает чуть быстрее, чем операция «INTO TABLE», разница не существенна, но опять же если выжимать максимум, то следует задуматься. Однако если вы вдруг перепутаете порядок следования полей, то получите вместо ускорения запроса, наоборот его замедление.

На разных выборках время выполнения запроса H1, фактически стало равно времени выполнения запроса G1.

Описанные выше комбинации работают быстро, однако грузят все данные в память - ресурс, который, может закончиться, причём в самый неподходящий момент. Поэтому иногда приходится жертвовать производительностью, но при этом оптимизировать использование памяти. Для этого данные обрабатываются по ходу их считывания в конструкции SELECT <>. ENDSELECT.

Как видим время выполнения данного запроса больше (у меня эти колебания были в среднем от 10% до 15%). Но что касается экономии памяти, такая конструкция - вне конкуренции. Несомненно, время выполнения будет быстрее, если перечислить в запросе только поля, которые нам нужны.

Как видим, скорость выполнения запроса J1 в среднем в три раза выше, чем скорость выполнения запросов I1. По поводу перечисления полей и порядка их следования, правила такие же, как и для запросов F1 и H1, порядок следования полей важен.

Выводы по скорости: Наиболее быстрым будет вариант выбора данных во внутреннюю таблицу с использованием запроса вида E1, при этом внутренняя таблица должна содержать только те поля, которые нам необходимы для работы. Желательно чтобы порядок следования полей, совпадал с порядком следования полей из выбираемой таблицы.

Выводы по памяти: Если у вас хватает памяти, то конструкция выбора всех данных в память с последующей обработкой будет предпочтительнее, чем их построчная обработка. Для оптимизации использования памяти, выбирайте и определяйте в структурах только те поля, которые вам нужны для обработки. Из практики, мне только раз встретилась система, где не то чтобы памяти было мало, но данных было очень много, поэтому пришлось оптимизировать память, жертвуя скоростью выполнения запроса.

2. Использование агрегатных функций.

Агрегатные функции - это использование таких конструкций как SUM, COUNT, MAX и т. д. в запросах. Сложно однозначно предсказать, что будет быстрее: выбрать данные во внутреннюю таблицу и посчитать все самому или же попросить, чтобы все посчитала база данных. Также, наблюдается зависимость работы данных функции от типа базы данных, на которой работает система. Данные тесты проводились для базы данных DB6, Рис.1. Большая вероятность, что для других баз данных результаты будут отличными, от полученных в данном описании.

 
Рис. 1 Database

В общем виде ситуация следующая, есть запрос вида

Мы считаем, сколько штук всего было оприходовано по всем документам, при этом я убрал автоматически добавляемые позиции из запроса. Сделаны запросы следующего вида:

В среднем время выполнения второго запроса B2, оказывается быстрее, чем при использовании агрегатной функции, хотя приходится написать «больше» кода и выглядит он «не так красиво» как в запросе A2. В среднем после более чем десяти прогонов, время выполнения запроса B2 было быстрее от 7% до 15%. Итак в среднем, выигрыш в скорости работы составляет около 10%

Примечание: Об оптимизации циклов по внутренним таблицам, будет рассказано ниже. Использование оптимизации такого рода даст дополнительный прирост производительности для запроса B2.

С функцией COUNT результат еще интереснее

По факту, скорость выполнения запроса D2 меньше, чем выполнения C2. При этом после 50 прогонов в двух случаях зафиксировано, что запрос скорость запроса D2 была почти равна скорости запроса С2. Среднее значение ускорения скорости выполнения запроса в процентах, по сумме прогонов получилось в районе от 10% до 15%.

Функции же MIN и MAX показывают обратный результат, т. е. запрос к базе отрабатывает быстрее, чем локальная обработка.

Однако, если в запрос E2 добавить сортировку, используя конструкцию ORDER BY, (т. е. исключаем локальную сортировку после запроса), как в примере запроса G2, то скорость практически была такой же, как и запроса F2. Индексов на поле, по которому выполнялась сортировка, не было. В целом, разница по скорости выполнения для запросов E2 и F2, колебалась в районе от 3% до 5%.

Примечание: При проверке работы агрегатных функций для базы данных Oracle 9, по таблицам COSS/COCP/COEP, функция суммирования работала быстрее, чем локальная выгрузка во внутренние таблицы с последующей обработкой в цикле по внутренней таблице. При проверке суммирования, GROUP BY был по индексу.

Выводы по скорости: Так как работа агрегированных функций очень специфична в зависимости от используемой базы данных, то сделайте тестирование работы этих функций на своей системе, чтобы определить наиболее быстрый вариант. Для текущей используемой системы рекомендации оказались следующими, для функций COUNT и SUM по возможности нужно было использовать локальную обработку, с чтением данных во внутренние таблицы и последующим расчетом, после запросов, а для функций MIN и MAX, наоборот, лучше оказалось выполнять обработку, используя их.

Выводы по памяти: С точки зрения использования памяти, всегда наиболее оптимальным будет вариант использования агрегированных функций, так как в этом случае все расчеты идут на уровне запроса к базе данных.

3. Ограничение выборки в условиях WHERE.

Стандартная рекомендация: если вы можете ограничить выборку по каким-то полям, то сделайте это. Скорость обработки, будет выше, чем проход по всей таблице, без каких либо ограничений, с последующим выбором нужных данных из внутренней таблицы. Как минимум, выигрыш будет на количестве пересылаемых данных от сервера к клиенту, в том случае, когда по полям ограничений в условии WHERE нет никаких индексов, и выполняется полный проход по всем записям таблицы. Конечно, более логичным будет при формировании условия отбора обратить внимание на наличие индексов к таблице. После чего сформировать запрос так, чтобы оптимизатор не ломал голову, какой индекс использовать, т. е. при перечислении полей в условии WHERE, используйте порядок такой же, какой существует в подходящем индексе. В общем случае данное утверждение спорно: результат зависит от типа используемой базы данных и статистики индекса, если подходящих индексов несколько. Из классики:

Как видим, запросы с ограничением условия в WHERE, работают в разы быстрее. Кстати, не забываем при использовании довеска «FOR ALL ENTRIES IN» делать проверку на наличие данных в таблице, используемой в условии. Потому что, если данных там нет, то будет выполнен полный проход по таблице выборки, а это плачевно отражается на скорости выбора данных, даже если вам нужно получить все записи.

Как видим, простой проход по таблице выполняется быстрее, чем тот же самый проход, но с пустой таблицей в условии FOR ALL ENTRIES. Поэтому отслеживайте, чтобы таблица в таком случае не была пустой. Если в ходе работы программы у вас может возникать такая ситуация, то более правильным будет сделать два запроса в программе, в зависимости от заполнениия таблицы lt_meins.

При задании ограничения по полям очень важно, чтобы в системе были подходящие индексы, иначе при запросе система будет выполнять полный проход по таблице. Наличие индексов к таблицам можно посмотреть в транзакции SE11. В режиме просмотра данных таблицы, на панели инструментов есть кнопка «Индексы», Рис.2.

 
Рис. 2 SE11-4

Кроме показанных в списке индексов, где первая колонка это имя индекса, всегда существует еще так называемый индекс первичного ключа с именем «0», в этот индекс входят все поля, которые отмечены в таблице как ключевые. В данном случае, для таблицы MKPF, существует только один дополнительный индекс, построенный по дате проводки документов материала. Для просмотра данных индекса сделайте двойной клик в строке.

Во время построения запроса, оптимизатор базы данных, анализирует поля в условии WHERE и старается найти подходящий индекс, если такой не найден, то будет выполнен полный перебор записей таблицы, что конечно же катастрофически скажется на скорости выбора данных. Для большинства баз данных, порядок перечисления полей в условии WHERE не важен, т.е. если есть индекс по полям 1 и 2, а в ограничении порядок полей стоит 2 и 1, то оптимизатор «теоретически» найдет правильный индекс и будет его применять. Для базы DB2 это правило работает. Так же оптимизатор старается подобрать индекс, если есть не полная совместимость по используемым полям, т.е. к примеру, индекс содержит только некоторые из полей WHERE или же, наоборот, в индексе содержится больше полей, чем в запросе. При наличии нескольких подходящих индексов, система будет использовать тот индекс, у которого лучшая статистика. Статистику индекса умеют собирать системные администраторы базы данных. В общем виде статистика, в терминах системы «Estimated Costs» есть число, значение которого рассчитывается в зависимости от величины различных (неодинаковых) записей в таблице (чем оно меньше, тем более лучшим считается индекс).

По возможности, следует избегать оператора NOT в условиях, так как при использовании такой конструкции поля определенные через NOT не обрабатываются как пригодные для индекса. Поэтому нужно стараться избегать операции отрицания. Это относится и к операциям не равно – «<>», такие поля также пропускаются индексом. Для приведенного ниже примера использовалась таблица MSEG с анализом использования индексов в транзакции ST05. В системе создан завод U100 и для этого завода созданы два склада с кодами 0001 и 0002. Простым запросом выбираем документы, проведенные по одному или другому складу. Например, выберем документы, проведенные по складу 0001.

Как видим простое указание, выбирать документы, где завод не равен 0002, т.е. только документы с заводом 0001, приводит к значительному увеличению времени работы запроса. Поверьте что завода там действительно только два. Причина увеличения времени кроется в выборе индексов для запросов E3 и F3. Для запроса E3 система использовала созданный индекс по заводу

Если хотите прочитать статью полностью и оставить свои комментарии присоединяйтесь к sapland

У вас уже есть учетная запись?

Войти