🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Thread View: borland.public.delphi.language.basm
32 messages
32 total messages Started by Bram Bos Wed, 06 Aug 2008 06:09
WAIT... why?
#6
Author: Bram Bos
Date: Wed, 06 Aug 2008 06:09
29 lines
1756 bytes
There are a couple of lines in my code that get executed almost 400,000 times per second - so they are good candidates for optimization.

Looking at the code generated by Delphi 5 in the CPU window I see this:

//lbuffer^ := lbuffer^ + leftvol * l;
fld    qword ptr [ebp-$28]
fmul   qword ptr [ebp-$38]
fadd   qword ptr [edi]
fstp   qword ptr [edi]
wait
//inc( lbuffer );
add    edi, $08
//rbuffer^ := rbuffer^ + rightvol * l;
fld    qword ptr [ebp-$30]
fmul   qword ptr [ebp-$38]
mov    eax, [ebp-$20]
fadd   qword ptr [eax]
mov    eax, [ebp-$20]
fstp   qword ptr [eax]
wait
//inc( rbuffer );
add    qword ptr [ebp-$20], $08

I notice there is a number of WAIT instructions in there. Why? It seems like a waste of time. But obviously Borland had a good reason to put them there...

Also, I guess I can speed up the code above a little bit by using FPU registers and local variables?

Thanks for any enlightened insights.
Re: WAIT... why?
#9
Author: Dan Downs
Date: Wed, 06 Aug 2008 11:13
39 lines
2314 bytes
Bram Bos wrote:
> There are a couple of lines in my code that get executed almost 400,000 times per second - so they are good candidates for optimization.
> 
> Looking at the code generated by Delphi 5 in the CPU window I see this:
> 
> //lbuffer^ := lbuffer^ + leftvol * l;
> fld    qword ptr [ebp-$28]
> fmul   qword ptr [ebp-$38]
> fadd   qword ptr [edi]
> fstp   qword ptr [edi]
> wait
> //inc( lbuffer );
> add    edi, $08
> //rbuffer^ := rbuffer^ + rightvol * l;
> fld    qword ptr [ebp-$30]
> fmul   qword ptr [ebp-$38]
> mov    eax, [ebp-$20]
> fadd   qword ptr [eax]
> mov    eax, [ebp-$20]
> fstp   qword ptr [eax]
> wait
> //inc( rbuffer );
> add    qword ptr [ebp-$20], $08
> 
> I notice there is a number of WAIT instructions in there. Why? It seems like a waste of time. But obviously Borland had a good reason to put them there...
> 
> Also, I guess I can speed up the code above a little bit by using FPU registers and local variables?
> 
> Thanks for any enlightened insights.
> 

I might be missing something but why is are you multiplying by 1? I 
would think removing that would speed things up. FPU ASM is way out of 
my league, but I thought x86 32bit isa used a stack based FPU so I don't 
know if there are registers per say, you'd have to convert it to SSE.

-- 
DD
  - Always assume I'm posting without coffee.
Re: WAIT... why?
#10
Author: Bram Bos
Date: Wed, 06 Aug 2008 11:53
30 lines
2175 bytes
Dan Downs <ddowns@nospam.online-access.com> wrote:
>Bram Bos wrote:
>> There are a couple of lines in my code that get executed almost 400,000 times per second - so they are good candidates for optimization.
>> 
>> Looking at the code generated by Delphi 5 in the CPU window I see this:
>> 
>> //lbuffer^ := lbuffer^ + leftvol * l;
[snip]
>> //rbuffer^ := rbuffer^ + rightvol * l;

Thanks for having a look at it.

>I might be missing something but why is are you multiplying 
>by 1? 

I'm actually multiplying by "L", which is a single float variable in this procedure containing a sampled value between -1.0 and +1.0, but apparently the lowercase L looks very similar to a "1" in some fonts ;-)

>FPU ASM is way out of my league, but I thought x86 32bit isa 
>used a stack based FPU so I don't know if there are 
>registers per say, 

The x86 FPU uses a stack indeed, but you can still access the 8 items at the top of the stack individually, like registers, as St(0) .. St(7)

>you'd have to convert it to SSE.

I wish I could - that would have major potential in this situation - however Delphi 5 doesn't support SSE (nor MMX, but there's a handy little byte-code converter available for MMX).

Thanks,
Bram
Re: WAIT... why?
#11
Author: John Herbster
Date: Wed, 06 Aug 2008 12:22
13 lines
1354 bytes
"Bram Bos" <buramu@gmail.com> wrote
> There are a couple of lines in my code that get executed
> almost 400,000 times per second ...
>   Lbuffer^ := Lbuffer^ + leftvol * L;
>   inc( Lbuffer );
>   Rbuffer^ := Rbuffer^ + rightvol * L;
>   inc( Rbuffer );

Bram, 
If you could show us some of the surrounding code, 
it would help.
Rgds, JohnH
Re: WAIT... why?
#12
Author: Bram Bos
Date: Wed, 06 Aug 2008 12:52
54 lines
2517 bytes
"John Herbster" <herb-sci1_AT_sbcglobal.net> wrote:

>If you could show us some of the surrounding code, 
>it would help.

Hi John, 

I've simplified the code a bit, but here is the core of the 
procedure. Basically it reads a sample from a sound lookup 
table, applies volumes to it and stores it in 2 separate 
(left/right) buffers. 

All samples are "single" values, ranging from -1 to +1...
The two lines I singled out earlier are (according to the profiler) using by far the most CPU time in my entire project, so any performance gain would be useful.


Procedure getSamplesFromWavetable( leftbuf, rightbuf : pointer; numsamples : integer );
var
  lbuffer, rbuffer : ^single;
  n : integer;
  sample, leftvol, rightvol : single;
  idx : integer;
begin

  Lbuffer := leftbuf;
  Rbuffer := rightbuf;

  //determine the volume of the left audio channel
  leftvol := getLeftVolume();    
  //determine the volume of the right channel
  rightvol := getRightVolume();  

  for n := 0 to numSamples - 1 do
    begin
       
      // get an index into a lookup table containing 
      // e.g. a sinewave or a sawtooth. This part has already 
      // been nicely optimized using fixed point arithmatic
      idx := getNextIndexIntoWavetable();
 
      // get a sample from the wavetable
      sample := oscWavetable[idx];      

      // now apply volume adjustments to this sample and 
      // store in separate left/right audio buffers
      Lbuffer^ := Lbuffer^ + leftvol * sample;
      inc( Lbuffer );
      Rbuffer^ := Rbuffer^ + rightvol * sample;
      inc( Rbuffer );

    end;

end;
Re: WAIT... why?
#13
Author: Jens Gruschel
Date: Wed, 06 Aug 2008 13:26
21 lines
1729 bytes
> //lbuffer^ := lbuffer^ + leftvol * l;
> fld    qword ptr [ebp-$28]
> fmul   qword ptr [ebp-$38]
> fadd   qword ptr [edi]
> fstp   qword ptr [edi]
> wait
> //inc( lbuffer );
> add    edi, $08

[...]

> I notice there is a number of WAIT instructions in there. Why? It seems like a waste of time.

"wait" is necessary to synchronize the FPU with the CPU. When the 80x87 
was introduced both were on separate chips working separately and in 
parallel. The "wait" in the code above makes sure edi is not used by the 
CPU before the FPU has stored the result to this register. Also see 
http://www.website.masmforum.com/tutorials/fptute/fpuchap3.htm#fwait

Not sure whether wait is still necessary today where CPU and FPU are 
integrated on a single chip. Does anybody know?
Re: WAIT... why?
#14
Author: Jens Gruschel
Date: Wed, 06 Aug 2008 13:40
6 lines
1345 bytes
> Not sure whether wait is still necessary today where CPU and FPU are 
> integrated on a single chip. Does anybody know?

According to http://faydoc.tripod.com/cpu/fwait.htm "wait" is also used 
for the FPU exceptions. Anyway, "sync" probably would have been a better 
name for it :-)
Re: WAIT... why?
#15
Author: Bram Bos
Date: Wed, 06 Aug 2008 13:47
12 lines
1309 bytes
Jens Gruschel <nospam@thisurldoesnotexist.com> wrote:

>Anyway, "sync" probably would have been a better name for it :-)

Ah.. that makes sense.. at least it makes the instruction sound a little bit more useful than "wait" :-)

I'll try and figure out whether it's still essential to have 
critical code littered with waits. I consider the PIII my
bottom line CPU, and that one definitely had the FPU integrated into the CPU.

Thanks for the info!
Re: WAIT... why?
#16
Author: Jens Gruschel
Date: Wed, 06 Aug 2008 14:11
10 lines
1427 bytes
> I consider the PIII my
> bottom line CPU, and that one definitely had the FPU integrated into the CPU.

On the 80286 it was not integrated yet, on the 80486 it was (at least 
the DX). Not sure about the 80386 (I never had one, I switched from 286 
to 486).

-- 
Jens Gruschel
http://www.pegtop.net
Re: WAIT... why?
#17
Author: Dan Downs
Date: Wed, 06 Aug 2008 14:14
25 lines
1963 bytes
Bram Bos wrote:
> Dan Downs <ddowns@nospam.online-access.com> wrote:
>> Bram Bos wrote:
>>> There are a couple of lines in my code that get executed almost 400,000 times per second - so they are good candidates for optimization.
>>>
>>> Looking at the code generated by Delphi 5 in the CPU window I see this:
>>>
>>> //lbuffer^ := lbuffer^ + leftvol * l;
> [snip]
>>> //rbuffer^ := rbuffer^ + rightvol * l;
> 
> Thanks for having a look at it.
> 
>> I might be missing something but why is are you multiplying 
>> by 1? 
> 
> I'm actually multiplying by "L", which is a single float variable in this procedure containing a sampled value between -1.0 and +1.0, but apparently the lowercase L looks very similar to a "1" in some fonts ;-)

Doh! Actually 1 and l look almost the same on my system, the one is a 
pixel shorter and a touch less blurry.


Are leftvol and rightvol constants in the loop?

DD
Re: WAIT... why?
#18
Author: Dan Downs
Date: Wed, 06 Aug 2008 14:15
6 lines
1279 bytes
> Are leftvol and rightvol constants in the loop?

I should have looked at the other posts first, never mind.
-- 
DD
  - Always assume I'm posting without coffee.
Re: WAIT... why?
#20
Author: Dan Downs
Date: Wed, 06 Aug 2008 14:17
15 lines
1488 bytes
Jens Gruschel wrote:
>> I consider the PIII my
>> bottom line CPU, and that one definitely had the FPU integrated into 
>> the CPU.
> 
> On the 80286 it was not integrated yet, on the 80486 it was (at least 
> the DX). Not sure about the 80386 (I never had one, I switched from 286 
> to 486).
> 

386 was still external, I had a math co-processor.

-- 
DD
  - Always assume I'm posting without coffee.
Re: WAIT... why?
#19
Author: Dan Downs
Date: Wed, 06 Aug 2008 14:27
15 lines
1637 bytes
Depending on how getNextIndexIntoWavetable() works it looks like you 
could split the loop into two threads. Left & right channel threads and 
wait for both before returning, that mixed with any possible fpu 
improvements the others come up with could add up to quite a bit. It 
would depend on numsamples being large enough to warrant using threads 
though. If its more of a real time problem then some sort of threaded 
pipeline/queuing structure might be better.

I've been following http://17slon.com/blogs/gabr/blogger.html with much 
interest lately.


-- 
DD
  - Always assume I'm posting without coffee.
Re: WAIT... why?
#21
Author: Bob Gonder
Date: Wed, 06 Aug 2008 14:58
25 lines
1663 bytes
Bram Bos wrote:

>      // now apply volume adjustments to this sample and 
>      // store in separate left/right audio buffers
>      Lbuffer^ := Lbuffer^ + leftvol * sample;
>      inc( Lbuffer );
>      Rbuffer^ := Rbuffer^ + rightvol * sample;
>      inc( Rbuffer );

Change to
      Lbuffer^ := Lbuffer^ + leftvol * sample;
      Rbuffer^ := Rbuffer^ + rightvol * sample;
      inc( Lbuffer );
      inc( Rbuffer );

Will eliminate the first wait.
But I don't think it will speed up very much.

As to Why the Wait...If you didn't wait,
edi would be changed to edi+8 before
the fstp[edi] got around to executing.

This might be a good candidate for threadng.
One thread for each channel on a multi-cpu
chip should be twice as fast.
Re: WAIT... why?
#22
Author: Dennis Passmore
Date: Wed, 06 Aug 2008 17:24
1 lines
916 bytes
check your email
Re: WAIT... why?
#25
Author: Carl Caulkett
Date: Wed, 06 Aug 2008 17:55
14 lines
1054 bytes
Bram Bos wrote:

> 
> There are a couple of lines in my code that get executed almost
> 400,000 times per second - so they are good candidates for
> optimization.

Hi, are you the Bram Bos of Hammerhead and Tuareg fame? Although I'm
more of an FL Studio man these days, I had fun with Hammerhead when it
first came out.

-- 
Cheers,
Carl
Re: WAIT... why?
#23
Author: Derek Jones
Date: Wed, 06 Aug 2008 18:01
14 lines
1544 bytes
"Jens Gruschel" <nospam@thisurldoesnotexist.com> wrote in message 
news:4899a627>
>
> Not sure whether wait is still necessary today where CPU and FPU are 
> integrated on a single chip. Does anybody know?

Not according to the Intel IA-32 docs. It's only use these days is to 
syncronize floating point exceptions. And if you look at where the Delphi 
compiler places them it is after each source line, so that if an exception 
is generated, it is handled and flagged for the correct line.

If you code by hand and omit the waits, you will often get the exception 
some instructions later than where it actualy occured.
Re: WAIT... why?
#24
Author: Derek Jones
Date: Wed, 06 Aug 2008 18:05
15 lines
1553 bytes
"Bob Gonder" <none@nowhere.invalid> wrote in message 
news:9aej94dup0sk6ro0ir4kcmc0lfkbup9ffg@4ax.com...
>
> As to Why the Wait...If you didn't wait,
> edi would be changed to edi+8 before
> the fstp[edi] got around to executing.

These days it's only use is to sync floating point exceptions. If you look 
at where they are placed by the compiler, it is at the end of each source 
line, so the debugger can flag the correct source line should an exception 
occur.

The Intel IA32 docs state that it is for exception syncronization, nothing 
else.
Re: WAIT... why?
#26
Author: Bram Bos
Date: Wed, 06 Aug 2008 18:44
29 lines
1831 bytes
"Carl Caulkett" <carlca@dircon.co.uk> wrote:
>Bram Bos wrote:
>
>> 
>> There are a couple of lines in my code that get executed almost
>> 400,000 times per second - so they are good candidates for
>> optimization.
>
>Hi, are you the Bram Bos of Hammerhead and Tuareg fame? Although I'm
>more of an FL Studio man these days, I had fun with Hammerhead when it
>first came out.

Hi Carl,

Yes.. that's me. Funny you remember my name. 
I made Hammerhead over 10 years ago! Incidentally that was 
my first real Delphi application ;-) 

I'm currently working on a freeware all-in-one music
application for tiny netbooks, like the Asus EEE PC.
These typically have small screens and slow, last-gen CPUs so
that's why I need to optimize my synth and mix code as much as
possible (my own EEE PC has an underclocked Celeron M @ 630MHz 
so that explains why I'm interested in assembler all of a 
sudden).

Cheers,
Bram
Re: WAIT... why?
#27
Author: Jens Gruschel
Date: Wed, 06 Aug 2008 21:02
15 lines
1479 bytes
> Yes.. that's me. Funny you remember my name. 
> I made Hammerhead over 10 years ago! Incidentally that was 
> my first real Delphi application ;-) 

Hammerhead was great, one of the first applications I had when I 
switched from DOS to Windows. Thanks for that!

> I'm currently working on a freeware all-in-one music
> application for tiny netbooks, like the Asus EEE PC.

Sounds interesting. Let us know when it's done :-)

-- 
Jens Gruschel
http://www.pegtop.net
Re: WAIT... why?
#29
Author: Dennis Christensen
Date: Thu, 07 Aug 2008 06:41
41 lines
2176 bytes
Hi

> I notice there is a number of WAIT instructions in there. Why? It seems 
> like a waste of time. But obviously Borland had a good reason to put them 
> there...

WAIT is not a sleep.

> Also, I guess I can speed up the code above a little bit by using FPU 
> registers and local variables?

The WAIT instruction = FWAIT takes 0 clock cycles to execute on modern 
processors. Do not worry about them.

> Thanks for any enlightened insights.

http://download.intel.com/design/pentium/MANUALS/24319101.PDF
page 485
Description

Causes the processor to check for and handle pending, unmasked, 
floating-point exceptions

before proceeding. (FWAIT is an alternate mnemonic for the WAIT).

This instruction is useful for synchronizing exceptions in critical sections 
of code. Coding a

WAIT instruction after a floating-point instruction insures that any 
unmasked floating-point

exceptions the instruction may raise are handled before the processor can 
modify the instruction's

results. See the section titled "Floating-Point Exception Synchronization" 
in Chapter 7 of

the Intel Architecture Software Developer's Manual, Volume 1, for more

Regards
Dennis
Re: WAIT... why?
#28
Author: Dennis Christensen
Date: Thu, 07 Aug 2008 06:46
13 lines
1313 bytes
Hi Derek

I agree

I have often tried removing FWAIT instructions in BASM code and I have never 
been able to measure any difference in speed.

They are needed after FP instructions that can raise exceptions if one wants 
the exception at the rigth line. If one does not care then having one at the 
end of a function is enough.

Regards
Dennis 
Re: WAIT... why?
#31
Author: Dennis Christensen
Date: Thu, 07 Aug 2008 06:47
6 lines
1084 bytes
Hi

Why care about anything older than P4 these days?

Regards
Dennis
Re: WAIT... why?
#33
Author: David J Taylor
Date: Thu, 07 Aug 2008 06:57
13 lines
1550 bytes
Dennis Christensen wrote:
> Hi
>
> Why care about anything older than P4 these days?
>
> Regards
> Dennis

I have at least two production real-time data capture systems still 
running Pentium III with Windows 2000, and no intention to replace them 
any time soon.

David 
Re: WAIT... why?
#30
Author: Bram Bos
Date: Thu, 07 Aug 2008 06:58
9 lines
1272 bytes
Jens Gruschel <nospam@thisurldoesnotexist.com> wrote:

>> I'm currently working on a freeware all-in-one music
>> application for tiny netbooks, like the Asus EEE PC.
>
>Sounds interesting. Let us know when it's done :-)

I will and I definitely need to credit the people in this group in my documentation, because I've already learned an awful lot just reading through the archives of this newsgroup.
Re: WAIT... why?
#32
Author: Bram Bos
Date: Thu, 07 Aug 2008 07:02
9 lines
1589 bytes
"Dennis Christensen" <dkc@GateHouse.dk> wrote:
>Hi
>
>Why care about anything older than P4 these days?

For this particular project (as I explained further down the thread) I'm writing specifically for ultraportable netbooks like the Asus EEE PC. The basic model uses an old Celeron M (Dothan-900) running underclocked at 630MHz. So it's basically a slowed down Pentium III with some P4 sauce. 

These are still far from being obsolete.
Re: WAIT... why?
#35
Author: thaddy
Date: Thu, 07 Aug 2008 08:12
8 lines
1223 bytes
De compiler has no way of knowing you are doing audio and that it can safely ignore de fpu state in most cases.
You can set the FPU state to ignore floating point exceptions. In that case the wait will take no cycles. 

btw: I have some old BASM code that does the same as you do here:
http://thaddy.co.uk/tdkdsplib.zip

Maybe you can use it.
Re: WAIT... why?
#36
Author: Bram Bos
Date: Thu, 07 Aug 2008 08:20
13 lines
1416 bytes
"thaddy" <thaddy@thaddy.com> wrote:
>
>De compiler has no way of knowing you are doing audio and that it can safely ignore de fpu state in most cases.
>You can set the FPU state to ignore floating point exceptions. In that case the wait will take no cycles. 
>
>btw: I have some old BASM code that does the same as you do here:
>http://thaddy.co.uk/tdkdsplib.zip
>
>Maybe you can use it.

Thanks.. it looks like I should give the AdjustVolume32f
procedure a good closer look. Very helpful!
Re: WAIT... why?
#37
Author: Q Correll
Date: Thu, 07 Aug 2008 16:42
14 lines
1184 bytes
Dennis,

| The WAIT instruction = FWAIT takes 0 clock cycles to execute on modern 
processors. Do not worry about them.

How about the instruction decode cycle?  How does the cpu know it's a WAIT 
until the instruction is read?

-- 
    Q

08/07/2008 09:41:26

XanaNews Version 1.18.1.11  [Leonel's & Pieter's & Q's Mods]
Re: WAIT... why?
#38
Author: Bob Gonder
Date: Thu, 07 Aug 2008 18:02
12 lines
1225 bytes
Dennis Christensen wrote:

>The WAIT instruction = FWAIT takes 0 clock cycles to execute on modern 
>processors. Do not worry about them.

Hmmm.. My 3GHz Quad runs (using GetTickCount() )
 10 million 16-nop loop in 31
 10 million 16-wait loop in 62

My 2GHz Celeron laptop in 50/90

So it looks like it takes twice as long as the 0-cycle nop :->
Re: WAIT... why?
#39
Author: Hubert Seidel
Date: Sat, 09 Aug 2008 18:14
28 lines
1902 bytes
Hello Bram,

"Bram Bos" <buramu@gmail.com> schrieb im Newsbeitrag
news:4899402f$1@newsgroups.borland.com...
>
> There are a couple of lines in my code that get executed almost 400,000
times per second - so they are good candidates for optimization.
> Looking at the code generated by Delphi 5 in the CPU window I see this:
..
> Also, I guess I can speed up the code above a little bit by using FPU
registers and local variables?

If you have an example for us that i can compile and run, then i can try to
supporting.
We had success "I need the fastest routine"
news:486e9cec@newsgroups.borland.com
e.g. e small projekt with a procedure than fills the array (sin/cos) and a
procedure with the main-function.
(I have Delphi 5 too)

mfg.
Herby

P.S:
Sorry, my english,...i hope yor understand me :-)

-- 
http://www.hubert-seidel.de
Re: WAIT... why?
#40
Author: Hubert Seidel
Date: Sat, 09 Aug 2008 19:53
59 lines
2807 bytes
Hello Bram,

"Bram Bos" <buramu@gmail.com> schrieb im Newsbeitrag
news:48999e7b$1@newsgroups.borland.com...
...
> Procedure getSamplesFromWavetable( leftbuf, rightbuf : pointer; numsamples
: integer );
> var
>   lbuffer, rbuffer : ^single;
>   n : integer;
>   sample, leftvol, rightvol : single;
>   idx : integer;
> begin
>
>   Lbuffer := leftbuf;
>   Rbuffer := rightbuf;
>
>   //determine the volume of the left audio channel
>   leftvol := getLeftVolume();
>   //determine the volume of the right channel
>   rightvol := getRightVolume();
>
>   for n := 0 to numSamples - 1 do
>     begin
>
>       // get an index into a lookup table containing
>       // e.g. a sinewave or a sawtooth. This part has already
>       // been nicely optimized using fixed point arithmatic
###
>       idx := getNextIndexIntoWavetable();
###
What do this function?
It is possible that this is the bottleneck, i don't know without to see the
code..
>
>       // get a sample from the wavetable
>       sample := oscWavetable[idx];
###
I think move the code from getNextIndexIntoWavetable to here
and combine with sample := with some transpositions
could incrase the speed.
>
>       // now apply volume adjustments to this sample and
>       // store in separate left/right audio buffers
>       Lbuffer^ := Lbuffer^ + leftvol * sample;
>       inc( Lbuffer );
>       Rbuffer^ := Rbuffer^ + rightvol * sample;
>       inc( Rbuffer );

With external function-calls, you (or i) can't be sure what to do the FPU.

I have some ideas to optimize the code, if there are not surprises in
external call.

mfg.
Herby

-- 
http://www.hubert-seidel.de
Thread Navigation

This is a paginated view of messages in the thread with full content displayed inline.

Messages are displayed in chronological order, with the original post highlighted in green.

Use pagination controls to navigate through all messages in large threads.

Back to All Threads