Is this an issue?
#1
Hi all,

I was looking at creating C/C++ Dll's to add functionality to QB64PE, and I was doing timings to see what would be best done in a dll vs native to QB64PE and I got some results that confused me.

I have a simple c function I created which adds 2 numbers:

int Add(int a, int b)
{
  return (a + b);
}

I have a QB64PE function that does the same as the c function:

Function addit% (a%, b%)
    addit% = a% + b%
End Function


and as a baseline/control I do inline addition, each is done in a loop (500000000 times).

The results gave me pause:

Dll - 7.25 seconds
inline - 5.38 seconds
internal function - 41.16 seconds

I expected that the internal function would be between the dll and inline in timing.  Why would calling external to a dll to a function to add 2 numbers be quicker than calling internal to a function to add 2 numbers?


My code:

' dll test
Declare Dynamic Library "c:\users\bob\qb64\mydll"
    Function Add% (ByVal a As Integer, Byval b As Integer) 'SDL procedure name
End Declare

f% = 6
e% = 23

Locate 2, 1
Print "external dll call";

Locate 4, 1
Print "QB64PE inline addition";

Locate 6, 1
Print "QB64PE internal function";

a = Timer
For x& = 1 To 500000000
    k% = Add%(f%, e%)
Next
b = Timer

Locate 1, 1
Print Using "##.##########"; (b - a);

c = Timer
For x& = 1 To 500000000
    k% = f% + e%
Next
d = Timer

Locate 3, 1
Print Using "##.##########"; (d - c);

g = Timer
For x& = 1 To 500000000
    k% = addit%(f%, e%)
Next
h = Timer

Locate 5, 1
Print Using "##.##########"; (h - g);

End

Function addit% (a%, b%)
    addit% = a% + b%
End Function

If you want the c code for the dll let me know.
Reply
#2
Looks like a great time machine in the works!

Code: (Select All)
tstart# = Timer(.001)
For i = 1 To 5000000
    a = 2 + 2
Next
timesUp# = Timer(.001) - tstart#
Print timesUp#
b = b + ...
Reply
#3
The Time Machine - is a great film.  Wink
Reply
#4
@bobkreid - I don't understand what you want to show or prove. Do you want to show how long it takes to add two numbers in C or in Basic with a function?

So, in the two examples, probably about 0.0001 second, or something.

Code: (Select All)
//Addiert zwei Zahlen - 1. Juni 2022

#include <stdlib.h>
#include <stdio.h>

//Funktionsdeklaration
int addiere_zwei_zahlen(int zahl1, int zahl2);

int main(void)
{
    int zahl1, zahl2;
    
    printf("\nGeben Sie 2 Zahlen ein (Zahl1 <Leertaste> Zahl2\n");
    scanf("%d %d", &zahl1, &zahl2);
    
    printf("\nDie Summe ist: %d\n", addiere_zwei_zahlen(zahl1, zahl2));
    
    return(0);
}

int addiere_zwei_zahlen(int zahl1, int zahl2)
{
    return(zahl1 + zahl2);
}

Code: (Select All)
'Addition zweier Zahlen, 1. Juni 2022

Option _Explicit

Declare Function addiere(Zahl1 as Integer, Zahl2 as Integer) as Integer

Dim Zahl1 As Integer, Zahl2 As Integer

Print "Geben Sie 2 Zahlen ein: "
Input "Zahl1: ", Zahl1
Input "Zahl2: ", Zahl2

Print
Print Using "Die Summe der beiden Zahlen ist: ####"; addiere(Zahl1, Zahl2)

End

Function addiere (Zahl1 As Integer, Zahl2 As Integer)

  addiere = Zahl1 + Zahl2
End Function

The Time Machine is really a very well film (1960!).  Tongue
Reply
#5
(07-01-2022, 07:56 PM)bobkreid Wrote: Hi all,

I was looking at creating C/C++ Dll's to add functionality to QB64PE, and I was doing timings to see what would be best done in a dll vs native to QB64PE and I got some results that confused me.

I have a simple c function I created which adds 2 numbers:

...

If you want to understand what's going I would recommend taking a look in
./internal/temp/main.txt
and some of the other files (But main.txt is where most of the code goes). That has the actual generated C++ which you could then compare. Sub's and Function's get their own C++ function, they're listed in that file after the main code.

That said I can offer a simple explanation - QB64 SUBs and FUNCTIONs have a variety of resource setup and cleanup that happens at the beginning and end of the generated C++ function for them. It's not necessarily slow, but since the code you're testing does so little that extra logic ends up taking significantly longer than your actual code. For slightly longer SUBs and FUNCTIONs the overhead should be less noticeable.

So your original assumption about the timings was probably correct, the issue is that the C++ function you wrote isn't really equivalent to the QB64 generated function.
Reply
#6
Aye, the issue is as Matt stated: *All QB64 subs and functions come with some built in overhead.*

In this case, the function you produce is basically:

Function Add (v1, v2)
Step 1: Check for errors. If any, send error message. Pause.
Step 2: Check for break/exit conditions. Ctrl-C, or Red X in top right of Window. Quit if clicked.
Step 3: Allocate temp variable.
Step 4: Add v1 + v2, assign to temp
Step 5: Assign temp to Add for return value
Step 6: Cleanup temp variables
END FUNCTION

Compared to a base C function of:

Function Add (v1, v2)
Return v1 + v2
END FUNCTION

If necessary, you can remove some of that overhead with $CHECKING:OFF before your Function, but be aware that it'll disable the check for exit conditions and error reporting, if you do so. (And remember to turn $Checking:On after the Function.)

But I think the example above easily explains why you're seeing the results you're seeing here. Wink
Reply
#7
Maybe instead of using a DLL to add functionality, let's look at what it is you ultimately want the DLL to do. Is it something that can be done directly in QB64, avoiding the overhead you would incur by using an external library?
Reply
#8
(07-02-2022, 09:35 PM)madscijr Wrote: Maybe instead of using a DLL to add functionality, let's look at what it is you ultimately want the DLL to do. Is it something that can be done directly in QB64, avoiding the overhead you would incur by using an external library?

Well the reality here is that the 'overhead' of using an external library is basically non-existent, and with that a C version of some functionality will basically always be faster than a QB64 version. But I don't think this should be all that surprising, lots of languages are like this. The advantage of a language like QB64 is ease of use, not speed at all costs.
Reply
#9
(07-02-2022, 12:32 AM)DSMan195276 Wrote:
(07-01-2022, 07:56 PM)bobkreid Wrote: Hi all,

I was looking at creating C/C++ Dll's to add functionality to QB64PE, and I was doing timings to see what would be best done in a dll vs native to QB64PE and I got some results that confused me.

I have a simple c function I created which adds 2 numbers:

...

If you want to understand what's going I would recommend taking a look in
./internal/temp/main.txt
and some of the other files (But main.txt is where most of the code goes). That has the actual generated C++ which you could then compare. Sub's and Function's get their own C++ function, they're listed in that file after the main code.

That said I can offer a simple explanation - QB64 SUBs and FUNCTIONs have a variety of resource setup and cleanup that happens at the beginning and end of the generated C++ function for them. It's not necessarily slow, but since the code you're testing does so little that extra logic ends up taking significantly longer than your actual code. For slightly longer SUBs and FUNCTIONs the overhead should be less noticeable.

So your original assumption about the timings was probably correct, the issue is that the C++ function you wrote isn't really equivalent to the QB64 generated function.

Thank you, that explains it. I did not even consider that there was that much going on behind the curtain.
Reply
#10
(07-02-2022, 01:10 AM)SMcNeill Wrote: Aye, the issue is as Matt stated: *All QB64 subs and functions come with some built in overhead.*

In this case, the function you produce is basically:

Function Add (v1, v2)
  Step 1: Check for errors.  If any, send error message.  Pause.
  Step 2: Check for break/exit conditions.  Ctrl-C, or Red X in top right of Window.  Quit if clicked.
  Step 3: Allocate temp variable.
  Step 4: Add v1 + v2, assign to temp
  Step 5: Assign temp to Add for return value
  Step 6: Cleanup temp variables
END FUNCTION

Compared to a base C function of:

Function Add (v1, v2)
    Return v1 + v2
END FUNCTION

If necessary, you can remove some of that overhead with $CHECKING:OFF before your Function, but be aware that it'll disable the check for exit conditions and error reporting, if you do so.  (And remember to turn $Checking:On after the Function.)

But I think the example above easily explains why you're seeing the results you're seeing here.  Wink

Thank you Sam, hope all is going well for you.  A lot more is going on in QB64 than I imagined and will take that into account in the future when I see these types of things.  Thank you for helping a noob with this.
Reply




Users browsing this thread: 5 Guest(s)