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:

/wp-content/uploads/2015/12/clipboard1_850259.png

/wp-content/uploads/2015/12/clipboard2_850260.png

The application logs would be displayed in WebUI as below:

/wp-content/uploads/2015/12/clipboard3_850261.png

The signature of API:

/wp-content/uploads/2015/12/clipboard4_850262.png

The structure of table type CRMT_ODATA_TASK_LOGST could be found below:

/wp-content/uploads/2015/12/clipboard5_850263.png

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:

/wp-content/uploads/2015/12/clipboard6_850264.png

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( ).
To report this post you need to login first.

Be the first to leave a comment

You must be Logged on to comment or reply to a post.

Leave a Reply