It's ~30% faster to use "DO i = x TO 1 BY -1" than the closest "DO i = 1 TO x", even when x is calculated before the loop, unless, of course, the value of x changes in the loop and you want to change the current number of loops based of it.
This result is probably because, as documentation states,
variable = expression1 TO expression2 [ BY k ]
The expression2 is re-evaluated on each iteration of the block.
and that it is faster to evaluate a constant than even a variable.
Sample code:
&SCOPED-DEFINE NumLoops 100000 DEFINE VARIABLE i AS INTEGER NO-UNDO. DEFINE VARIABLE iEmptyLoopTime AS INTEGER NO-UNDO. DEFINE VARIABLE iMethodTime AS INTEGER NO-UNDO. DEFINE VARIABLE iStringToTestIndex AS INTEGER NO-UNDO. DEFINE VARIABLE cStringToTestList AS CHARACTER NO-UNDO INITIAL "123456789|sadfgilshdfgljshdfgklsjdhfg|sdgffffajksdghakjghakdfjghaajksdghakjghakdfjghasdjghasdkfasdkjajksdghakjghakdfjghasdjghasdkfasdkjajksdghakjghakdfjghasdjghasdkfasdkjajksdghakjghakdfjghasdjghasdkfasdkjajksdghakjghakdfjghasdjghasdkfasdkjsdjghasdkfasdkjfgasdkgasdgasdfjkghasdkfjghasdfkjghasdfhgasdfh". DEFINE VARIABLE cStringToTest AS CHARACTER NO-UNDO. DEFINE VARIABLE cResults AS CHARACTER NO-UNDO INITIAL "--Results--~n". DEFINE VARIABLE iNumStringsToTest AS INTEGER NO-UNDO. DEFINE VARIABLE iLength AS INTEGER NO-UNDO. DEFINE VARIABLE iCharIndex AS INTEGER NO-UNDO. ETIME(TRUE). DO i = 1 TO {&NumLoops}: END. iEmptyLoopTime = ETIME. iNumStringsToTest = NUM-ENTRIES(cStringToTestList, "|"). DO iStringToTestIndex = 1 TO iNumStringsToTest: ASSIGN cStringToTest = ENTRY(iStringToTestIndex, cStringToTestList, "|") cResults = cResults + "~n~nString:" + cStringToTest . ETIME(TRUE). DO i = 1 TO {&NumLoops}: /* Method 1 */ iLength = LENGTH(cStringToTest). DO iCharIndex = 1 TO iLength: END. END. iMethodTime = ETIME. cResults = cResults + "~nMethod 1:" + STRING(iMethodTime - iEmptyLoopTime). ETIME(TRUE). DO i = 1 TO {&NumLoops}: /* Method 2 */ DO iCharIndex = 1 TO LENGTH(cStringToTest): END. END. iMethodTime = ETIME. cResults = cResults + "~nMethod 2:" + STRING(iMethodTime - iEmptyLoopTime). ETIME(TRUE). DO i = 1 TO {&NumLoops}: /* Method 2 */ DO iCharIndex = LENGTH(cStringToTest) TO 1 BY -1: END. END. iMethodTime = ETIME. cResults = cResults + "~nMethod 3:" + STRING(iMethodTime - iEmptyLoopTime). ETIME(TRUE). DO i = 1 TO {&NumLoops}: /* Method 1 */ iLength = LENGTH(cStringToTest). DO iCharIndex = iLength TO 1 BY -1: END. END. iMethodTime = ETIME. cResults = cResults + "~nMethod 4:" + STRING(iMethodTime - iEmptyLoopTime). END. /* DO iStringToTestIndex = 1 TO iNumStringsToTest */ MESSAGE cResults VIEW-AS ALERT-BOX.
Sample results on my machine:
--Results--
String:123456789
Method 1:289
Method 2:319
Method 3:206
Method 4:218
String:sadfgilshdfgljshdfgklsjdhfg
Method 1:679
Method 2:800
Method 3:476
Method 4:491
String:sdgffffajksdghakjghakdfjghaajksdghakjghakdfjghasdjghasdkfasdkjajksdghakjghakdfjghasdjghasdkfasdkjajksdghakjghakdfjghasdjghasdkfasdkjajksdghakjghakdfjghasdjghasdkfasdkjajksdghakjghakdfjghasdjghasdkfasdkjsdjghasdkfasdkjfgasdkgasdgasdfjkghasdkfjghasdfkjghasdfhgasdfh
Method 1:5740
Method 2:7655
Method 3:3953
Method 4:3996