Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations wOOdy-Soft on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

& 1

Status
Not open for further replies.

monikai

Programmer
Aug 4, 2002
358
PL
Hi,[sunshine]
are the macros (&string) make the program slower?
much or little?

Regards from Monika (Warszawa - Poland)
(monikai@yahoo.com)
 
evaluate() is supposed to be a bit quicker, but it really depends on what you're doing and how often you're using macro replacements.

I have never felt that using macro replacement was a bottle neck.

Brian
 
They can make your program much slower if they are used in a large quantity. See for yourself (cut-n-paste the code below into a prg and execute it):

Code:
LOCAL lcCommand
lcCommand = "y = 5 + 5"

x = SECONDS()
FOR lnCounter = 1 TO 1000000
	y = 5 + 5
ENDFOR
?"Without: " + TRANSFORM(SECONDS() - x) + " seconds"

x = SECONDS()
FOR lnCounter = 1 TO 1000000
	&lcCommand
ENDFOR
?"With: " + TRANSFORM(SECONDS() - x) + " seconds"

I even put the macro as the last test so it benefits some from VFP's use of cache. My results were as follows:

Without Macro Substitution: .33 seconds avg
With Macro Substitution: 2.85 seconds avg

...as you can see, when running a command 1 million times the performance degradation is quite noticeable.

boyd.gif

 
And evaluate() is in between.

Code:
x = SECONDS()
FOR lnCounter = 1 TO 1000000
    EVALUATE(lcCommand)
ENDFOR
?"With EVALUATE(): " + TRANSFORM(SECONDS() - x) + " seconds"
 
OK... not apples to apples. EVALUATE() requires a slightly different syntax.

Code:
lcCommand = "5 + 5"

x = SECONDS()
FOR lnCounter = 1 TO 1000000
    y=EVALUATE(lcCommand)
ENDFOR
?"With EVALUATE(): " + TRANSFORM(SECONDS() - x) + " seconds"

lcCommand2 = "5 + 5"

x = SECONDS()
FOR lnCounter = 1 TO 1000000
    y=&lcCommand
ENDFOR
?"With &: " + TRANSFORM(SECONDS() - x) + " seconds"
[code]
 
Just to note - many of the VFP commands no longer need a macro if you place () around them.

Examples:

lcTablename = 'customer'

* macro substitution:
USE &lcTablename

* with macro substitution:
USE (lcTablename)

The same is true for many of the SET commands.

Jim Osieczonek
Delta Business Group, LLC
 
If I build SELECT-SQL clauses as I go, would it be more efficient to build the whole statement first, then macro substitute the entire thing than to substitute several little bits and pieces in one statement?

I mean,

sql_str="SELECT "+my_list+" FROM "+my_alias+ ;
" WHERE "+my_conditions+" INTO DBF "+my_table

vs.

SELECT &my_list ;
FROM &my_alias ;
WHERE &my_conditions ;
INTO DBF (my_table)

Got to admit, I usually prefer the second, due to its clarity and readability. But should I, for speed sake, use the first?

 
Sorry, it's

sql_str="SELECT "+my_list+" FROM "+my_alias+ ;
" WHERE "+my_conditions+" INTO DBF "+my_table

&sql_str

vs.

SELECT &my_list ;
FROM &my_alias ;
WHERE &my_conditions ;
INTO DBF (my_table)
 
stella740pl,

I believe that in that case the macro is evaluated one time and you may as well go with maintainability and readibility.

Jim,

() works fine as long as you don't expect the value to change. & is slower because it is re-evaluated every time it is called whereas () is not.

Brian
 
() works fine as long as you don't expect the value to change. & is slower because it is re-evaluated every time it is called whereas () is not.

I'm not sure I believe this... I'll be back after some testing..

- Bill

Get the best answers to your questions -- See FAQ481-4875.
 
Yes.. Name expressions ( parentheses ) are evaluated every time. Try this example code:
Code:
* Create a cursor and fill it:
CREATE CURSOR jnk ( one N(3) )
FOR lnI = 1 TO 10
  APPEND BLANK 
  replace one WITH lnI
ENDFOR

x = SECONDS()
FOR lnI = 1 TO 200
  lcFile = 'fileNameEx'+TRANSFORM(lnI)+'.tmp'
  COPY TO (lcFile)
ENDFOR
?"With (): " + TRANSFORM(SECONDS() - x) + " seconds"

x = SECONDS()
FOR lnI = 1 TO 200
  lcFile = 'fileMacro'+TRANSFORM(lnI)+'.tmp'
  COPY TO &lcFile
ENDFOR
?"With &: " + TRANSFORM(SECONDS() - x) + " seconds"

Then check for the existance of the different "fileNameEx" files to prove that (lcFile) was evaluated every time. AND, is still faster than macro expansion.

- Bill

Get the best answers to your questions -- See FAQ481-4875.
 
Brian,

I believe that in that case the macro is evaluated one time and you may as well go with maintainability and readibility.
I guess, that's what I wanted to hear.:) Thanks.

() works fine as long as you don't expect the value to change.
What do you mean? I use variable filenames in the () in loops, and it works fine every time.

Code:
datadir="D:\My_Data\"
FOR i=1998 TO 2003
	ii=STR(i,4)
	sfx=IIF(i=2003, "old", "new")
	SELECT	* ;
		FROM [COLOR=red](datadir+"My_Table"+ii+sfx)[/color] ;
		WHERE &wkd AND &no_std AND &in_list ;
		GROUP BY 1,2,3 ORDER BY 1,2,3 ;
		INTO CURSOR tmp
	
	SELECT avg_tmp
	APPEND FROM DBF("tmp")
NEXT

Or I misunderstood you?

Stella
 
Stella,

Not sure where I picked up that notion. A quick look through the help certainly doesn't support my memory.

Either way, the helpdoes suggest using () a.k.a 'name expression' or EVALUATE() over a macro replacement whenever possible with the caveat that EVALUATE() can return an incorrect result when used in the WHERE portion of a SQL command.

Brian
 
I found where I got my idea that *something* didn't get re-evaluated and it turns out that:

Macro substitution statements that appear in DO WHILE, FOR, and SCAN are evaluated only at the start of the loop and are not reevaluated on subsequent iterations. Any changes to the variable or array element that occur within the loop are not recognized.
 
Here is an old article:

Difference Between Macro Substitution and Named Expression
The FoxPro language compiler parses the program and replaces a name expression with the value during the first pass; from that point on, only the value, not the variable, is used. The FoxPro compiler leaves a macro substitution as a reference to the variable that is not resolved until the program is executed.

and another:
Macro Substitution, Name Expressions, and EVALUATE() Run-Time Evaluation
The only situation where macro substitution is still required is when the expression to evaluate contains the command to be executed, in whole or in part,...

 
Craig,

Check this out.

Code:
lcTest=[1+playerno]

create table entrylist (Entryno I(3),  Playerno I(3),  Weight N(4,1),   Date  D(8))

insert into entrylist values (1, 1, 4,   {09/25/04})
insert into entrylist values (1, 2, 3.8, {09/25/04})
insert into entrylist values (2, 5, 4.2, {09/25/04})
insert into entrylist values (2, 7, 4.4, {09/25/04})
insert into entrylist values (3, 4, 4,   {09/25/04})

SELECT * from entrylist WHERE EVALUATE(lcTest)=2

SELECT entrylist 
LOCATE FOR EVALUATE(lcTest)=2
SELECT * from entrylist WHERE EVALUATE(lcTest)=2
 
baltman,

Very Nice! Star worthy. I tried a number of different datatypes and removing the "1 +" from the expression evaluated...none of which made it work any different. It appears that evaluate is stuck on whatever record the table is on when the select statement is run...consider the following:

Code:
lcTest=[entrylist.playerno]

create table entrylist (Entryno I(3),  Playerno I(3),  Weight N(4,1),   Date  D(8))

insert into entrylist values (1, 1, 4,   {09/25/04})
insert into entrylist values (1, 2, 3.8, {09/25/04})
insert into entrylist values (2, 5, 4.2, {09/25/04})
insert into entrylist values (2, 7, 4.4, {09/25/04})
insert into entrylist values (3, 4, 4,   {09/25/04})

SELECT * from entrylist WHERE EVALUATE(lcTest)=4 && Records returned
SELECT * from entrylist WHERE EVALUATE(lcTest)=1 && No Records returned

GO TOP IN "entrylist"

SELECT * from entrylist WHERE EVALUATE(lcTest)=4 && No Records returned
SELECT * from entrylist WHERE EVALUATE(lcTest)=1 && Records returned

I may be back to this...I'm not done checking this out.

boyd.gif

 
Another bit of oddness this way comes:
Code:
create table entrylist (Entryno I(3),  Playerno I(3),  Weight N(4,1),   Date  D(8))

insert into entrylist values (1, 1, 4,   {09/25/04})
insert into entrylist values (1, 2, 3.8, {09/25/04})
insert into entrylist values (2, 5, 4.2, {09/25/04})
insert into entrylist values (2, 7, 4.4, {09/25/04})
insert into entrylist values (3, 4, 4,   {09/25/04})

lcTest=[playerno]
SELECT * from entrylist WHERE EVALUATE(lcTest) = 4 && One Record returned

lcTest=[[COLOR=red]entrylist[/color].playerno]
SELECT * from entrylist WHERE EVALUATE(lcTest) = 4 && All Records returned

boyd.gif

 
Craig,

In your last example the active record is the last one where EVALUATE(lcTest) = 4 = .t. I think it is equivalent to SELECT * from entrylist WHERE .t. or .f. depending on active record.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top