Offhand I would say definitely, a built-in function is going to be faster than a user-defined function doing the same thing. Then again,
.ISNULL() actually handles a couple of cases that yours doesn't (i.e. the case where the argument equals
.NULL and the case where the argument is a string, possibly containing blanks.) But for what it's worth, here's a test program to measure it ...
! test performance of .isempty(x) vs a function
define ISNULL_TRUE = -1
map1 usecs,b,4
map1 x,x,0
map1 i,f
map1 b,b,1
x = fill(chr(0),30)
xcall TIMES,3,usecs
for i = 1 to 1000000
b = fn'isnull(x)
next i
xcall TIMES,3,usecs
? "fn'isnull(x) -- "; usecs/1000000; " usecs per call"
xcall TIMES,3,usecs
for i = 1 to 1000000
b = .isempty(x)
next i
xcall TIMES,3,usecs
? ".isempty(x) -- "; usecs/1000000; " usecs per call"
end
FUNCTION fn'isnull(var as x0:INPUTONLY) as b1
IF (var = fill$(CHR(0),LEN(var))) THEN
fn'isnull = ISNULL_TRUE
ENDIF
ENDFUNCTION
On my laptop, the results are as follows (and it's pretty consistent, within a range of +/- 10%) ...
.run isemptyx
1 million fn'isnull(x) -- 2.35262
1 million .isempty(x) -- 0.465129
So as expected, the built-in function is faster (about 5X), even though it's actually more complex. On the other hand, even the slow poke here is only burning up a couple of microseconds per call (and that's including the overhead of the FOR/NEXT loop and the XCALL to measure the time).
Just for kicks I re-arranged the loops so they performed the function call 10 times in line for each loop iteration, instead of once, which in theory should reduce the FOR/NEXT overhead 10 fold, and that shaved about 3 tenths of a microsecond off the above values, i.e.
.run isemptyx2
fn'isnull(x) -- 2.06697 usecs per call
.isempty(x) -- 0.191827 usecs per call
So the real differential appears to more like 10X!