16 to 32 bit floating point conversions

DSP, Plugin and Host development discussion.
RELATED
PRODUCTS

Post

aciddose wrote:it is better to use a divide or multiply than a bitshift in most cases on modern cpus, and always in c/c++ langauge. the divide or multiply will get optimized to the best form by the compiler, while if you use a bitshift it will not.

in any case, a bitshift will take the same number of cycles as a divide or multiply.. either as a single opcode or in a lea, they all take one cycle. the use of bitshifts in place of multiplies/divides is an artifact of older architectures and is really no longer nessicary in almost all cases.

many multiples/divides will get optimized selectively to both bitshifts and leas on the x86 because sometimes it is more advantageous to use one than the other.
One FDIV per clock cycle, but a 6 cycle latency.
If the divs are SSE2, it means you can do four single precision divs per clock cycle, but on the other hand, BarenB's bit ops are probably going to pipeline nicely, using only the integer ALU. I think the FPU should be a lot faster if you can keep it fed, not a problem considering the type of data and the nature of the problem.

I realize I'm out of my depth here. My assembly experience includes MIPS and early X86, and I'm only a wannabe DSP programmer. But this topic does really interest me.

Thanks!

Post

Geez, simply TRY it; never trust some prettily formulated phrases. Benchmark both solutions. You might be in for a surprise, one way or the other 8-)

Algorithms that get passed as "tools of the trade" because they worked oh so well on G3/G4 Macs or earlier machines can fail miserably when applied to an Athlon or P4.
"Until you spread your wings, you'll have no idea how far you can walk." Image

Post

arakula wrote:Geez, simply TRY it; never trust some prettily formulated phrases. Benchmark both solutions. You might be in for a surprise, one way or the other 8-)
Of course! Apologies to the original poster, to me this is all hypothetical.
My compiler makes the cast and divide into two f* instructions that I don't even know, and it's the same with optimizations on or off. In reality, it would not occur to me to even look at that, unless it turned out to be a bottleneck.
Algorithms that get passed as "tools of the trade" because they worked oh so well on G3/G4 Macs or earlier machines can fail miserably when applied to an Athlon or P4.
Of course! One thing that can make a difference, is the path the data takes to the FPU or ALU. Intel FPU gets his data from the L2 cache, Athlon gets his from the L1 cache. Bananas to Oranges comparison, I know, but possibly a real difference there.

I *wish* I was in a world where I actually had to care about shaving cycles off a computation ;-)

Oh well, it's clear that a stream of audio will have no problem keeping a pipeline full, and I doubt any reasonable approach has a real problem.

Post

james0tucson wrote:I really like the way you did the scaling in logic without a divide. Is it actually faster than a good hardware divide by a constant? (Just wondering).
Hard to say, but I did a little test with a few different optimization settings, and it seemed to be slightly faster in most cases. But the real advantage is when using this trick to go back to integer, since that skips the slow float to int conversion.

Post

james; in this case it was a division of an integer value, of course you can not bitshift a float without casting it to int. the reason using mul/div is better isnt entirely because it will always be faster, no, in fact it will usually be optimized into other instructions. THAT is the reason it is better to use mul/div, because it can be optimized, and the compiler will usually know better than you when to use what specific instruction for the specific task.

Post

aciddose wrote:james; in this case it was a division of an integer value, of course you can not bitshift a float without casting it to int.
I'm not sure exactly what case you are referring to, but my code was about doing a FLOAT division/multiplication and converting at once by logical operations and an add/sub only.

Post

i was refering to the code you posted before, which would be:

float fVal = (float&)(((iVal << 8) & 0x007FFFFF) | 0x3F800000) - 1.f;

expanded to translate a signed short packed sign extended into a long:

float fVal = (float&)((int&)((float&)((((iVal ^ (iVal>>31)) << 8) & 0x007FFFFF) | 0x3F800000) - 1.f) | (iVal&0x80000000));

which is actually slower than simply converting using the cpu and then multiplying by a constant.

Post

I agree that a compiler will of course find a better optimization for a given approach to a problem, than a human would find, but the compiler is NOT going to decide that a radically different approach would eliminate the need for that optimization.

There are asm programmers who can give plenty of counterexamples to the claim that compilers "always" outperform humans. Some optimizations can be pretty specialized. Then you get guys like Paul Hsieh who can visualize pipeline stalls in his head while coding...

Like I said, I'm really not trying to pretend I know this subject; so I hope I don't come across as a prick in the forum.

Post

i was never talking about broad asm optimizations, i was talking about if you should use a bitshift in place of a multiply or divide in c source code.. which you should not. a bitshift should never be used in c source unless a bitshift is intended to be used for logic purposes. a multiply or divide by a power of two should never be replaced by a bitshift in c source, the compiler will do it automatically for you and will always do it better.

for example:

a = b*8 + c*2

shouldnt be replaced by

a = (b << 3) + (c << 1)

or by

a = (b << 3) + c + c



the compiler will optimize this into a single lea instruction:

lea a, c + c + b*8

but only if the source is:

a = b*8 + c*2


simmilarly:

a = b * 3

should usually not be replaced by

a = b * 2 + b

or

a = (b << 1) + b

since the compiler will do it for you automatically depending upon which is faster.

such a line in c source should always remain

a = b * 3


additionally, use of bitshifts in place of multiplies and divides in c source is bad because it can negate the ability of the compiler to use all of the registers available. in a multiply or a lea additional registers like edi, esi, ebp and esp can be used where in a bitshift they can not. using bitshifts thus leads to the compiler generating code with unnessicary register shuffling overhead. knowing these simple rules (do not use bitshift in place of mul and div in c source) can make the difference between very fast code, and very slow code.

Post

I agree that the compiler optimizes alot of these things itself, but I don't agree that you should never use bitshifts instead of multiplications (or other optimizations). The compiler doesn't always know what you know. If for example the multiplication factor is a variable, from which YOU know its always a power of 2 so you can implement the same thing with a shift, the compiler will use a mul instead, and thus produces slower code.

Post

acting as:

a << b == a*pow(2, b)

though a bitshift is not faster than a multiply when an intermediate is not used. bitshifts by static/intermediates are faster because the intermediate value is stored in the instruction. if you have to fetch the value from a register, mul is the same speed. bitshifts in this case only become useful when you want to translate directly from a linear number (0...31) to a power (1...2147483648) without having any complex steps between. if you are assigning the scale value in some part out of the loop, you should be using a multiply, not a shift.

it is better to avoid bitshifts in any case other than logic operations. try changing your code containing bitshifts to multiplies and divides and watch it work much faster.

here isnt an example where bitshifts are useful and not useful:
Last edited by aciddose on Fri Jun 09, 2006 10:15 pm, edited 1 time in total.

Post

in this example, no decent compiler will use a div : it WILL bitshift to do the division, and do s & 4095 (bitwise AND) for the modulus.

Post

hm, yes, horrible example.
i've read a valid example somewhere once, i wish i could remember it.. it may have been for a cpu other than the x86. perhaps it was with shifts/subs and a modulus of a non power of two number or something..

anyway, you still shouldnt use a bitshift or and in it's place. anywhere that a shift will indeed be better used, the compiler will use it.

Post Reply

Return to “DSP and Plugin Development”