使用ABAP并发编程解决一个实际应用场景中的性能瓶颈问题
When I was responsible for CRM Fiori application, I once meet with a performance issue.
When the users perform the synchronization for the first time on their mobile device, the opportunities belonging to them will be downloaded to mobile which is so called the initial load phase. The downloaded data includes attachment header information.
Since for Attachment read in CRM, there is no multiple-enabled API, so we have to perform the single read API within the LOOP, which means the read is performed sequentially:
We really suffer from this poor performance. As explained in my blog What should an ABAPer continue to learn as an application developer,
I get inspiration from the concept Parallel computing which is usually related to functional programming language. A function which has no side-effect, only manipulates with immutable data set is a good candidate to be handled concurrently. When looking back on my performance issue, the requirement to read opportunity attachment header data perfectly fits the criteria: only read access on header data, each read is segregated from others – no side effect. As a result it is worth a try to rewrite the read implementation into a parallelism version.
The idea is simple: split the opportunities to be read into different parts, and spawn new ABAP sessions via keyword STARTING NEW TASK, each session is responsible for a dedicated part.
The screenshot below is an example that totally 100 opportunities are divided into 5 sub groups, which will be handled by 5 ABAP sessions, each session reads 100 / 5 = 20 opportunity attachments.
The parallel read version:
METHOD PARALLEL_READ.
DATA:lv_taskid TYPE c LENGTH 8,
lv_index TYPE c LENGTH 4,
lv_current_index TYPE int4,
lt_task LIKE it_orders,
lt_attachment TYPE crmt_odata_task_attachmentt.
* TODO: validation on iv_process_num and lines( it_orders )
DATA(lv_total) = lines( it_orders ).
DATA(lv_additional) = lv_total MOD iv_block_size.
DATA(lv_task_num) = lv_total DIV iv_block_size.
IF lv_additional <> 0.
lv_task_num = lv_task_num + 1.
ENDIF.
DO lv_task_num TIMES.
CLEAR: lt_task.
lv_current_index = 1 + iv_block_size * ( sy-index - 1 ).
DO iv_block_size TIMES.
READ TABLE it_orders ASSIGNING FIELD-SYMBOL(<task>) INDEX lv_current_index.
IF sy-subrc = 0.
APPEND INITIAL LINE TO lt_task ASSIGNING FIELD-SYMBOL(<cur_task>).
MOVE-CORRESPONDING <task> TO <cur_task>.
lv_current_index = lv_current_index + 1.
ELSE.
EXIT.
ENDIF.
ENDDO.
IF lt_task IS NOT INITIAL.
lv_index = sy-index.
lv_taskid = 'Task' && lv_index.
CALL FUNCTION 'ZJERRYGET_ATTACHMENTS'
STARTING NEW TASK lv_taskid
CALLING read_finished ON END OF TASK
EXPORTING
it_objects = lt_task.
ENDIF.
ENDDO.
WAIT UNTIL mv_finished = lv_task_num.
rt_attachments = mt_attachment_result.
ENDMETHOD.
The method READ_FINISHED:
METHOD READ_FINISHED.
DATA: lt_attachment TYPE crmt_odata_task_attachmentt.
ADD 1 TO mv_finished.
RECEIVE RESULTS FROM FUNCTION 'ZJERRYGET_ATTACHMENTS'
CHANGING
ct_attachments = lt_attachment
EXCEPTIONS
system_failure = 1
communication_failure = 2.
APPEND LINES OF lt_attachment TO mt_attachment_result.
ENDMETHOD.
In function module ZJERRYGET_ATTACHMENTS, I still use the attachment single read API:
FUNCTION ZJERRYGET_ATTACHMENTS.
*"----------------------------------------------------------------------
*"*"Local Interface:
*" IMPORTING
*" VALUE(IT_OBJECTS) TYPE CRMT_OBJECT_KEY_T
*" CHANGING
*" VALUE(CT_ATTACHMENTS) TYPE CRMT_ODATA_TASK_ATTACHMENTT
*"----------------------------------------------------------------------
DATA(lo_tool) = new zcl_crm_attachment_tool( ).
ct_attachments = lo_tool->get_attachments_origin( it_objects ).
ENDFUNCTION.
```ABAP
So in fact I didn’t spend any effort to optimize the single read API. Instead, I call it in parallel. Let’s see if there is any performance improvement.
In this test report, first I generate an internal table with 100 entries which are opportunity guids. Then I perform the attachment read twice, one done in parallel and the other done sequentially. Both result are compared in method compare_read_result to ensure there is no function loss in the parallel version.
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy8yMDg1NzkxLWExYzU4NjUyMmUyMTFiMjgucG5n?x-oss-process=image/format,png)
# Testing result ( unit: second )
It clearly shows that the performance increases with the number of running ABAP sessions which handles with the attachment read. When the block size = 100, the parallel solution degrades to the sequential one – even worse due to the overhead of WAIT.
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy8yMDg1NzkxLWU1ZWY3MzNjOGM4NzcxYzEucG5n?x-oss-process=image/format,png)
For sure in productive usage the number of block size should not be hard coded.
In fact in my test code why I use the variable name iv_block_size is to express my respect to the block size customizing in tcode R3AC1 in CRM middleware.
- 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 数组属性和方法
- tensorflow之读取jpg图像长和宽实例
- Python数据可视化实现多种图例代码详解
- Python使用tkinter实现摇骰子小游戏功能的代码
- pandas to_excel 添加颜色操作
- Python自带的IDE在哪里
- php+Ajax无刷新验证用户名操作实例详解
- PHP经典设计模式之依赖注入定义与用法详解
- 浅谈python出错时traceback的解读
- PHP判断是否是微信打开还是浏览器打开的方法
- php面向对象程序设计中self与static的区别分析
- PHP如何根据文件头检测文件类型实例代码
- PHP去除空数组且数组键名重置的讲解
- Yii2框架配置文件(Application属性)与调试技巧实例分析
- java解析json方法总结
- PHP正则验证字符串是否为数字的两种方法并附常用正则