Author Topic: Speed Comparison Tool  (Read 3884 times)

0 Members and 1 Guest are viewing this topic.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Speed Comparison Tool
« on: February 11, 2019, 06:49:03 am »
A quickly expandable routine to test how fast our operators are against each other, for those cases when we want to optimize speed for our programs.  (Such as the EllipseFill topic STx posted.)

Code: QB64: [Select]
  1. _TITLE "Expandable Speed Comparison Tool"
  2.  
  3. SCREEN _NEWIMAGE(800, 600, 32) 'nice size screen to display results
  4.  
  5. CONST LIMIT = 1 'time allowed for tests (in seconds)
  6.  
  7.  
  8. 'An expandable list of things to test
  9. DATA "+","-","^","*","/","\","INT"
  10. DATA "SQR","^.5"
  11. DATA "x + x","x * 2"
  12. DATA "DONE"
  13.  
  14. TYPE Results
  15.     Int AS _FLOAT 'to store times with integers being tested
  16.     Float AS _FLOAT 'to store times with floats being tested
  17.     Index AS LONG 'to store the index of the operation, in case we want to sort the results at a later date as we generate more data
  18.  
  19. DIM TestString(1000) AS STRING 'to hold the string
  20. DIM Times(1000) AS Results 'to hold the number of times we can run in a LIMIT
  21.  
  22. DO UNTIL t$ = "DONE" 'Read in the test results
  23.     READ t$
  24.     IF t$ <> "DONE" THEN count## = count## + 1: TestString(count##) = t$
  25.  
  26. PRINT "Running tests.  Wait"; count## * 2; " seconds..."
  27.  
  28. counter## = 0
  29.     counter## = counter## + 1
  30.     DEFLNG A-Z
  31.  
  32.     t# = TIMER
  33.     DO UNTIL TIMER > t# + LIMIT
  34.         junk = RND * 1000000 + 1 'just some random junk to test
  35.         junk1 = RND * 1000000 + 1 'add one so we never get division by 0 glitches
  36.         SELECT EVERYCASE TestString(counter##) 'We use everystring here so we don't skew results with fast exit CASE comparisons
  37.             CASE "+": junk = junk + junk1
  38.             CASE "-": junk = junk - junk1
  39.             CASE "^": junk = junk ^ junk1
  40.             CASE "*": junk = junk * junk1
  41.             CASE "/": junk = junk / junk1
  42.             CASE "\": junk = junk \ junk1
  43.             CASE "INT": junk = INT(junk)
  44.             CASE "SQR": junk = SQR(junk)
  45.             CASE "^.5": junk = junk ^ 0.5
  46.             CASE "x + x": junk = junk + junk
  47.             CASE "x * 2": junk = junk * 2
  48.         END SELECT
  49.         Times(counter##).Int = Times(counter##).Int + 1
  50.     LOOP
  51.  
  52.  
  53.     _DEFINE A-Z AS _FLOAT
  54.     t# = TIMER
  55.  
  56.     'Simply copy/paste a duplicate of the SELECT CASE from above to here for _FLOAT comparison speeds
  57.  
  58.     DO UNTIL TIMER > t# + LIMIT
  59.         junk = RND * 1000000 + 1 'just some random junk to test
  60.         junk1 = RND * 1000000 + 1 'add one so we never get division by 0 glitches
  61.         SELECT EVERYCASE TestString(counter##) 'We use everystring here so we don't skew results with fast exit CASE comparisons
  62.             CASE "+": junk = junk + junk1
  63.             CASE "-": junk = junk - junk1
  64.             CASE "^": junk = junk ^ junk1
  65.             CASE "*": junk = junk * junk1
  66.             CASE "/": junk = junk / junk1
  67.             CASE "\": junk = junk \ junk1
  68.             CASE "INT": junk = INT(junk)
  69.             CASE "SQR": junk = SQR(junk)
  70.             CASE "^.5": junk = junk ^ 0.5
  71.             CASE "x + x": junk = junk + junk
  72.             CASE "x * 2": junk = junk * 2
  73.         END SELECT
  74.         Times(counter##).Float = Times(counter##).Float + 1
  75.     LOOP
  76. LOOP UNTIL counter## = count##
  77.  
  78.  
  79. FOR i = 1 TO count##
  80.     PRINT "Testing speed for: "; TestString(i),
  81.     PRINT "INTEGERS:"; Times(i).Int,
  82.     PRINT "FLOATS:"; Times(i).Float

Oddly enough, some of the things which I've always taken for granted now seem to be much different performace-wise than they used to be.  (For a quick example, run this under GL and then run it under the old SDL version, if you have it, and watch how drastically the values change.)

In SDL, addition is about 1.5x as fast as multiplication. (3.9 vs 2.5).  In the latest GL version of QB64, the two run at comparable speeds, with multiplication being a little faster than addition on my PC.

x + x no longer seems to be any more optimized than x * 2 is.  (The speed difference between the two is negligible, at less than 1% difference on my tests.)

x * x, however, still shows a remarkable improvement over x ^ 2.  (1.8M vs 0.5M)



Perhaps swapping out the old compiler to the newer compiler which is packaged with QB64 has changed the old rules which I remember so firmly for optimization; I'm not certain.  What *is* certain, just from these simple tests, is that I need to start spending more time and testing things more thoroughly to maximize speeds than I used to, for routines which require it.

Also notice that in some cases, speed depends on what type of data you're working with..

Integer Division is faster than Division when dealing with INTEGERS.  (Who would've guessed that?!)
Division is faster than Integer Division when dealing with _FLOATS.

(Of course, if you add in an additional INT() command to make the number an integer, then / probably isn't as fast as \ with _FLOATS...)



General rule to remember:  Higher numbers relate to faster operations.  (After all, it represents the number of times we can perform the operation in a minute.)




Play around with it.  Test it with SDL, GL, 32 and 64-bit versions of QB64.  See how the values compare on your own machines.   Add more test conditions to it and then share them here so we all can make use of them. 

And, most importantly, if you see something wrong with the logic/flow which you think makes the tests invalid, report that issue/concern here.  Personally, I think it'd be nice to have a quick little tool to use to see what would be the best operators to use when optimizing for speed as much as possible.  ;)
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline _vince

  • Seasoned Forum Regular
  • Posts: 422
    • View Profile
Re: Speed Comparison Tool
« Reply #1 on: February 12, 2019, 04:13:38 pm »
A lot of the operator speed differences only apply to old 16 bit processors on which QB45 was intended to run on which is why you see those conventions on old recycled code.  Modern processors will execute all operators from bitwise AND to shift to division at the same speed for a given data type.  QB64 doesn't seem to prioritize speed when translating to C++ but most modern C++ compilers will optimize the obvious operator substitutions in the final executable. It is generally thought of as bad practice to micro-optimize at the expense of code readability. Other than improving your algorithm and logical optimizations, it is almost impossible to optimize down to the cycle level these days since executed code will be heavily pipelined and cached on modern processors and you will see different results on different machines which may even vary due to time of day and dozen other factors such as cached data/code, processor load, multitasking, etc. Beyond an order of magnitude difference, it is worth ignoring slight time differences when doing time comparisons
« Last Edit: February 12, 2019, 04:17:18 pm by _vince »