Performance improvements for ABL Code

Posted by mliu.mike on 15-Feb-2012 08:57

Can you help with some suggestions for improvements?

Background:  The method below is called to write a buffer out to file.   Before that, each field in the buffer is checked to format it among other things.

I profiled a sample where we have 64 columns over 200,000 rows to output.   Therefore we loop through each field for each row.

In summary:

  • if (pFieldBuffer:buffer-value = ?) then ... TIME TAKEN 12.8Million times X .000006 seconds = 76 seconds
  • Roughly other if statements also take 76 seconds
  • There is a .001 seconds overhead to call the method (in a delegated object) for each row of data: TIME TAKEN: 200 seconds

Due to the volume of data, are there any suggestions to improve performance?

method public override logical writeBuffer(input pHRowBuffer as handle):


/* code  ....Loop through each field in the row buffer */


if (pFieldBuffer:buffer-value = ?) then


if (pFieldBuffer:label > "") then  .....

All Replies

Posted by dlauzon on 15-Feb-2012 11:31

Can you post the complete code of your method?

Posted by mliu.mike on 15-Feb-2012 12:16

Here is the code:

a. Overhead to call a method takes up roughly 24% of total time.  Seems high to me.

b. If conditions on booleans are 10 times faster than conditions involving strings.

    

     206       method public override logical writeBuffer(input pHBuffer as handle):

      207  

      208           if (pHBuffer:available) then

      209           do:     

      210               for each ttOutStream no-lock :

      211                   vCharBuffer = "".

      212                   ttOutStream.hbuf:buffer-copy(pHBuffer).

      213  

      214                   do i = 1 to ttOutStream.hbuf:num-fields:

      215                       pFBuffer = ttOutStream.hbuf:buffer-field(i).

      216  

      217                       if (pFBuffer:label > "") then

      218                       do:

      219                       /*

      220                        * if formatter present: right now we label attribute

      221                        * to store it.  For performance reasons we choose to use

      222                        * the many attributes available to pass info

      223                        * around for the field

      224                        */

      225                           vFormatter = pFBuffer:label.  /* contains formatter*/

      226                           vFormatter = trim( "com.ifdsgroup.ifast.extract." + vFormatter).

      227                           vReportValue =  DYNAMIC-INVOKE(vFormatter,

      228                               "format",

      229                               input pFBuffer:buffer-value,

      230                               input pFBuffer:format).

      231                       end.

      232                       else

      233                       do: /* no formatter present*/

      234                           if (pFBuffer:buffer-value = ?) then

      235                           do:

      236                               case pFBuffer:data-type:

      237                                   when "date" then

      238                                       do:

      239                                           vReportValue = "00000000".

      240                                       end.

      241                                   Otherwise

      242                                   do:

      243                                       vReportValue = 

      244                                       fill(" ",integer(pFBuffer:width-chars)).

      245                                   end.

      246                               end case.

      247                           end.

      248                           else

      249                           do:

      250                               vReportValue = pFBuffer:string-value().

      251                           end.

      252                       end.

      253  

      254                       if (ttOutStream.columnDelimiter = ""

      255                           or (i = ttOutStream.hbuf:num-fields)

      256                           ) then

      257                           vCharBuffer = vCharBuffer + vReportValue.

      258                       else

      259                           vCharBuffer = vCharbuffer + vReportValue +

      260                                         ttOutStream.columnDelimiter.

      261                   end. /* do i =  */

      262                                               

      263                   if ttOutStream.upperCase  then

      264                   do:

      265                                   /* the higher CASE configurations take over if

      266                                    * set

      267                                    */

      268                     vCharBuffer = CAPS(vCharBuffer).

      269                   end.

      270                   put stream-handle ttOutStream.hStream unformatted

      271                       vCharBuffer skip.

      272               end. /* for each*/

      273        

      274           end. /* if available */

      275       end method.

Posted by Tim Kuehn on 15-Feb-2012 12:31

This:

pFBuffer = ttOutStream.hbuf:buffer-field(i).

is a serious performance killer, and what's more it's redudant information you only need to figure out once.

What I generally do is go through the buffer, stick all the buffer field handles in a TT, then FOR EACH through the TT as needed. Much faster.

Posted by dlauzon on 15-Feb-2012 12:44

Something like this should already be faster (less useless DO blocks, more assign).

One more thing: if your report is not going to be more than 1 gig in size, it might prove to be a lot faster to build it using a LONGCHAR and copy the longchar to a file only once at the end with COPY-LOB than to use multiple "put stream-handl".

method public override logical writeBuffer(input pHBuffer as handle):

   DEFINE VARIABLE iNumFields AS INTEGER NO-UNDO.

   iNumFields = pHBuffer:NUM-FIELDS.

   if (pHBuffer:available) then

   do:     

       for each ttOutStream no-lock :

           vCharBuffer = "".

           ttOutStream.hbuf:buffer-copy(pHBuffer).

           do i = 1 to iNumFields:

               pFBuffer = ttOutStream.hbuf:buffer-field(i).

               vReportValue = if (pFBuffer:label > "") then

               /*

                * if formatter present: right now we label attribute

                * to store it.  For performance reasons we choose to use

                * the many attributes available to pass info

                * around for the field

                */

                     DYNAMIC-INVOKE(right-trim( "com.ifdsgroup.ifast.extract." + pFBuffer:label /* contains formatter*/),

                       "format",

                       input pFBuffer:buffer-value,

                       input pFBuffer:format)

               else (if (pFBuffer:buffer-value = ?)

                    then (if pFBuffer:data-type = "date"

                          then "00000000"

                          else fill(" ",integer(pFBuffer:width-chars)))

                    else pFBuffer:string-value()).

               vCharBuffer = vCharbuffer + vReportValue + ttOutStream.columnDelimiter.

           end. /* do i =  */

           /* presumes that the delimiter can not be part of the data (we'd have to use a substring instead if so) */

           vCharBuffer = right-trim(vCharBuffer, ttOutStream.columnDelimiter).

                                         

            /* the higher CASE configurations take over if set */

            if ttOutStream.upperCase  then

             vCharBuffer = CAPS(vCharBuffer).

            put stream-handle ttOutStream.hStream UNFORMATTED vCharBuffer skip.

         end. /* for each*/

   end. /* if available */

end method.

Posted by Admin on 15-Feb-2012 12:48

What I generally do is go through the buffer, stick all the buffer field handles in a TT, then FOR EACH through the TT as needed. Much faster.

Did you test if the temp-table performs better than an array of the field handles (that would surprise me).

Posted by Tim Kuehn on 15-Feb-2012 15:14

mikefe wrote:

What I generally do is go through the buffer, stick all the buffer field handles in a TT, then FOR EACH through the TT as needed. Much faster.

Did you test if the temp-table performs better than an array of the field handles (that would surprise me).

From an array of field handles? No - and I'd expect using an array of field handles to be faster still, albeit less flexible.

He's getting his buffer-field handles from the buffer handle itself though, and that's what I think is partly affecting his performance.

Posted by mliu.mike on 16-Feb-2012 08:06

Thanks .... great work.

This thread is closed