blog de Floren

SuccessFactors, SAP HXM y Programación ABAP

Método del Cursor Paralelo

Posted by Floren en agosto 30, 2008

El rendimiento es un pilar muy importante en la construcción de nuestros programas. Aunque la mayoría de las veces no disponemos de un entorno de desarrollo/integración adecuado para la realización de pruebas de volumen aceptables (comparables a la realidad de los sistemas productivos), existen algunas herramientas en el Workbench de SAP con las que conseguir optimizar los programas que vamos a entregar.

 Éste es un área en el que tenemos que investigar y aprender mucho porque es muy amplio y cambiante. Las últimas versiones de SAP incorporan al lenguaje ABAP nuevas instrucciones de acceso a base de datos, nuevos tipos de tablas internas… Hay algunos manuales pero sería bueno hacer una revisión profunda de los mismos y debemos asegurarnos su mayor difusión.

 No es nada nuevo el decir que las transacciones ST05 Trace Requests y SE30 Runtime Analysis son las principales herramientas para ello. Un comentario. Se debe salir y entrar a estas transacciones cada vez que se quieran utilizar, pues aparentemente SAP no inicializa los valores obtenidos en la anterior ejecución, lo que conduce a que se acumulen los valores de forma muy engañosa.

 Muchas veces nos fijamos y tratamos de mejorar los accesos a bases de datos, lo cual es muy importante. La correcta utilización de la cláusula for all entries, la optimización en la selección de campos en la claúsula select y en la restricción de registros a recuperar en la cláusula where, el empleo de índices al realizar los accesos… son algunos de los muchos puntos que hay que cuidar. Sin embargo no hay que descuidar la programación del tratamiento de los datos, una vez recuperados de la base de datos.

 Voy a comentar aquí un ejemplo que mejora sensiblemente el rendimiento en el proceso de tablas internas.

 Imaginemos que tenemos dos tablas internas, I_HEADER e I_DETAIL con los datos de cabecera y detalle de documentos de, por ejemplo, FI. Ambas tablas tienen, digamos (para simplificar) el campo BELNR (‘numero de documento’) como enlace común. La forma natural o instintiva de realizar su tratamiento es

 Ejemplo 1.

 LOOP AT I_HEADER.

             LOOP AT I_DETAIL WHERE BELNR = I_HEADER-BELNR.

                         PROCESO PARA CADA POSICION

             ENDLOOP.

ENDLOOP.

Este tratamiento hace que para cada registro de I_HEADER se recorra desde el principio la tabla I_DETAIL hasta encontrar aquel(los) registro(s) con el mismo BELNR que dicho registro de I_HEADER. Esto se puede evitar con el llamado método del curso paralelo (MCP), que ‘recuerda’ qué registros de la tabla I_HEADER ya se han leído en pasos anteriores por el bucle para seguir buscando a partir del último ya leído en el paso anterior:

Ejemplo 2.

V_COUNTER = 1.

LOOP AT I_HEADER.

            LOOP AT I_DETAIL FROM V_COUNTER.

                        V_COUNTER = SY-TABIX.

                        IF I_DETAIL-BELNR < I_HEADER-BELNR.

                                   CONTINUE.

                        ELSEIF I_DETAIL-BELNR > I_HEADER-BELNR.

                                   EXIT.

                        ELSEIF I_DETAIL-BELNR = I_HEADER-BELNR.

                                   PROCESO PARA CADA POSICION

                        ENDIF.

            ENDLOOP.

ENDLOOP.

Aunque de apariencia algo más compleja, los resultados obtenidos son realmente sorprendentes.

Esto requiere que las dos tablas tengan uno o varios campos que las relacionen y exige que ambas tablas estén ordenadas de igual forma por dichos campos. Lógicamente el ejemplo que aquí se muestra está simplificado: se restringe a un solo campo de enlace entre las tablas, pero se podría aplicar para n campos de enlace. Asímismo se puede complicar el proceso, introduciendo además condiciones de restricción where al segundo loop en el Ejemplo 1, siendo entonces sustituidas por condiciones de restricción check dentro del segundo loop en el Ejemplo 2.

Véanse a continuación los tiempos obtenidos al modificar simplemente el tratamiento de tres parejas de tablas en tres forms (F1, F2, F3) en un ejemplo real. El tiempo está expresado en microsegundos, tal y como lo refleja la transacción SE30; la tabla de cabecera contenía únicamente 700 registros. Con un volumen real de datos, los resultados serán impresionantes a todas luces:

                        Ejemplo 1         % Total             Ejemplo 2         % Total             Reducción (veces)

TOTAL              24.728.827        100                  7.534.817         100

Base Datos       5.587.376         22,6                 5.666.839         75.2                 0,98 (aprox 1: circunstancial)

ABAP               19.023.286        76,9                 1.765.707         23,4                 10,8

R3                    118.165                        0,5                   102.271                        1,4                   1,15 (aprox 1: circunstancial)

F1                    8.794.506                                 125.033                                               70,3

F2                    4.950.223                                 80.004                                     61,8

F3                    3.905.878                                 421.887                                               9,2

Adviértase cómo se traslada el peso del consumo de tiempo desde la lógica de proceso (76,9% en el Ejemplo 1; 23,4% en el Ejemplo 2) en ABAP al acceso a base de datos (22,6% en el Ejemplo 1; 75,2% en el Ejemplo 2). El tiempo de proceso ABAP general se reduce 11 veces. En particular, en los tres forms en los que se puede aplicar esta mejora, se reduce 70, 60 y 9 veces respectivamente. Un análisis detallado del form F3 podría arrojar pistar sobre el motivo de por qué la reducción es ‘sólo’ de 9 veces; pero esto es otra problemática y se escapa al propósito inicial de este mensaje.

3 respuestas to “Método del Cursor Paralelo”

  1. gunshit said

    Buenas,

    A mi con este método me ocurre algo extraño. En efecto al usar el ejemplo 2 los microsegundos se reducen en el total, pero al contrario de lo que dice Floren, a mi el trabajo de ABAP me aumenta considerablemente en el ejemplo 2, disminuyendo la parte de BBDD. Os dejo aquí mi código y los gráficos de la SE30:

    Además curiosamente el JOB del ejemplo 1 termina antes que el JOB del ejemplo 2:
    JOB Ejemplo 1: 32
    JOB Ejemplo 2: 169

    Gráficos SE30:
    EJEMPLO 1: http://gunshit.250free.com/SAP/test_1.PNG
    EJEMPLO 2: http://gunshit.250free.com/SAP/test_2.PNG

    Sabeis a que pueden deberse estos comportamientos y como mejorar el acceso en este caso concreto?

    Salu2.

    EJEMPLO 1: http://paste.ideaslabs.com/show/vgvje9MPQ

    EJEMPLO 2: http://paste.ideaslabs.com/show/d7ENcJsC

  2. Fabio A. Rodriguez said

    Yo he probado el ejemplo con el cursor paralelo y mas performante que no usarlo. Pero también existe otro algoritmo de cursor paralelo que me parece más performante aún (se los dejo debajo). Por otra parte, cuando realizamos las mediciones de costos de las consultas (testing), debemos considerar que la Base de Datos trabaja en forma interna con estadísticas de las consultas realizadas (para mejorar la Performance); por lo cual, cuando se realiza una segunda consulta seleccionando los mismos datos, es normal que tarde menos que la anterior.
    Bueno les dejo mis saludos y el algoritmo:

    LOOP AT i_mara INTO w_mara.

    READ TABLE i_makt TRANSPORTING NO FIELDS
    WITH KEY matnr = w_mara-matnr
    BINARY SEARCH.

    IF sy-subrc EQ 0.

    v_tabix = sy-tabix.

    LOOP AT i_makt INTO w_makt FROM v_tabix.

    IF w_makt-matnr NE w_mara-matnr.

    EXIT.

    ENDIF.

    ENDLOOP.

    ENDIF.

    ENDLOOP.

  3. Floren said

    Buen aporte Fabio,

    Gracias y saludos

Deja un comentario