midrange.com code scratchpad
Name:
Dennis Lovelady
Scriptlanguage:
Plain Text
Tabwidth:
4
Date:
03/07/2010 01:50:32 pm
IP:
Logged
Description:
Compare differences in clock time for three approaches to character string matching:
1) SQL
2) Regular expressions
3) RPG built-in function
Code:
  1.      H OPTION(*NOSHOWCPY:*NOEXPDDS:*NODEBUGIO:*SRCSTMT)
  2.      H DATFMT(*ISO) TIMFMT(*ISO) DFTACTGRP(*NO)
  3.      H CVTOPT(*VARCHAR:*NODATETIME)
  4.      H THREAD(*SERIALIZE)
  5.      H BndDir('QC2LE')
  6.  
  7.        // SQL.V.BIF //////////////////////////////////////////////////////
  8.        // Compare the execution times of three methods of SQL's LIKE
  9.        // function:
  10.        //    1) SQL's LIKE function
  11.        //    2) Regular expressions
  12.        //    3) Built-in %Scan function
  13.        //
  14.        // The comparisons will be made for two kinds of LIKE conditions.
  15.        // The first is a "contains" function, such as '%mytext%'
  16.        // The second is a more complex comparison (from %SCAN's point
  17.        // of view, anyway) as in '%my%text%'
  18.        //
  19.        // The general flow is as follows:
  20.        //    Do OUTER_LOOP times
  21.        //       Do SQL_test_1 INNER_LOOP times
  22.        //       Do RegEx_test_1 INNER_LOOP times
  23.        //       Do BIF_test_1 INNER_LOOP times
  24.        //       Do SQL_test_2 INNER_LOOP times
  25.        //       Do RegEx_test_2 INNER_LOOP times
  26.        //       Do BIF_test_2 INNER_LOOP times
  27.        //    EndOuterLoop
  28.        //
  29.        // In this manner, we can avoid performance-related questions
  30.        // that might come up, like "such and such test performed better
  31.        // because it was done first" or "... last" et cetera.  We can
  32.        // also determine/adjust differences in setup time (as in regcomp)
  33.        // if desired.
  34.        //
  35.        // On an unknown partition size of a 520 system running minimal
  36.        // processes, the results for 1000 iterations of 1000 inner loops
  37.        // the results on V5R3M0 were:
  38.        //    SQL "contains" . . . : 72.201 seconds
  39.        //    RegEx "contains" . . :  6.766 seconds
  40.        //    %Scan "contains" . . :  1.020 seconds
  41.        //    SQL "complex"  . . . : 72.097 seconds
  42.        //    RegEx "complex"  . . : 41.286 seconds
  43.        //    %Scan "complex"  . . :   .987 seconds
  44.        //
  45.        // It is interesting that the more complex %SCAN test performed
  46.        // slightly better than the simpler one.  This has held true on
  47.        // each test, and it's a difference that probably only a Barbara
  48.        // might understand.  It is also interesting how much more time
  49.        // was used by the "complex" Regular Expressions approach.  I
  50.        // personally thought that test would be the clear winner.
  51.  
  52.       /Include LUVPGMSRC,REGEXP_H
  53.  
  54.      D THIS_PGM        C                   'SQL.V.BIF'
  55.  
  56.      D myProto         PR                  ExtPgm(THIS_PGM)
  57.      D  parmOuter                    15  5 Const
  58.      D  parmInner                    15  5 Const
  59.  
  60.      D myProto         PI
  61.      D  parmOuter                    15  5 Const
  62.      D  parmInner                    15  5 Const
  63.  
  64.  
  65.  
  66.      D MS_FACTOR       C                   1000000
  67.      D ERROR_RETURN    C                   -999999
  68.  
  69.      D SQLcontains     PR            21  3 ExtProc('SQLcontains')
  70.      D regExContains   PR            21  3 ExtProc('regExContains')
  71.      D BIFcontains     PR            21  3 ExtProc('BIFcontains')
  72.  
  73.      D SQLcomplex      PR            21  3 ExtProc('SQLcomplex')
  74.      D regExComplex    PR            21  3 ExtProc('regExComplex')
  75.      D BIFcomplex      PR            21  3 ExtProc('BIFcomplex')
  76.  
  77.      D myText          S             50    Varying Inz('SearchXYabZXXX yz')
  78.  
  79.        // Values for a "contains" test
  80.      D CON_SQL_CT      S             20    Varying Inz('%XX y%')
  81.      D CON_REX_CT      S             20    Varying Inz('XX y')
  82.      D CON_BIF_CT      S             20    Varying Inz('XX y')
  83.  
  84.        // A more complex test
  85.      D CON_SQL_CPX     S             20    Varying Inz('%ch%ab%')
  86.      D CON_REX_CPX     S             20    Varying Inz('ch.*ab')
  87.      D CON_BIF_CPX1    S             20    Varying Inz('ch')
  88.      D CON_BIF_CPX2    S             20    Varying Inz('ab')
  89.  
  90.      D I               S             10U 0
  91.      D innerIters      S             10U 0 Inz(2000)
  92.      D msg             S             50    Varying
  93.      D outerIters      S             10U 0 Inz(1000)
  94.      D timingBIFcpx    S                   Like(BIFcomplex)    Inz(*Zero)
  95.      D timingBIFct     S                   Like(BIFcontains)   Inz(*Zero)
  96.      D timingREXcpx    S                   Like(regExComplex)  Inz(*Zero)
  97.      D timingREXct     S                   Like(regExContains) Inz(*Zero)
  98.      D timingSQLcpx    S                   Like(SQLcomplex)    Inz(*Zero)
  99.      D timingSQLct     S                   Like(SQLcontains)   Inz(*Zero)
  100.  
  101.       /Free
  102.        Monitor ;                        // If any errors, we will bail out
  103.           If %Parms > *Zero ;              // If any parameters were passed
  104.              outerIters = parmOuter ;      // Override default outer loop count
  105.              If %Parms > 1 ;               // If two or more parms
  106.                 innerIters = parmInner ;   // Override inner loop
  107.              EndIF ;
  108.           EndIF ;
  109.           For I = 1 to outerIters ;            // Major loop
  110.              timingSQLct += SQLcontains() ;    // SQL "%contains%"
  111.              timingREXct += regExContains() ;  // RegEx "contains"
  112.              timingBIFct += BIFcontains() ;    // %SCAN "contains"
  113.  
  114.              timingSQLcpx += SQLcomplex() ;    // SQL "%comp%lex%"
  115.              timingREXcpx += regExComplex() ;  // RegEx "comp.*lex"
  116.              timingBIFcpx += BIFcomplex() ;    // %SCAN "comp" + "lex"
  117.           EndFOR ;
  118.        On-error *ALL ;
  119.        EndMON ;
  120.  
  121.        // Grunt work...
  122.  
  123.        Msg = %Char(outerIters) + ' outer, '
  124.            + %Char(innerIters) + ' inner loops.' ;
  125.        Dsply Msg ;
  126.        Msg = 'SQL contains: ' + %Char(timingSQLct) ;
  127.        Dsply Msg ;
  128.        Msg = 'RegEx contains: ' + %Char(timingREXct) ;
  129.        Dsply Msg ;
  130.        Msg = '%Scan contains: ' + %Char(timingBIFct) ;
  131.        Dsply Msg ;
  132.        Msg = 'SQL complex: ' + %Char(timingSQLcpx) ;
  133.        Dsply Msg ;
  134.        Msg = 'RegEx complex: ' + %Char(timingREXcpx) ;
  135.        Dsply Msg ;
  136.        Msg = '%Scan complex: ' + %Char(timingBIFcpx) ;
  137.        Dsply Msg ;
  138.        *INLR = *On ;
  139.        Return ;
  140.       /End-free
  141.  
  142.  
  143.  
  144.      P SQLcontains     B
  145.      D                 PI            21  3
  146.  
  147.        // Using SQL, determine if a LIKE '%simple%' condition exists
  148.        // Any error (including "no match" condition) will cause this
  149.        // procedure to return ERROR_RETURN
  150.  
  151.      D startTimestamp  S               Z   Inz
  152.      D stopTimestamp   S               Z   Inz
  153.      D I               S             10U 0
  154.      D isFound         S               N
  155.      D rtnVal          S                   Like(SQLcontains)
  156.  
  157.       /Free
  158.  
  159.        Monitor ;
  160.           startTimestamp = %Timestamp() ;
  161.           For I = 1 to innerIters  ;
  162.              Exec SQL SET :isFound = CASE WHEN :myText LIKE :CON_SQL_CT
  163.                                      THEN '1'
  164.                                      ELSE '0'
  165.                                      End
  166.                 ;
  167.              If isFound = *Off ;
  168.                 stopTimestamp -= %Years(5000) ;    // Force an error
  169.                 Leave ;
  170.              EndIF ;
  171.           EndFOR ;
  172.           stopTimestamp = %Timestamp ;
  173.           rtnVal = %Diff(stopTimestamp: startTimestamp:*MS) / MS_FACTOR ;
  174.        On-Error *all ;
  175.           rtnVal = ERROR_RETURN ;
  176.        EndMON ;
  177.        Return rtnVal ;
  178.  
  179.       /End-free
  180.  
  181.      P SQLcontains     E
  182.  
  183.  
  184.  
  185.      P regExContains   B
  186.      D                 PI            21  3
  187.  
  188.        // Using RegEx, determine if a simple condition exists
  189.        // Any error (including "no match" condition) will cause this
  190.        // procedure to return ERROR_RETURN
  191.  
  192.      D startTimestamp  S               Z   Inz
  193.      D stopTimestamp   S               Z   Inz
  194.      D matched         S               N   Inz(*Off)
  195.      D regEx           DS                  LikeDS(regex_t)
  196.      D match           DS                  LikeDS(regmatch_t)
  197.      D I               S             10U 0
  198.      D rc              S             10I 0
  199.      D regExErrorBuff  S            512
  200.      D rtnVal          S                   Like(regExContains)
  201.  
  202.       /Free
  203.  
  204.        Monitor ;
  205.           startTimestamp = %Timestamp() ;
  206.           rc = regcomp(regEx: CON_REX_CT
  207.                      : REG_EXTENDED + REG_NOSUB
  208.                       ) ;
  209.           If (rc <> *Zero) ;
  210.              regError(rc: regEx
  211.                         : %Addr(regExErrorBuff): %Size(regExErrorBuff)) ;
  212.              stopTimestamp -= %Years(5000) ; // Force an error
  213.           EndIF ;
  214.           For I = 1 to innerIters  ;
  215.              rc = regexec(regEx
  216.                         : myText
  217.                         : *Zero
  218.                         : match
  219.                         : *Zero
  220.                          ) ;
  221.              matched = (rc = *Zero) ;           // Match(es) found
  222.              If matched = *Off ;
  223.                 stopTimestamp -= %Years(5000) ; // Force an error
  224.                 Leave ;
  225.              EndIF ;
  226.           EndFOR ;
  227.           regFree(regEx) ;
  228.           stopTimestamp = %Timestamp ;
  229.           rtnVal = %Diff(stopTimestamp: startTimestamp:*MS) / MS_FACTOR ;
  230.        On-error *ALL ;
  231.           rtnVal = ERROR_RETURN ;
  232.        EndMON ;
  233.        Return rtnVal ;
  234.  
  235.       /End-free
  236.  
  237.      P regExContains   E
  238.  
  239.  
  240.  
  241.      P BIFcontains     B
  242.      D                 PI            21  3
  243.  
  244.        // Using %SCAN, determine if a simple condition exists
  245.        // Any error (including "no match" condition) will cause this
  246.        // procedure to return ERROR_RETURN
  247.  
  248.      D startTimestamp  S               Z
  249.      D stopTimestamp   S               Z
  250.      D matched         S               N   Inz(*Off)
  251.      D I               S             10U 0
  252.      D pos             S             10I 0
  253.      D rtnVal          S                   Like(BIFcontains)
  254.  
  255.       /Free
  256.  
  257.        Monitor ;
  258.           startTimestamp = %Timestamp() ;
  259.           For I = 1 to innerIters  ;
  260.              pos = %Scan(CON_BIF_CT: myText) ;
  261.              matched = (pos > *Zero) ;
  262.              If matched = *Off ;
  263.                 stopTimestamp -= %Years(5000) ; // Force an error
  264.                 Leave ;
  265.              EndIF ;
  266.           EndFOR ;
  267.           stopTimestamp = %Timestamp ;
  268.           rtnVal = %Diff(stopTimestamp: startTimestamp:*MS) / MS_FACTOR ;
  269.        On-error *ALL ;
  270.           rtnVal = ERROR_RETURN ;
  271.        EndMON ;
  272.        Return rtnVal ;
  273.  
  274.       /End-free
  275.  
  276.      P BIFcontains     E
  277.  
  278.  
  279.  
  280.      P SQLcomplex      B
  281.      D                 PI            21  3
  282.  
  283.        // Using SQL, determine if a LIKE '%comp%lex%' condition exists
  284.        // Any error (including "no match" condition) will cause this
  285.        // procedure to return ERROR_RETURN
  286.  
  287.      D startTimestamp  S               Z
  288.      D stopTimestamp   S               Z
  289.      D I               S             10U 0
  290.      D isFound         S               N
  291.      D rtnVal          S                   Like(SQLcomplex)
  292.  
  293.       /Free
  294.  
  295.        Monitor ;
  296.           startTimestamp = %Timestamp() ;
  297.           For I = 1 to innerIters  ;
  298.              Exec SQL SET :isFound = CASE WHEN :myText LIKE :CON_SQL_CPX
  299.                                      THEN '1'
  300.                                      ELSE '0'
  301.                                      End
  302.                 ;
  303.              If isFound = *Off ;
  304.                 stopTimestamp -= %Years(5000) ; // Force an error
  305.                 Leave ;
  306.              EndIF ;
  307.           EndFOR ;
  308.           stopTimestamp = %Timestamp ;
  309.           rtnVal = %Diff(stopTimestamp: startTimestamp:*MS) / MS_FACTOR ;
  310.        On-error *ALL ;
  311.           rtnVal = ERROR_RETURN ;
  312.        EndMON ;
  313.        Return rtnVal ;
  314.  
  315.       /End-free
  316.  
  317.      P SQLcomplex      E
  318.  
  319.  
  320.  
  321.      P regExComplex    B
  322.      D                 PI            21  3
  323.  
  324.        // Using RegEx, determine if a 'comp.*lex' condition exists
  325.        // Any error (including "no match" condition) will cause this
  326.        // procedure to return ERROR_RETURN
  327.  
  328.      D startTimestamp  S               Z
  329.      D stopTimestamp   S               Z
  330.      D matched         S               N   Inz(*Off)
  331.      D regEx           DS                  LikeDS(regex_t)
  332.      D match           DS                  LikeDS(regmatch_t)
  333.      D I               S             10U 0
  334.      D rc              S             10I 0
  335.      D regExErrorBuff  S            512
  336.      D rtnVal          S                   Like(regExComplex)
  337.  
  338.       /Free
  339.  
  340.        Monitor ;
  341.           startTimestamp = %Timestamp() ;
  342.           rc = regcomp(regEx: CON_REX_CPX
  343.                      : REG_EXTENDED + REG_NOSUB
  344.                       ) ;
  345.           If (rc <> *Zero) ;
  346.              regError(rc: regEx
  347.                         : %Addr(regExErrorBuff): %Size(regExErrorBuff)) ;
  348.              stopTimestamp -= %Years(5000) ; // Force an error
  349.           EndIF ;
  350.           For I = 1 to innerIters  ;
  351.              rc = regexec(regEx
  352.                         : myText
  353.                         : *Zero
  354.                         : match
  355.                         : *Zero
  356.                          ) ;
  357.              matched = (rc = *Zero) ;           // Match(es) found
  358.              If matched = *Off ;
  359.                 stopTimestamp -= %Years(5000) ; // Force an error
  360.                 Leave ;
  361.              EndIF ;
  362.           EndFOR ;
  363.           regFree(regEx) ;
  364.           stopTimestamp = %Timestamp ;
  365.           rtnVal = %Diff(stopTimestamp: startTimestamp:*MS) / MS_FACTOR ;
  366.        On-error *ALL ;
  367.           rtnVal = ERROR_RETURN ;
  368.        EndMON ;
  369.        Return rtnVal ;
  370.  
  371.       /End-free
  372.  
  373.      P regExComplex    E
  374.  
  375.  
  376.  
  377.      P BIFcomplex      B
  378.      D                 PI            21  3
  379.  
  380.        // Using %SCAN, determine if two conditions exist in proper
  381.        // sequence.  Any error (including "no match" condition) will
  382.        // cause this procedure to return ERROR_RETURN
  383.  
  384.      D startTimestamp  S               Z   Inz
  385.      D stopTimestamp   S               Z   Inz
  386.      D matched         S               N   Inz(*Off)
  387.      D I               S             10U 0
  388.      D pos             S             10I 0
  389.      D s1Len           S              5U 0
  390.      D rtnVal          S                   Like(BIFcomplex)
  391.  
  392.       /Free
  393.  
  394.        Monitor ;
  395.           startTimestamp = %Timestamp() ;
  396.           s1Len = %Len(CON_BIF_CPX2) ;
  397.           For I = 1 to innerIters  ;
  398.              pos = %Scan(CON_BIF_CPX1: myText) ;
  399.              If pos > *Zero ;
  400.                 pos = %Scan(CON_BIF_CPX2: myText: pos + s1Len + 1) ;
  401.              EndIF ;
  402.              matched = (pos > *Zero) ;
  403.              If matched = *Off ;
  404.                 stopTimestamp -= %Years(5000) ; // Force an error
  405.                 Leave ;
  406.              EndIF ;
  407.           EndFOR ;
  408.           stopTimestamp = %Timestamp ;
  409.           rtnVal = %Diff(stopTimestamp: startTimestamp:*MS) / MS_FACTOR ;
  410.        On-error *ALL ;
  411.           rtnVal = ERROR_RETURN ;
  412.        EndMON ;
  413.        Return rtnVal ;
  414.  
  415.       /End-free
  416.  
  417.      P BIFcomplex      E
  418.  
© 2004-2019 by midrange.com generated in 0.009s valid xhtml & css