 |
BorlandTalk.com Borland discussion newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Gary Guest
|
Posted: Fri May 13, 2005 10:17 pm Post subject: PAS loop faster than BASM |
|
|
I created a BASM function that searches through an array of integers for
a value. To my surprise, it benchmarks at about 10 times slower than
the equivalent PAS version. Why is it so slow?
{----------------------------------------------------------
Searches an array of integers (or pointers) for a value.
Returns the position of the value, or -1 if not found.
On entry, EAX will contain P, EDX will contain Value,
and ECX will contain Count.
-----------------------------------------------------------}
function ScanArray(const P : Pointer;
const Value : Integer;
const Count : Integer) : Integer;
asm
jcxz @@Done // check for Count=0
// The following MUST be done in this order,
// since the parameters are actually in registers.
push EDI // must preserve EDI
mov EDI, P
mov EAX, Value
mov EDX, Count // remember the count for later
repne scasd // search!
jnz @@NotFound // success?
mov EAX, EDX // get the original count
sub EAX, ECX // subtract the end count
dec EAX // subtract 1 more for origin 0
pop EDI
ret
@@NotFound:
mov EAX, -1 // not found
pop EDI
@@Done:
end;
{ Pascal version }
function ScanArrayPAS(const P : array of integer;
const Value : Integer;
const Count : Integer) : Integer;
var
i : integer;
begin
Result := -1;
for i := Count - 1 downto 0 do
if P[i] = Value then
begin
Result := i;
break;
end;
end;
|
|
| Back to top |
|
 |
Rudy Velthuis [TeamB] Guest
|
Posted: Fri May 13, 2005 10:38 pm Post subject: Re: PAS loop faster than BASM |
|
|
At 00:17:55, 14.05.2005, Gary wrote:
| Quote: | I created a BASM function that searches through an array of integers
for a value. To my surprise, it benchmarks at about 10 times slower
than the equivalent PAS version. Why is it so slow?
|
Why don't you have a look at the code produced by the PurePascal loop in
the CPU window? <g>
--
Rudy Velthuis [TeamB]
"The most important job is not to be Governor, or First Lady in my
case." -- George W. Bush
|
|
| Back to top |
|
 |
Martin James Guest
|
Posted: Fri May 13, 2005 10:38 pm Post subject: Re: PAS loop faster than BASM |
|
|
Dunno. REPNZ SCASD should be plenty fast.
Why not look at the assembly in the Delphi CPU window, find out & tell us?
Rgds,
Martin :)
|
|
| Back to top |
|
 |
Gary Guest
|
Posted: Fri May 13, 2005 10:40 pm Post subject: Re: PAS loop faster than BASM |
|
|
The code produced by the PAS function is big and ugly. I can't imagine
why it's faster. Is there a way to copy it to the clipboard from the
CPU window?
Rudy Velthuis [TeamB] wrote:
| Quote: | At 00:17:55, 14.05.2005, Gary wrote:
I created a BASM function that searches through an array of integers
for a value. To my surprise, it benchmarks at about 10 times slower
than the equivalent PAS version. Why is it so slow?
Why don't you have a look at the code produced by the PurePascal loop in
the CPU window?
|
|
|
| Back to top |
|
 |
Rudy Velthuis [TeamB] Guest
|
Posted: Fri May 13, 2005 10:45 pm Post subject: Re: PAS loop faster than BASM |
|
|
At 00:40:52, 14.05.2005, Gary wrote:
| Quote: | The code produced by the PAS function is big and ugly. I can't imagine
why it's faster. Is there a way to copy it to the clipboard from the
CPU window?
|
Yes, but only line by line.
--
Rudy Velthuis [TeamB]
"No mention of God. They keep Him up their sleeves for as long as
they can, vicars do. They know it puts people off." -- Alan Bennett.
|
|
| Back to top |
|
 |
Rudy Velthuis [TeamB] Guest
|
Posted: Fri May 13, 2005 10:46 pm Post subject: Re: PAS loop faster than BASM |
|
|
At 00:40:52, 14.05.2005, Gary wrote:
| Quote: | The code produced by the PAS function is big and ugly. I can't imagine
why it's faster.
|
Probably because the looping part is faster?
--
Rudy Velthuis [TeamB]
"Everybody wants to go to heaven, but nobody wants to die."
-- Joe Louis.
|
|
| Back to top |
|
 |
Gary Guest
|
Posted: Fri May 13, 2005 10:59 pm Post subject: Re: PAS loop faster than BASM |
|
|
Rudy Velthuis [TeamB] wrote:
| Quote: | At 00:40:52, 14.05.2005, Gary wrote:
The code produced by the PAS function is big and ugly. I can't imagine
why it's faster.
Probably because the looping part is faster?
|
{ begin }
push ebp
mov ebp,esp
add esp,-$14
mov [ebp-$0c],ecx
mov [ebp-$08],edx
mov [ebp-$04],eax
{ Result := -1; }
mov [ebp-$10],$ffffffff
{for i := Count - 1 downto 0 do }
mov eax,[ebp+$08]
sub eax,$01
jno +$05
call @IntOver
cmp eax,$00
jl +$30
mov [ebp-$14],eax
{ if P[i] = Value then }
mov eax,[ebp-$14]
test eax,eax
jl +$05
cmp eax,[ebp-$08]
jle +$05
call @BoundErr
mov edx,[ebp-$04]
mov eax,[edx+eax*4]
cmp eax,[ebp-$0c]
jnz +$08
{ Result := i; }
mov eax,[ebp-$14]
mov [ebp-$10],eax
{ break; }
jmp +$09
{ end; }
dec dword ptr [ebp-$14]
{ for i := Count - 1 downto 0 do }
cmp dword ptr [ebp-$14],-$01
jnz -$2d
mov eax,[ebp-$10]
{ end; }
mov esp,ebp
pop ebp
ret $0004
|
|
| Back to top |
|
 |
Pierre le Riche Guest
|
Posted: Sat May 14, 2005 7:42 am Post subject: Re: PAS loop faster than BASM |
|
|
Hi Gary,
| Quote: | a value. To my surprise, it benchmarks at about 10 times slower than the
equivalent PAS version. Why is it so slow?
|
It's because you're using repne scasd and jcxz. The string manipulation
instructions are quite slow on the modern CPUs and jcxz is slower than using
a "test ecx, ecx; jz xxxx". This will be faster:
function ScanArray(P: Pointer; Value, Count: Integer): Integer;
asm
{On entry: eax = P, edx = Value, ecx = Count}
@CompareLoop:
sub ecx, 1
js @NotFound
cmp [eax + ecx * 4], edx
jne @CompareLoop
mov eax, ecx
ret
@NotFound:
mov eax, -1
end;
Regards,
Pierre
|
|
| Back to top |
|
 |
Pierre le Riche Guest
|
Posted: Sat May 14, 2005 7:59 am Post subject: Re: PAS loop faster than BASM |
|
|
Hi Again,
You can make it even shorter if you change the order of parameters and
change the definition of the return value from "-1 if not found" to
"negative if the value is not found". The return value will be -1 for all 0
or positive values of ACount, but will be (ACount - 1) if ACount was
negative on entry. Here goes:
{Scans a list of integers starting at address AFirstItem for ASearchValue,
returning the index of item if it was found or < 0 if not}
function ScanArray(ACount, ASearchValue: Integer; AFirstItem: Pointer):
Integer;
asm
{On entry: eax = ACount, edx = ASearchValue, ecx = @AFirstItem}
@CompareLoop:
sub eax, 1
js @Done
cmp [ecx + eax * 4], edx
jne @CompareLoop
@Done:
end;
Regards,
Pierre
|
|
| Back to top |
|
 |
Robert Houdart Guest
|
Posted: Sat May 14, 2005 10:24 am Post subject: Re: PAS loop faster than BASM |
|
|
| Quote: | function ScanArray(ACount, ASearchValue: Integer; AFirstItem: Pointer):
Integer;
asm
{On entry: eax = ACount, edx = ASearchValue, ecx = @AFirstItem}
@CompareLoop:
sub eax, 1
js @Done
cmp [ecx + eax * 4], edx
jne @CompareLoop
@Done:
end;
|
That's what I would call elegant code!
For very large arrays the "sentinel" approach can be useful: you start by
putting the value you're looking for at the end of the array.
That way you've got only one compare/conditional jump in the loop.
Robert
|
|
| Back to top |
|
 |
Gary Guest
|
Posted: Sat May 14, 2005 12:55 pm Post subject: Re: PAS loop faster than BASM |
|
|
Wonderful, thank you!
I found that my original tests were not using truly random samples, and
since the ASM code searched forward through the array while the PAS
version searched backward through the array, that accounted for the huge
difference. When my samples were more random, the timings were closer,
but the PAS version was still faster. Your version is the fastest.
I wasn't aware that the string instructions were so slow.
Thanks!
....Gary
|
|
| Back to top |
|
 |
Gary Guest
|
Posted: Sat May 14, 2005 1:32 pm Post subject: Re: PAS loop faster than BASM |
|
|
I'm finding that I have to check the count before calling the ASM
routine because passing the address of the first element generates an
exception if the array is empty. For example, the following will
generate an exception if MyArray has no elements:
Result := ScanArray(Length(MyArray), Value, @MyArray[0]);
So I find that I have to do this, even though ScanArray would safely
handle it:
if Length(MyArray) > 0 then
Result := ScanArray(Length(MyArray), Value, @MyArray[0])
else
Result := -1;
Is there a simpler way to accomplish this? Do I have to turn off range
checking?
|
|
| Back to top |
|
 |
Lee_Nover Guest
|
Posted: Sat May 14, 2005 2:09 pm Post subject: Re: PAS loop faster than BASM |
|
|
Result := ScanArray(Length(MyArray), Value, @MyArray);
should work as well
|
|
| Back to top |
|
 |
Gary Guest
|
Posted: Sat May 14, 2005 2:13 pm Post subject: Re: PAS loop faster than BASM |
|
|
Does that work with dynamic arrays, as well?
Lee_Nover wrote:
| Quote: | Result := ScanArray(Length(MyArray), Value, @MyArray);
should work as well
|
|
|
| Back to top |
|
 |
Pierre le Riche Guest
|
Posted: Sat May 14, 2005 4:50 pm Post subject: Re: PAS loop faster than BASM |
|
|
Hi Gary,
| Quote: | Does that work with dynamic arrays, as well?
|
The code you're looking for is:
Result := ScanArray(length(MyArray), Value, Pointer(MyArray))
Long string and dynamic array references are effectively pointers to the
first character or element, so typecasting it to a pointer will avoid the
range check error you get when using @MyArray[0] with a zero length array.
Regards,
Pierre
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|