读取SAP CRM One Order应用日志的优化方式
Recently I am responsible for the performance optimization of one API which retrieves application logs of the given one order documents.
API Requirement
The input is an internal table containing document guids, and output are all the application logs belonging to those documents, with type “Error” and priority “Very important” and “Important”.
For the application logs of a single document, you can view them via tcode SLG1 as below:
The application logs would be displayed in WebUI as below:
The signature of API:
The structure of table type CRMT_ODATA_TASK_LOGST could be found below:
Original implementation ( has performance issue )
The idea of original implementation is:
LOOP AT it_order_guid_tab.
" get all of the message handles of the current order by its guid and stores to variable lt_message_handles
LOOP AT lt_message_handles.
" get the detail of each message according to its handle
ENDLOOP.
ENDLOOP.
As you see there are nested loop, so the algorithm complexity is o(n2).
The optimized implementation
The idea is to avoid the nested LOOP.
" get all message handles belonging to all orders in a single method call
lt_all_message_handlers = get_all( it_order_guid_tab ).
LOOP AT lt_all_message_handlers.
" get the detail of each message according to its handle
ENDLOOP.
The algorithm complexity is reduced to o(n). The original implementation could be found from method GET_ORDER_ERROR_MESSAGE of the class CL_CRM_ORDER_MESSAGE_TOOL in the attachment. The optimized version is in method GET_ORDER_ERROR_MESSAGE_OPT.
Performance comparison
I do the performance measurement based on the following three scenarios. We can see the performance is improved a lot after nested LOOP is removed.
The idea of original implementation is:
Unit Test
In order to guarantee that the optimized implementation does return exactly the same data as the original one, I write the following report to do unit test. The design is quite simple, retrieve the application log for the same input twice, one using the original implementation and the other one using the optimized version, and eASSERT lt_result1 = lt_result2. The report source code is attached here:
REPORT tool_display_log_compare.
CLASS lcl_test DEFINITION.
PUBLIC SECTION.
METHODS: test_all_oppt, test_created_by_jerry, test_oppt_jerry,
constructor.
PRIVATE SECTION.
METHODS: get_oppt_guid, compare, get_created_by, get_oppt_jerry.
DATA: mt_guid_tab TYPE crmt_object_guid_tab,
mt_msg_origin TYPE crmt_odata_task_logst,
mt_msg_opt LIKE mt_msg_origin,
mo_tool TYPE REF TO cl_crm_order_message_tool.
ENDCLASS.
CLASS lcl_test IMPLEMENTATION.
METHOD: test_oppt_jerry.
get_oppt_jerry( ).
compare( ).
WRITE: / 'lines of message: ' , lines( mt_msg_origin ).
WRITE: / 'test on all Opportunity with type OPPT and created by Jerry passed.' COLOR COL_NEGATIVE.
ENDMETHOD.
METHOD: test_created_by_jerry.
get_created_by( ).
compare( ).
WRITE: / 'lines of message: ' , lines( mt_msg_origin ).
WRITE: / 'test on all Opportunity created by Jerry passed.' COLOR COL_NEGATIVE.
ENDMETHOD.
METHOD: test_all_oppt.
get_oppt_guid( ).
compare( ).
WRITE: / 'lines of message: ' , lines( mt_msg_origin ).
WRITE: / 'test on all Opportunity with type OPPT passed.' COLOR COL_NEGATIVE.
ENDMETHOD.
METHOD: get_created_by.
CLEAR: mt_guid_tab.
SELECT guid INTO TABLE mt_guid_tab FROM crmd_orderadm_h WHERE created_by = 'WANGJER'.
ENDMETHOD.
METHOD: get_oppt_jerry.
CLEAR: mt_guid_tab.
SELECT guid INTO TABLE mt_guid_tab FROM crmd_orderadm_h WHERE process_type = 'OPPT' AND created_by = 'WANGJER'.
ENDMETHOD.
METHOD: get_oppt_guid.
CLEAR: mt_guid_tab.
SELECT guid INTO TABLE mt_guid_tab FROM crmd_orderadm_h WHERE process_type = 'OPPT'.
ENDMETHOD.
METHOD: compare.
CLEAR: mt_msg_origin, mt_msg_opt.
mt_msg_origin = mo_tool->get_order_error_message_opt( mt_guid_tab ).
CALL FUNCTION 'CRM_MESSAGES_INIT'
EXPORTING
it_docnumber = mt_guid_tab.
mt_msg_opt = mo_tool->get_order_error_message( mt_guid_tab ).
SORT mt_msg_origin BY header_guid log_msg.
SORT mt_msg_opt BY header_guid log_msg.
ASSERT mt_msg_origin = mt_msg_opt.
ENDMETHOD.
METHOD: constructor.
mo_tool = NEW cl_crm_order_message_tool( ).
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
DATA: lo_test TYPE REF TO lcl_test.
lo_test = new lcl_test( ).
lo_test->test_all_oppt( ).
lo_test->test_created_by_jerry( ).
lo_test->test_oppt_jerry( ).
- Angr:一个具有动态符号执行和静态分析的二进制分析工具
- Spark编程指南
- Spark Streaming编程指南
- Spark源码系列(八)Spark Streaming实例分析
- “震网三代”(CVE-2017-8464)的几种利用方法与防范
- Spark1.0新特性-->Spark SQL
- 挖洞经验 | 看我如何综合利用4个漏洞实现GitHub Enterprise 远程代码执行
- Spark的机器学习算法mlib的例子运行
- Spark Streaming自定义Receivers
- 利用USB橡皮鸭在目标机器上启动Empire或Meterpreter会话
- Spark部署
- Spark调优
- perl语言十分钟入门【零基础可入】
- Spark硬件配置推荐
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 翻车!居然在这里翻车了!!!
- PAT (Advanced Level) Practice 1029 Median (25分)
- ServiceMesh的关键:边车模式(sidecar);又要开车了
- Codeforces Round #615 (Div. 3)A. Collecting Coins
- PAT (Advanced Level) Practice 1040 Longest Symmetric String (25分)
- 【Java】23 函数式编程
- 【Java】24 常用函数式接口
- mvnw,到底是什么鬼?
- 【Java】25 Stream 流
- Codeforces Round #615 (Div. 3)D. MEX maximizing
- PAT (Advanced Level) Practice 1039 Course List for Student (25分)
- Codeforces Round #615 (Div. 3) E. Obtain a Permutation
- 数据结构实验完结撒花之内部排序比较
- Codeforces Round #615 (Div. 3) F. Three Paths on a Tree
- Matlab 内联函数及匿名函数