Abap/4 vs C++
В статье автор сравнивает производительность программ, написанных на языках Abap/4 и C++, на примере алгоритма формирования расшифровки строк баланса по основным средствам.
Введение
В гугле можно найти много статей – противопоставлений: «Python vs C++», «Ruby vs Python», «Java vs C#» и т.д. В них языки, кроме своих выразительных способностей, сравниваются по скорости работы программ, написанных на этих языках. Но статей «ABAP vs …» я не нашёл. Хотелось бы узнать: насколько скорость работы программы на проприетарном языке программирования (ABAP), который используется только в SAP R/3, отличается от скорости работы программы, написанной на другим языке программирования, который используется не только в SAP R/3.
По скорости работы все языки сравниваются с C/C++. Так как последняя парочка обладает самыми большими способностями по написанию скоростных программ и хранится в парижской палате мер и весов.
Я не стал измерять скорость по базовым алгоритмам типа «а+б», quicksort или обходы по графу. Такие синтетические тесты может быть и дают некоторое понятие о порядке, но не надо забывать, что одна и та же функциональная часть программы может быть записана по разному в зависимости от способностей языка. Поэтому применяется реальный тест. Я взял главный алгоритм программы для формирования расшифровки строк баланса по основным средствам.
На abap его кодировал опытный разработчик.
Разработчика на С++ у меня не было, поэтому эту часть выполнял сам.
Результат теста
ABAP: ~40 секунд
C++: 0.15 секунды.
Анализ причин
Различие в скорости работы объясняется тем, что в С++ нет некоторых конструкций. Например, нет «loop at table where key = “x”». А в abap есть. С учётом этого abapером было написано:
Loop at lt_rowsettings into ls_rowsets.
Loop at lt_obj into ls_obj where type = ls_rowsets-type.
append initial line to <out> assigning <ls_out>.
assign component “rownum” of structure <ls_out> to <ls_out_field>.
<ls_out_field> = ls_rowsets-rownum.
Loop at lt_transactions into ls_trans where objnum = ls_obj-objnum.
Loop at lt_columnsettings into ls_colsets where type = ls_trans-type.
Assign component ls_colsets-colname of structure <ls_out> to <ls_out_field>.
Add lt_trans-sum to <ls_out_field>.
Endloop.
Endloop.
Endloop.
Endloop.
Далее шёл цикл, который делал «подсуммировку» сумм по колонкам (в структуре <ls_out> эти поля имеют имя colname) по полю rownum.
Но для решения задачи на С++ надо применять другие подходы. Дальше я буду рассматривать примеры преобразования реализации алгоритма на ABAP к реализации алгоритма на C++.
Пример 1.
В SAP есть некоторая таблица настроек, в которой мы задаём соответствие хозяйственной операции множеству кодов видов движений. В отображаемой alv-таблице суммы по каждой хозяйственной операции располагаются в своём столбце. По сути (но не по реализации) настроечная таблица (Таблица 1) выглядит так:
Таблица 1
Название столбца хоз. операции Columnsettings-colname |
Название хоз. операции |
Код хоз. операции в системе Columnsettings-type |
SUM1 |
Приход |
100 |
SUM1 |
Приход |
Z01 |
SUM2 |
Расход |
200 |
SUM2 |
Расход |
Z02 |
В программе реализуется цикл по таблице объектов lt_obj , и для каждого объекта мы находим перечень операций. В зависимости от кода операции добавляем сумму операции в нужную колонку выходной таблицы lt_out. Это выглядит так:
Loop at lt_obj into ls_obj.
Loop at lt_transactions into ls_trans where objnum = ls_obj-objnum.
Loop at lt_columnsettings into ls_colsets where type = ls_trans-type.
Assign component ls_colsets-colname of structure <ls_out> to <ls_out_field>.
Add lt_trans-sum to <ls_out_field>.
Endloop.
Endloop.
Endloop.
Разумеется, это можно оптимизировать: заменим assign component по имени на assign по номеру. Но проблему это не решит. Проблема здесь – тройной цикл.
Делаем тот же расчёт на С++. Для начала определим, что входную таблицу (таблица 1) удобно преобразовать к виду, приведённому в Таблице 2:
Таблица 2
Код хоз. операции Columnsettings-type |
Номер столбца хоз. операции Columnsettings-colnum |
100 |
1 |
Z01 |
1 |
200 |
2 |
Z02 |
2 |
Далее преобразуем тип поля Columnsettings-type. В sap это NUMC 3 знака. Т.е. кроме цифр 100 и 200 можно задать и цифробуквенное сочетание. Например, значение Z01. Вводим 36 разрядную систему измерений, где 0 = 0, 1 = 1… A = 10, B=11…Z = 35. В результате, Z01 будет иметь значение 35*36*36 + 0* 36 + 1 = 45 361. Пишем функцию int typconv(const char*). Текст приводить не буду. Она небольшая и разумеется её выгодно сделать inline.
Максимально число видов движений 36^3 = 46 656.
Создаём массив нужного размера:
typedef unsigned int uint;
std::vector<uint> typecol;
typecol.reserve(46656);
Заполняем его значениями:
for(const auto &ls: columnsettings){
typecol[typconv(ls.type)] = ls.colnum;
}
Теперь, когда в нужную колонку требуется добавить сумму, достаточно написать следующую конструкцию:
ls_out[row][typecol[typconv(lt_trans.type)]] += lt_trans.sum;
Никаких binary search - все на порядки быстрее. Фактически, мы заменили внутренний цикл на конструкцию вида:
typeconv_outval = macros_typconv(lt_trans-type).
read table typecol into ls_typecol index typeconv_outval.
assign component ls_typecol of structure <ls_out> to <ls_out_field>.
add lt_trans-sum to <ls_out_field>.
Пример 2.
Изначально, речь шла про 4х-кратно вложенный цикл. Перед выводом поля структур с суммами в <lt_out> мы «подсуммируем» по номерам строки rownum. Над тройным циклом, описанным в прошлом примере, располагается ещё один, который служит для определения номера строки в alv-таблицу:
Loop at lt_rowsettings into ls_rowsets.
Assign component “rownum” of structure <ls_out> to <ls_out_field>.
<ls_out_field> = lt_rowsetttings-rownum.
Есть настройка для соответствия класса объекта и номером строки. Настроечная таблица (Таблица 3) выглядит так:
Таблица 3
Номер строки rowsettings-rownum |
Название строки |
Класс rowsettings-type |
1 |
Здания |
Если хотите прочитать статью полностью и оставить свои комментарии присоединяйтесь к sapland
ЗарегистрироватьсяУ вас уже есть учетная запись?
Войти
Обсуждения 13
Комментарий от
Павел Телепко
| 08 июня 2014, 16:12
help.sap.com/saphelp_nw04/helpdata
Комментарий от
Олег Точенюк
| 09 июня 2014, 23:07
Комментарий от
Юрий Сычов
| 10 июня 2014, 08:50
ABAP задача выполнялась на сервере (возможно еще чем-то нагруженном), который брал данные из реальной СУБД ( Oracle, например). Такие вещи не сэмулируешь и не сравнишь.
Хотя бы время работы с СУБД исключите с помощью тр. SE30.
Да, Assembler и C оказались быстрее SAP. Как эта информация поможет практикующим Саперам?
Комментарий от
Николай Кронский
| 10 июня 2014, 10:04
Олег Точенюк 09 июня 2014, 23:07
Роман, можно личный вопрос? Вы по образованию надеюсь не программист? А то если таки программист, то ваше чудное сравнение теплого с мягким разрушит последние надежды на то, что и как преподают текущим программистам.
Роман, как сказано в предисловии, сравнений "АВАР vs ..." не нашел, но, мне кажется, сделал неверный вывод, что это упущение...
Комментарий от
Степан Лаврентьев
| 10 июня 2014, 13:50
"В sap это NUMC 3 знака. Т.е. кроме цифр 100 и 200 можно задать и цифробуквенное сочетание. Например, значение Z01."
Либо я не понял глобальной мысли, либо написан бред. Тип n в SAP призван для хранения чисел в char-овском виде. То есть, с ведущими нулями (прим.: "001"). Никаких букв в такое поле не засунешь и значение типа "Z01" просто преобразуется, в поле такого типа, в "001".
Комментарий от
Степан Лаврентьев
| 10 июня 2014, 13:59
А для чистоты эксперимента, осталось реализовать интерфйс интергацию со смежными модулями, сами модули реализовать на C++. Так же, на отдельном сервере запустить процесс, сравнить время обработки данных, защитить докторскую и продавать ваш продукт как альтернативу SAP-у. Только, к моменту запуска продукта в серию, законодательство притерпит существенные изменения и ваш продукт никому уже не понадобится.
А текущие сравшения - очень уж притянуты за уши.
Комментарий от
Олег Точенюк
| 10 июня 2014, 14:25
Николай Кронский 10 июня 2014, 10:04
Тут, скорее не "теплое и мягкое" а, например, "часовая отвертка и газовый ключ" (можно что-то откручивать), "воробей и горный орел" (оба летают) :)
Роман, как сказано в предисловии, сравнений "АВАР vs ..." не нашел, но, мне кажется, сделал неверный вывод, что это упущение...
Комментарий от
Роман Шахлович
| 21 июня 2014, 16:21
Павел Телепко 08 июня 2014, 16:12
Добрый день, прочитал только заключение, и решил заметить, что выборку и обработку данных с помощью Abap также можно легко распараллелить...например так:
help.sap.com/saphelp_nw04/helpdata
Комментарий от
Роман Шахлович
| 21 июня 2014, 16:40
Юрий Сычов 10 июня 2014, 08:50
"Замер времени выполнения проводился на разных машинах, так как возможность разработки на С++ у меня есть только дома."
ABAP задача выполнялась на сервере (возможно еще чем-то нагруженном), который брал данные из реальной СУБД ( Oracle, например). Такие вещи не сэмулируешь и не сравнишь.
Хотя бы время работы с СУБД исключите с помощью тр. SE30.
Да, Assembler и C оказались быстрее SAP. Как эта информация поможет практикующим Саперам?
Как информация поможет практикующим саперам? Очень просто - есть задача: очень медленную программу надо сделать очень быстрой. Берет критический участок кода и пишете его на ассемблере (с++, c# и т.д.) и компилируете. Вместо этого куска кода в абап-программе запускаете полученный эзешник, в который передаете данные для обработки (файлами, через rfc или другой вариант). Получаете результаты и выводите в alv. Profit.
Это не очень другозатратно. Зная исходный алгоритм я переписал его за неделю.
Надо понимать, что этот способ работает если время выполнения формируется преимущественно из-за сложности вычисления, а не объемов данных.
Комментарий от
Роман Шахлович
| 21 июня 2014, 16:42
Степан Лаврентьев 10 июня 2014, 13:50
Роман, вы пишете:
"В sap это NUMC 3 знака. Т.е. кроме цифр 100 и 200 можно задать и цифробуквенное сочетание. Например, значение Z01."
Либо я не понял глобальной мысли, либо написан бред. Тип n в SAP призван для хранения чисел в char-овском виде. То есть, с ведущими нулями (прим.: "001"). Никаких букв в такое поле не засунешь и значение типа "Z01" просто преобразуется, в поле такого типа, в "001".
Комментарий от
Валерий Заузолков
| 29 января 2015, 15:23
А как fun вполне интересно ... :)
П.С.: Единственно, что продолжительность "неделя" и характеристика "не очень трудозатратно" у меня не вяжутся. %)
Комментарий от
Аркадий Семенов
| 06 марта 2015, 00:27
И тут конечно возникает вопрос, почему это стоит таких денег. Все это похоже на эпоху колонизации, когда попуасы меняли на бусы свои острова.
При этом мы все вроде бы не дураки и программировать начинали на C++...
Комментарий от
Виталий Глущенко
| 27 октября 2015, 01:37
Роман Шахлович 21 июня 2014, 16:40
Время работы СУБД исключено. В заключении есть небольшой абзац относительно работы СУБД. Задача не выполнялась на загруженном сервере, потому что замеры делал в разные времена суток - цифры одинаковые.
Как информация поможет практикующим саперам? Очень просто - есть задача: очень медленную программу надо сделать очень быстрой. Берет критический участок кода и пишете его на ассемблере (с++, c# и т.д.) и компилируете. Вместо этого куска кода в абап-программе запускаете полученный эзешник, в который передаете данные для обработки (файлами, через rfc или другой вариант). Получаете результаты и выводите в alv. Profit.
Это не очень другозатратно. Зная исходный алгоритм я переписал его за неделю.
Надо понимать, что этот способ работает если время выполнения формируется преимущественно из-за сложности вычисления, а не объемов данных.
Надеюсь вы понимаете, что реализуя часть функционала на языке типичном для SAP, то для решения проблемы с этим функционалом возможно вам прийдется взять 2-х специалистов, одного, который знаком с SAP'ом и одного, который знаком с этим другим языком(c++, c# и т.д.)?