一
问题
之前写了一个接口程序: SAP中的数据获取后,根据不同的层级结构,拼接到一个TXT文件中(有点像IDOC的文件输出). 业务反馈存在性能问题. 大概需要三个小时才能完成数据的读取及文件的写入.
本文主要介绍字符串连接的性能优化方式
二
ST12
推荐使用事务代码ST12做程序的性能分析.这个工具会把所有的执行步骤,模块详细记录一个执行时间. 可以很快定位性能最差的模块及语句
填写要分析的程序/事务,点击 execute / start trace 按钮
执行后系统记录的分析结果
选中分析结果,点击ABAP trace按钮,按net time in 排序,可以看到耗时最大的模块,快速定位存在性能的代码.
三
问题分析
通过ST12分析结果. 发现在嵌套循环的一个子循环耗时较长. 于是尝试改变字符串关联的方式. 从方式1 调整为方式2.
内层循环直接拼接到最终字符串
内存循环先拼接到子串, 外层循环把子串拼接到最终字符串
01
方式1
内层循环直接拼接到最终字符串
LOOT AT ITAB.
LOOP AT ITAB1
GV_STR = GV_STR && 'A'.
GV_STR = GV_STR && 'B'.
ENDLOOP .
ENDLOOP.
02
方式2
内存循环先拼接到子串, 外层循环把子串拼接到最终字符串
LOOP AT ITAB2.
CLEAR LV_STR.
LOOP AT ITAB1
LV_STR = LV_STR && 'A'.
LV_STR = LV_STR && 'B'.
ENDLOOP .
GV_STR = GV_STR && LV_STR.
ENDLOOP.
通过上述方式2 优化程序后,对比二者的性能差异. 发现方式2性能好于方式1
尤其是最终的字符串非常大的情况下. 上述优化方案将极大的增加性能
如下图, 性能优化提升4倍多.
如果文件很大, 性能差异更大
四
两种拼接命令
concatenate lv_str 'NEW STRING' INTO lv_str
lv_str = lv_str && 'NEW STRING'
实际测试concatenate方式性能差于操作符 && . 但concatenate 命令可以在连接的字符串中添加空格或其它字符. 操作符&&做不到这一点.
五
原因猜测
直接在最终字符串上拼接所有的变量,当最终字符串较大或较复杂时. 拼接命令可能会导致系统循环整个字符串到字符串的最后一个字符位置. 方式1会导致不断循环这个较大的字符串.
而使用嵌套循环中的子串先拼接,再拼接到最终的字符串中. 这样就会减少较大的字符串本身的循环.
该猜想无法验证. 仅仅是根据两种方式的巨大时间差异来猜想的.
六
验证猜测
使用下面的测试代码验证. 发现二者性能差异不大. 并且依赖于外层循环与内层循环的个数.
非常奇怪这个验证结论和优化程序的结论不一致. 可能实际程序的拼接逻辑远比示例程序的拼接逻辑复杂或者有特殊字符的参与(换行符号)触发了猜想的字符串本身的循环,导致了这二者结论的差异.
*&---------------------------------------------------------------------*
*& Report ZTS_STRING_CONN
*&---------------------------------------------------------------------*
*&
*&---------------------------------------------------------------------*
REPORT ZTS_STRING_CONN.
PARAMETERS: P_TIMES TYPE I DEFAULT 1000,
P_TIME2 TYPE I DEFAULT 1000.
PARAMETERS: P_CON AS CHECKBOX.
DATA: LV_STR TYPE STRING.
START-OF-SELECTION.
CL_DEMO_OUTPUT=>BEGIN_SECTION( '直接连接' ).
GET RUN TIME FIELD DATA(T1).
DO P_TIMES TIMES.
DO P_TIME2 TIMES.
IF P_CON = ''.
LV_STR = LV_STR && 'JUST_TEST_STRING_JOIN' && CL_ABAP_CHAR_UTILITIES=>NEWLINE.
ELSE.
CONCATENATE LV_STR 'JUST_TEST_STRING_JOIN' INTO LV_STR.
ENDIF.
ENDDO.
ENDDO.
GET RUN TIME FIELD DATA(T2).
CL_DEMO_OUTPUT=>WRITE( STRLEN( LV_STR ) ).
CL_DEMO_OUTPUT=>WRITE( T2 - T1 ).
CL_DEMO_OUTPUT=>BEGIN_SECTION( '借用子串连接' ).
DATA: LV_SUBSTR TYPE STRING.
CLEAR LV_STR.
GET RUN TIME FIELD DATA(T3).
DO P_TIMES TIMES.
CLEAR LV_SUBSTR.
DO P_TIME2 TIMES.
IF P_CON = ''.
LV_SUBSTR = LV_SUBSTR && 'JUST_TEST_STRING_JOIN' && CL_ABAP_CHAR_UTILITIES=>NEWLINE..
ELSE.
CONCATENATE LV_SUBSTR 'JUST_TEST_STRING_JOIN' INTO LV_SUBSTR.
ENDIF.
ENDDO.
LV_STR = LV_STR && LV_SUBSTR.
ENDDO.
GET RUN TIME FIELD DATA(T4).
CL_DEMO_OUTPUT=>WRITE( STRLEN( LV_STR ) ).
CL_DEMO_OUTPUT=>WRITE( T4 - T3 ).
CL_DEMO_OUTPUT=>DISPLAY( ).
七
总结
但总体上讲, 如果排除了其它性能问题,并且直接拼接到最终字符串导致性能问题, 则可以尝试在嵌套循环(子循环)中先拼接子串, 在主循环中再把子串拼接到最终的字符串.
实测这个性能差异可能是数倍到数百倍.
THE
END
约定
如果你对这篇文章感兴趣,请帮忙点赞,在看,分享.
请微信联系管理员:
syjf1976
sharry_xlp
Yannick_Duan
申请进入公众号讨论群提问或者参与话题讨论