Application Development Blog Posts
Learn and share on deeper, cross technology development topics such as integration and connectivity, automation, cloud extensibility, developing at scale, and security.
cancel
Showing results for 
Search instead for 
Did you mean: 
horst_keller
Product and Topic Expert
Product and Topic Expert

It is a common programming pattern to fill a character string in iterative steps, either using the concatenation operator && or character string templates |...|:

DO|WHILE|LOOP|SELECT ...

  str = str && ...

  str = |{ str }...|.

ENDDO|END|ENDWHILE|ENDSELECT ...

Bad News

Both assignments have a string expression as an RHS (right hand side) . What does that mean? As a rule, when assigning an expression, a temporary or intermediate result is created that must be copied to the LHS (left hand side). Since the length of the intermediate result increases with each loop pass and there is a copy operation for each loop pass, the runtime dependency from the number of loop passes is quadratic. Not good.

Good News

Since this is a well known fact, there is an internal optimization for all concatenations that look as follows:

str = str && dobj1 && dobj2 && ... .

str = |{ str }...{ dobj1 [format_options] }...{ dobj2 [format_options] }...|.

As long

  • as the target string str occurs at the RHS only once and as the leftmost operator,
  • and as long as there are no formatting options for str ,
  • and as long as there are no other expressions or function calls involved in the RHS

no intermediate result is created but the characters are concatenated directly to the target string str. This prevents the quadratic dependency of the runtime from the number of iterations. The same is true for the CONCATENATE statement.


With other words, no problems in writing something like this:


DATA(html) = `<html><body><table border=1>`.

DO ... TIMES.

  html = |{ html }<tr><td>{ sy-index }</td></tr>|.

ENDDO.

html = html && `</table></body></html>`.


There is a simple data object sy-index concatenated to a target string html and the optimization takes place.


Bad News


The optimization only takes place for the simple cases above!


And this is the trap.


You loose the optimization, if you loop over concatenations that look as follows:


str = str && ... && meth( ... ) && ... .

str = str && ... && str && ... .


str = |{ str }...{ expr( ... ) }...|.


str = |{ str format_options }...|.


str = |{ str }...{ str }...|.


As long as you do such a concatenation outside of loops, no problem. But inside of loops and for a large number of iterations you can quickly experience really large runtimes.


There is in fact a problem, writing something looking as harmless like this:


DATA(html) = `<html><body><table border=1>`.

DO ... TIMES.

  html = |{ html }<tr><td>{

            CONV string( ipow( base = sy-index exp = 2 ) )

            }</td></tr>|.

ENDDO.

html = html && `</table></body></html>`.

Concatenating the ipow expression to str breaks the optimization.


Good News


Now that you know the problem, you can easily circumvent it. Normally we use expressions to get rid of helper variables. But in connection with loops helper variables can be a good thing. You already know that you use  them for calculating results that are constant within a loop. Now you learn, that you should also use them for concatenating expressions or functions to strings:

DATA(html) = `<html><body><table border=1>`.

DATA square type string.

DO ... TIMES.

   square = ipow( base = sy-index exp = 2 ).

   html = |{ html }<tr><td>{ square }</td></tr>|.

ENDDO.

html = html && `</table></body></html>`.

By assigning the ipow expression to helper variable square and concatenating that to str,  the optimization takes place again. Try it yourself and see what happens for large numbers of iterations with and without optimization!

Last but not least, what is said above for loops realized by control statements is also true for FOR loops in expressions:

DATA(result) =

  REDUCE string( INIT s = ``

                 FOR i = 1 UNTIL i > ...

                 NEXT s = s && CONV string( i ) ).

vs.

DATA(result) =

  REDUCE string( INIT s = ``

                 FOR i = 1 UNTIL i > ...

                 LET num = CONV string( i ) IN

                 NEXT s = s && num ).

Only by using the helper variable num declared in a LET expression the optimization is enabled. The example without helper variable shows a quadratic increase of runtime with the number of iterations.

The last example is directly taken from the recent documentation. But have you been aware of that?

18 Comments