For loop question

Just starting out? Need help? Post your questions and find answers here.
Dude
Addict
Addict
Posts: 1907
Joined: Mon Feb 16, 2015 2:49 pm

For loop question

Post by Dude »

In another post, it was brought to my attention that this is wrong to do:

Code: Select all

For loop = 1 To CountString(text$,"something")
Next
Apparently it's inefficient, and I should really be doing it like this:

Code: Select all

cs=CountString(text$,"something")
For loop = 1 To cs
Next
Is this truly better/faster? Does the first way really repeatedly call CountString() over and over with each loop iteration?

I was under the impression that "For" calculated the number of loops just once, at the start of the loop. No? :shock:
User avatar
TI-994A
Addict
Addict
Posts: 2512
Joined: Sat Feb 19, 2011 3:47 am
Location: Singapore
Contact:

Re: For loop question

Post by TI-994A »

Dude wrote:Is this truly better/faster?
QED:

Code: Select all

loopLength = 10000
a$ = Space(loopLength)

t = ElapsedMilliseconds()
For i = 1 To CountString(a$, " ")
  a + 1
Next
Debug ElapsedMilliseconds() - t

t = ElapsedMilliseconds()
For i = 1 To loopLength
  a + 1
Next
Debug ElapsedMilliseconds() - t
It's evidently faster.
Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel :D
Dude
Addict
Addict
Posts: 1907
Joined: Mon Feb 16, 2015 2:49 pm

Re: For loop question

Post by Dude »

OMG. :shock: Time to re-code all my loops, then.

Even the good old For i = 1 To Len(a$) to parse a string is slow:

Code: Select all

loopLength = 50000
a$ = Space(loopLength)

t = ElapsedMilliseconds()
For i = 1 To Len(a$)
  a + 1
Next
Debug ElapsedMilliseconds() - t ; 1655 ms

t = ElapsedMilliseconds()
For i = 1 To loopLength
  a + 1
Next
Debug ElapsedMilliseconds() - t ; 3 ms!
So it seems you should use a variable after "To" in all "For" loops, for the fastest performance. I'm truly shocked! :shock:
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: For loop question

Post by wilbert »

Dude wrote:So it seems you should use a variable after "To" in all "For" loops, for the fastest performance. I'm truly shocked! :shock:
Yes, that's true (or a constant of course). :)
Windows (x64)
Raspberry Pi OS (Arm64)
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: For loop question

Post by wilbert »

It doesn't have to do with your For loop question but this is another funny example with results you might not expect.

Code: Select all

DisableDebugger


Procedure.s P1(s.s)
  ProcedureReturn s
EndProcedure

Procedure.s P2(s.s)
  ProcedureReturn PeekS(@s)
EndProcedure


s.s = Space(10000)
t1 = ElapsedMilliseconds()
For i = 1 To 10000
  P1(s)
Next
t2 = ElapsedMilliseconds()
For i = 1 To 10000
  P2(s)
Next
t3 = ElapsedMilliseconds()
MessageRequester("Results", Str(t2-t1)+" vs "+Str(t3-t2))
Another thing is that when needing a temporary buffer, it can be faster to use a local Array (which is automatically freed) compared to AllocateMemory/FreeMemory.

Code: Select all

DisableDebugger


Procedure.s P1()
  Protected result.s, *mem
  *mem = AllocateMemory(10000)
  FillMemory(*mem, 10000, 32)
  result = PeekS(*mem)
  FreeMemory(*mem)
  ProcedureReturn result
EndProcedure

Procedure.s P2()
  Protected Dim buffer.a(9999)
  FillMemory(@buffer(), 10000, 32)
  ProcedureReturn PeekS(@buffer())
EndProcedure


t1 = ElapsedMilliseconds()
For i = 1 To 10000
  P1()
Next
t2 = ElapsedMilliseconds()
For i = 1 To 10000
  P2()
Next
t3 = ElapsedMilliseconds()
MessageRequester("Results", Str(t2-t1)+" vs "+Str(t3-t2))
Windows (x64)
Raspberry Pi OS (Arm64)
cas
Enthusiast
Enthusiast
Posts: 597
Joined: Mon Nov 03, 2008 9:56 pm

Re: For loop question

Post by cas »

Dude wrote:Does the first way really repeatedly call CountString() over and over with each loop iteration?
That is easy to check for youself:

Code: Select all

Procedure _Len2(string$,l)
  Debug "calculating whole string length... (line "+l+")"
  ProcedureReturn Len(string$)
EndProcedure
Macro Len2(s,l=#PB_Compiler_Line)
  _Len2(s,l)
EndMacro

a$="test string"

Debug "#### Loop #1: ####"
For i=1 To Len2(a$)
  Debug "i="+i
Next

Debug ""
Debug "### Loop #2: ####"

l=len2(a$)
For i=1 To l
  Debug "i="+i
Next
User avatar
CELTIC88
Enthusiast
Enthusiast
Posts: 154
Joined: Thu Sep 17, 2015 3:39 pm

Re: For loop question

Post by CELTIC88 »

look in pb assembly code!!

Code: Select all

  MOV    dword [v_LOOP],1
  JMP   _ForSkipDebug1
_For1:
_ForSkipDebug1:
  MOV    eax,_S2
  PUSH   eax
  PUSH   dword [v_text$]
  CALL  _PB_CountString@8 ;function "CountString()" is called each time in the loop :s
  CMP    eax,dword [v_LOOP]
  JL    _Next2
; Next
_NextContinue2:
  INC    dword [v_LOOP]
  JNO   _For1
_Next2:
interested in Cybersecurity..
User avatar
TI-994A
Addict
Addict
Posts: 2512
Joined: Sat Feb 19, 2011 3:47 am
Location: Singapore
Contact:

Re: For loop question

Post by TI-994A »

CELTIC88 wrote:look in pb assembly code...
Functions, regardless of where they are called, would always be executed. In the case of loops, variants (eg. function results) must be repeatedly evaluated as the compiler cannot know if they have changed during the iterations of the loop. Some smart compilers would actually optimise this by substituting the function with its static result, after determining that the contents of the loop do not alter the results of the variant.
Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel :D
User avatar
Josh
Addict
Addict
Posts: 1183
Joined: Sat Feb 13, 2010 3:45 pm

Re: For loop question

Post by Josh »

wilbert wrote:It doesn't have to do with your For loop question but this is another funny example with results you might not expect.
There are so many ways to save time. One of my favorites is 'Static'.

For example, if a procedure with a List is often called and the List always contains only few entries:

Code: Select all

DisableDebugger


Procedure.s P1()
  NewList MyList()

  For i=1 To 10
    AddElement (MyList())
  Next

EndProcedure

Procedure.s P2()
  Static NewList MyList()
  ClearList (MyList())

  For i=1 To 10
    AddElement (MyList())
  Next

EndProcedure


t1 = ElapsedMilliseconds()
For i = 1 To 10000
  P1()
Next
t2 = ElapsedMilliseconds()
For i = 1 To 10000
  P2()
Next
t3 = ElapsedMilliseconds()

MessageRequester("Results", Str(t2-t1)+" vs "+Str(t3-t2))
sorry for my bad english
User avatar
Fig
Enthusiast
Enthusiast
Posts: 351
Joined: Thu Apr 30, 2009 5:23 pm
Location: Côtes d'Azur, France

Re: For loop question

Post by Fig »

well, static is a global with a special name after the procedure... so it makes sens.
There are 2 methods to program bugless.
But only the third works fine.

Win10, Pb x64 5.71 LTS
User avatar
kenmo
Addict
Addict
Posts: 1967
Joined: Tue Dec 23, 2003 3:54 am

Re: For loop question

Post by kenmo »

I know about pre-calculating loop bounds to avoid function calls,

but wilbert and Josh, those extra tips are awesome :shock:
User avatar
Michael Vogel
Addict
Addict
Posts: 2666
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: For loop question

Post by Michael Vogel »

Nice ideas, so wilberts demo was quite interesting, even I would do move the '@' from the Procedure P2 to the calling instance P2(@s)...

I am also wondering if lists are speedier all the time - on my computer the traditional array can be much faster (but when playing around with #SpeedUp you might see also other results):

Code: Select all

DisableDebugger

Procedure.s P1()

	#SpeedUp=1
	Static Dim MyArr(0)

	For i=1 To 10
		CompilerIf #SpeedUp
			If ArraySize(MyArr())<i
				ReDim MyArr(((i>>#SpeedUp)+1)<<#SpeedUp)
			EndIf
		CompilerElse
			ReDim MyArr(i)
		CompilerEndIf
		MyArr(i)=i
	Next

EndProcedure
Procedure.s P2()
	Static NewList MyList()
	ClearList (MyList())

	For i=1 To 10
		AddElement (MyList())
	Next

EndProcedure

#Loop=100000

t1-ElapsedMilliseconds()
For i = 1 To #Loop
	P1()
Next
t2-ElapsedMilliseconds()
For i = 1 To #Loop
	P2()
Next
t3-ElapsedMilliseconds()

MessageRequester("Results", Str(t1-t2)+" vs "+Str(t2-t3))
Post Reply