For a routine that I needed to go superfast and didn't require high precision, I was thinking of adding an extra boost through the old practice of switching to fixed point math. To my surprise, my test benchmarks are telling me this isn't a good idea. Even integers (without the extra multiplication/shifting) seem to work slightly slower than doubles. How come?
Dim t As Double, n As LongInt
Dim As Double d1 = 299991, d2 = 299990, d3 = 1
Dim As LongInt i1 = 299991, i2 = 299990, i3 = 1
Print "Integers:"
t = Timer
Do
i3 *= i1
i3 /= i2
n += 1
Loop Until Timer >= t + 1
Print n, i3
GetKey
Print "Doubles:"
t = Timer
Do
d3 *= d1
d3 /= d2
n += 1
Loop Until Timer >= t + 1
Print n, d3
GetKey
On my computer, the code yields about 11 million iterations for integers and somewhat over 23 million iterations for doubles. What am I doing wrong? Or do current CPUs really perform better with floating point than with integers? :o
Dim t As Double, n As Long
Dim As Double d1 = 299991, d2 = 299990, d3 = 1
Dim As Long i1 = 299991, i2 = 299990, i3 = 1
do
i1 = 299991: i2 = 299990: i3 = 1
Print "Integers:"
t = Timer
Do
i3 *= i1
i3 /= i2
n += 1
Loop Until Timer >= t + 1
Print n, i3
'GetKey
d1 = 299991: d2 = 299990: d3 = 1
n=0
sleep 100
Print "Doubles:"
t = Timer
Do
d3 *= d1
d3 /= d2
n += 1
Loop Until Timer >= t + 1
Print n, d3
print
sleep 200
'GetKey
loop until len(inkey)
you measured the timer() call too
note: integer div is "\" not "/"
By the way here are fast fixed point math including vector and matrix math.
(fadd, fmul, fmul, fdiv, fsin, fcos, fsqr, fsrqd, ftan ...) viewtopic.php?f=7&t=22838
Joshy
32-bit data types on 64-bit OS
ints 1.225493478923397
real 16.572667466356
64-bit data types on 64-bit OS
Integers ...
Real ...
ints 2.146352917887271
real 1.429170743445866
#ifndef __FB_64BIT__
type real as single
type ints as long
#else
type real as double
type ints as longint
#endif
Dim As real d1 = 299991, d2 = 299990, d3 = 1
Dim As ints i1 = 299991, i2 = 299990, i3 = 1
const N = 1000 * 1000 * 100
var tStart = Timer()
Print "Integers ..."
tStart = Timer()
for i as integer = 1 to n
i3 *= i1
i3 \= i2
next
var tInts=Timer()-tStart
Print "Real ..."
tStart = Timer()
for i as integer = 1 to n
d3 *= d1
d3 /= d2
next
var tReal=Timer()-tStart
print "ints " & tInts
print "real " & tReal
sleep
Last edited by D.J.Peters on Oct 22, 2017 0:24, edited 2 times in total.
xlucas wrote:On my computer, the code yields about 11 million iterations for integers and somewhat over 23 million iterations for doubles. What am I doing wrong? Or do current CPUs really perform better with floating point than with integers? :o
Oh! How could I be so stupid! Not resetting n is the main reason why this gives such weird values!
Dodicat: Thanks. I'm really surprised. I knew it was much better to perform the test several times, but I didn't expect it to give such a huge difference. Why would it be so slow the first time in comparison to the others?
Joshy: Wow! Fixed point with all the functions! Well, if I do this with fixed point, it's not a whole program using it. It'd be just one routine that would make its calculations with fixed point. It'd be a few multiplications and a couple of divisions, it's just it'd be called many, many times. So instead of building functions, I'd probably just enter the fixed point math inline. So from your numbers, I understand that switching to fixed point is only worth it if I am to use the same word width as my compiled code? (I imagine that a 32bit ELF running under a 64bit GNU would still run fast because it'd be running in a 32bit code memory region, but if I will use Longs, I have to compile in 32bit and if I will use LongInts, I have to compile in 64bit or just use Integers, right?)
Paul Doe: ¡Gracias! Yep, that's more or less what I was starting to think. On today's computers, it looks like fixed point math may be worth it, but you have to take lots of precautions. Just using fixed point instead of floating point at any random place in the program will likely make it slower. I'll take a benchmark for Singles vs Doubles.
jj2007: As a matter of fact, I may later on translate that single routine to assembly and call it from my program, but as you say, I would have to be very skilled to use packed singles. I'm going to first try the routine with doubles and see how slow it really is, then I'll try optimising it with singles, some fixed point, etc. If I see it is worth it, I'll translate it to assembly and I'll study packed floats to see if I can get that working. I don't want to complicate my code more than necessary, but if it looks like it'll be necessary, I will.
I did a little test.
I made a C library file function multiplying two floats (singles), 4 lines of C
I compared with a freebasic function.
looping 1 to 50000000 and accumulating a float
(f+=mult(1.8876,-1.00097)
Time 0.2511555792298168 Pure C result -3.355443e+007
Time 0.3508026434574276 pure FB result -3.355443e+007
Time 0.4116691944655031 Pure C result -3.355443e+007
Time 0.3614287024829537 pure FB result -3.355443e+007
Time 0.2476997915655375 Pure C result -3.355443e+007
Time 0.3490622595418245 pure FB result -3.355443e+007
Time 0.2498644923325628 Pure C result -3.355443e+007
Time 0.3631800361908972 pure FB result -3.355443e+007
Time 0.2480419827625155 Pure C result -3.355443e+007
Time 0.362808759091422 pure FB result -3.355443e+007
Time 0.2481223978102207 Pure C result -3.355443e+007
Time 0.3702811852563173 pure FB result -3.355443e+007
Time 0.2500718601513654 Pure C result -3.355443e+007
Time 0.3465009594801813 pure FB result -3.355443e+007
Time 0.2493067209143192 Pure C result -3.355443e+007
Time 0.3533146679401398 pure FB result -3.355443e+007
The freebasic was 32 bit -gen gcc.
-gen gas is a little faster.
If I optimize the -gen gcc -W3 -O2 then it is faster but the result is incorrect.
Conclusion from these few code lines -- C is faster at multiplying floats than FreeBASIC.
(Written before your last post, Dodicat, but relevant)
Ha, ha, ha... no! I just found out why:
Simply because Dodicat made the same mistake I made. He fixed mine and reset n to 0 after the first loop, but forgot to do the same after the second. Dodicat: after fixing that, it looks like there virtually is no difference between integers and floating point. It doesn't seem to matter if it's single or double or whether it's long or longint. My guess is the reason is that the check for Timer is so much slower than the multiplication and division that the latter go unnoticed.
Dim t As Double, n As Long
Dim As Double d1 = 299991, d2 = 299990, d3 = 1
Dim As Long i1 = 299991, i2 = 299990, i3 = 1
Dim k As Long
do
i1 = 299991: i2 = 299990: i3 = 1
Print "Integers:"
t = Timer
Do
For k = 1 To 1000
i3 *= i1
i3 /= i2
n += 1
Next k
Loop Until Timer >= t + 1
Print n, i3
'GetKey
d1 = 299991: d2 = 299990: d3 = 1
n=0
sleep 100
Print "Doubles:"
t = Timer
Do
For k = 1 To 1000
d3 *= d1
d3 /= d2
n += 1
Next k
Loop Until Timer >= t + 1
Print n, d3
print
sleep 200
n=0
'GetKey
loop until len(inkey)
Dim t As Double, n As Long
Dim As Double d1 = 299991, d2 = 299990, d3 = 1
Dim As Long i1 = 299991, i2 = 299990, i3 = 1
Dim k As Long
do
i1 = 299991: i2 = 299990: i3 = 1
Print "Integers:"
t = Timer
For k = 1 To 100000000
i3 *= i1
i3 \= i2
n += 1
Next k
Print timer-t, i3,n
'GetKey
d1 = 299991: d2 = 299990: d3 = 1
n=0
sleep 100
Print "Doubles:"
t = Timer
For k = 1 To 100000000
d3 *= d1
d3 /= d2
n += 1
Next k
Print timer-t, d3,n
print
sleep 200
n=0
'GetKey
loop until len(inkey)
sleep
Oh! I thought operator / was mapped to operator \ when the two operands were integers! But it makes sense it doesn't, since it has to return a floating point number anyway. So I'm getting that plain integers are 19% faster than doubles when multiplying and dividing.