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: 
pokrakam
Active Contributor
I got a blog-worthy surprise when I did a quick performance test on two different ways to use the new-ish (7.4) table expressions to perform a search on an internal table.

My scenario involved internal table searches where the values would be missing a lot of the time. The unexpected result was that when a row can't be found, using table expressions with exceptions is ten times slower on average than all other lookup methods, including the good ol' fashioned READ TABLE with SY-SUBRC.

Quick primer for those still getting accustomed to 7.4 ABAP (feel free to skip to the results):

With table expressions, finding an entry in a table is really neat. I have a table that includes a field "ID". So the following READ TABLE can be rewritten using a table expression (the bit in square brackets):
READ TABLE itab INTO row WITH KEY id = find_id.
"Equivalent to:
row = itab[ id = find_id ].

If the matching entry cannot be found, you need to catch the exception:
  TRY.
row = itab[ id = find_id ].
CATCH cx_sy_itab_line_not_found.
...
ENDTRY.

Another alternative is to use a VALUE constructor expression where you can add the keyword OPTIONAL (or DEFAULT) to just initialize any non-found values.
row = VALUE #( itab[ id = find_id ] OPTIONAL ).
IF row IS INITIAL.
...

This particular usage of VALUE may seem a little awkward. I mean, why use x = VALUE #( y ) when you can just write x = y? The VALUE constructor's sole purpose here is the OPTIONAL bit that lets us do away with the exception.

As I was working on a performance-sensitive component, I tested it to see what performs best.

Results:


For completeness, and additionally with suggestions by eyjfc, quynh.doanmanh and se38 in the comments, I also added a couple of other ways to look for an entry in a table:

  • The trusty READ TABLE  ... WITH KEY, with and without TRANSPORTING NO FIELDS

  • line_exists( )

  • ASSIGN itab[ ... ]  - which (bizarrely) sets SY-SUBRC instead of producing an exception

  • REF #( ) instead of VALUE #( ).


I tested a million failed lookups on a 7.50 HANA system. Here are the results in microseconds:
line_exists( )                        :   610,758
READ TABLE ... TRANSPORTING NO FIELDS : 671,368
READ TABLE : 671,115
ASSIGN itab[ ... ] : 707,929
REF #( itab[ ... ] OPTIONAL ) : 888,562
VALUE #( itabl[ ... ] OPTIONAL ) : 961,803
TRY ... itab[ ... ] CATCH : 6,325,265

I did not expect the last one at all.

So the take-away here for me is that a TRY-CATCH may be easier to read, but should not be used in performance-sensitive code unless you expect the values to be found most of the time. I'm happy to sacrifice a little performance for readability, but this is a significant impact.

I suspect that this applies to CATCH blocks in general, but that's another analysis for another day.

For comparison, I also re-ran the same but this time with a lookup value that did exist. Two things happened:

  • the exception was no longer an influence (to be expected)

  • line_exists( ) became a poor choice in my scenario, because we need to double up the the search in order to also read the record:


  IF line_exists( itab[ id = find_id ] ).
row = itab[ id = find_id ].

To summarise:



  • If you don't need the data, line_exists( ) is fastest.

  • If performance is number 1 priority and you need the data, READ TABLE is fastest.

  • For compact and/or readable code, use table expressions.
    (Yes, I know, 'new' ABAP can be used to make code either more readable or more cryptic, but that's another discussion)

  • TRY-CATCH with table expressions can be a useful way to structure your code (e.g. to use a single catch handler for multiple expressions), but be mindful of the expected failure rate and performance-criticality of the component. If we're talking anything less than thousands in a short space of time then you can safely ignore the impact.


Update:

After several comments and suggestions, I uploaded my test program to GitHub at abap_itab_perf

It was just a quick-n-dirty originally for comparing just two options and just grew as a copy/paste of alternatives. Feel free to play with it or even send any updates back via GitHub.
36 Comments
Labels in this area