Author Topic: Is this fast enough as general circle fill?  (Read 20113 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
Re: Is this fast enough as general circle fill?
« Reply #30 on: June 26, 2018, 03:34:18 pm »
Sometimes, computers do things that are completely counter-intuitive to us, and we find ourselves having to step back as programmers and simply say, "WOW!!"   Here's a perfect example of that:

Code: QB64: [Select]
  1. DIM SHARED Radius AS INTEGER: Radius = 1000
  2. DIM SHARED CP(Radius, Radius) AS INTEGER 'CirclePoints
  3.  
  4.  
  5. SCREEN _NEWIMAGE(800, 600, 256)
  6.  
  7.  
  8. PreCalcCircles
  9.  
  10. x = _WIDTH / 2
  11. y = _HEIGHT / 2
  12. k = 15
  13. TestLoopLimit = 10000
  14.  
  15. t1 = TIMER(0.001)
  16. FOR i = 1 TO TestLoopLimit
  17.     CircleFillFast x, y, 300, k
  18.  
  19. t2 = TIMER(0.001)
  20. FOR i = 1 TO TestLoopLimit
  21.     CircleFill x, y, 300, k
  22. t3 = TIMER(0.001)
  23.  
  24. PRINT USING "##.#### seconds with CircleFillFast"; t2 - t1
  25. PRINT USING "##.#### seconds with CircleFill"; t3 - t2
  26.  
  27.  
  28. SUB PreCalcCircles
  29.     FOR i = 0 TO Radius 'each circle, for all radius sizes from 1 to limit
  30.         FOR j = 0 TO i 'get the points for each line of those circles
  31.             CP(i, j) = SQR(i * i - j * j)
  32.         NEXT
  33.     NEXT
  34.  
  35.  
  36. SUB CircleFillFast (x, y, Radius, k)
  37.     FOR j = 0 TO Radius 'get the points for each line of those circles
  38.         LINE (x - CP(Radius, j), y + j)-(x + CP(Radius, j), y + j), k, BF
  39.         LINE (x - CP(Radius, j), y - j)-(x + CP(Radius, j), y - j), k, BF
  40.     NEXT
  41.  
  42. SUB CircleFill (CX AS LONG, CY AS LONG, R AS LONG, C AS LONG)
  43.     DIM Radius AS LONG, RadiusError AS LONG
  44.     DIM X AS LONG, Y AS LONG
  45.  
  46.     Radius = ABS(R)
  47.     RadiusError = -Radius
  48.     X = Radius
  49.     Y = 0
  50.  
  51.     IF Radius = 0 THEN PSET (CX, CY), C: EXIT SUB
  52.  
  53.     ' Draw the middle span here so we don't draw it twice in the main loop,
  54.     ' which would be a problem with blending turned on.
  55.     LINE (CX - X, CY)-(CX + X, CY), C, BF
  56.  
  57.     WHILE X > Y
  58.         RadiusError = RadiusError + Y * 2 + 1
  59.         IF RadiusError >= 0 THEN
  60.             IF X <> Y + 1 THEN
  61.                 LINE (CX - Y, CY - X)-(CX + Y, CY - X), C, BF
  62.                 LINE (CX - Y, CY + X)-(CX + Y, CY + X), C, BF
  63.             END IF
  64.             X = X - 1
  65.             RadiusError = RadiusError - X * 2
  66.         END IF
  67.         Y = Y + 1
  68.         LINE (CX - X, CY - Y)-(CX + X, CY - Y), C, BF
  69.         LINE (CX - X, CY + Y)-(CX + X, CY + Y), C, BF
  70.     WEND
  71.  

Here we look at two different circle fill routines -- one, which I'd assume to be faster, which precalculates the offset needed to find the endpoints for each line which composes a circle, and another, which is the same old CircleFill program which I've shared countless times over the years with people on various QB64 forums.

When all is said and done though, CircleFill is STILL even faster than CircleFillFast, which pregenerates those end-points for us!

I've got to admit, I find these results rather shocking!  (Thus the name I chose when naming the CircleFill-NotSoFastAfterall routine.)  Apparently, in this case, the integer math used in CircleFill is faster than the time it takes for QB64 to look up those internal values from a preset array.

Who woulda thunk it?!!

Anywho, I thought I'd share, just so others could look over the two routines and compare.  Maybe there's a way to improve the CircleFillFast so that it'd be faster than CircleFill, but if it is, I'm not seeing it at the moment.

It looks like CircleFill is still the fastest routine to use to rapidly fill a circle for us.  :D
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Is this fast enough as general circle fill?
« Reply #31 on: June 26, 2018, 03:39:20 pm »
Code: QB64: [Select]
  1. DIM SHARED Radius AS INTEGER: Radius = 1000
  2. DIM SHARED CP(Radius, Radius) AS INTEGER 'CirclePoints
  3.  
  4.  
  5. SCREEN _NEWIMAGE(800, 600, 256)
  6.  
  7.  
  8. PreCalcCircles
  9.  
  10. x = _WIDTH / 2
  11. y = _HEIGHT / 2
  12. k = 15
  13. TestLoopLimit = 10000
  14.  
  15. t1 = TIMER(0.001)
  16. FOR i = 1 TO TestLoopLimit
  17.     CircleFillFast x, y, 300, k
  18.  
  19. t2 = TIMER(0.001)
  20. FOR i = 1 TO TestLoopLimit
  21.     CircleFill x, y, 300, k
  22.  
  23. t3 = TIMER(0.001)
  24.  
  25. PRINT USING "##.#### seconds with CircleFillFast"; t2 - t1
  26. PRINT USING "##.#### seconds with CircleFill"; t3 - t2
  27.  
  28.  
  29. SUB PreCalcCircles
  30.     FOR i = 0 TO Radius 'each circle, for all radius sizes from 1 to limit
  31.         FOR j = 0 TO i 'get the points for each line of those circles
  32.             CP(i, j) = SQR(i * i - j * j)
  33.         NEXT
  34.     NEXT
  35.  
  36.  
  37. SUB CircleFillFast (x, y, r, k)
  38.     DO UNTIL j > r
  39.         t = CP(r, j)
  40.         LINE (x - t, y + j)-(x + t, y + j), k, BF
  41.         LINE (x - t, y - j)-(x + t, y - j), k, BF
  42.         j = j + 1
  43.     LOOP
  44.  
  45. SUB CircleFill (CX AS LONG, CY AS LONG, R AS LONG, C AS LONG)
  46.     DIM Radius AS LONG, RadiusError AS LONG
  47.     DIM X AS LONG, Y AS LONG
  48.  
  49.     Radius = ABS(R)
  50.     RadiusError = -Radius
  51.     X = Radius
  52.     Y = 0
  53.  
  54.     IF Radius = 0 THEN PSET (CX, CY), C: EXIT SUB
  55.  
  56.     ' Draw the middle span here so we don't draw it twice in the main loop,
  57.     ' which would be a problem with blending turned on.
  58.     LINE (CX - X, CY)-(CX + X, CY), C, BF
  59.  
  60.     WHILE X > Y
  61.         RadiusError = RadiusError + Y * 2 + 1
  62.         IF RadiusError >= 0 THEN
  63.             IF X <> Y + 1 THEN
  64.                 LINE (CX - Y, CY - X)-(CX + Y, CY - X), C, BF
  65.                 LINE (CX - Y, CY + X)-(CX + Y, CY + X), C, BF
  66.             END IF
  67.             X = X - 1
  68.             RadiusError = RadiusError - X * 2
  69.         END IF
  70.         Y = Y + 1
  71.         LINE (CX - X, CY - Y)-(CX + X, CY - Y), C, BF
  72.         LINE (CX - X, CY + Y)-(CX + X, CY + Y), C, BF
  73.     WEND
  74.  
  75.  

And this version is quite a bit faster than the previous one -- but still slower than the original CircleFill routine.  The only real change here for performance?  Using         t = CP(r, j)     and using t in our circle drawing routines instead of CP(r,j).  Referencing arrays are slower than referencing single variables, and we see the change in performance here, noticeably.

https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Is this fast enough as general circle fill?
« Reply #32 on: June 26, 2018, 03:45:50 pm »
*Remove the SLEEP statement in the above routine, which I was using to test things, or else you'll just sit there and not do much of anything while waiting for the program to complete.  I'd placed it there while testing changes and can't go back and edit the above post to remove it.  :P
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline Petr

  • Forum Resident
  • Posts: 1720
  • The best code is the DNA of the hops.
    • View Profile
Re: Is this fast enough as general circle fill?
« Reply #33 on: June 26, 2018, 04:05:11 pm »
Hi Steve. NUMERIC TYPES!

Try it now:

Code: QB64: [Select]
  1. DIM SHARED Radius AS INTEGER: Radius = 1000
  2. DIM SHARED CP(Radius, Radius) AS INTEGER 'CirclePoints
  3.  
  4.  
  5. SCREEN _NEWIMAGE(800, 600, 256)
  6.  
  7.  
  8. PreCalcCircles
  9.  
  10. x = _WIDTH / 2
  11. y = _HEIGHT / 2
  12. k = 15
  13. TestLoopLimit = 10000
  14.  
  15. t1 = TIMER(0.001)
  16. FOR i = 1 TO TestLoopLimit
  17.     CircleFillFast x#, y#, 300, k#
  18.  
  19.  
  20. t2 = TIMER(0.001)
  21. FOR i = 1 TO TestLoopLimit
  22.     CircleFill x, y, 300, k
  23.  
  24. t3 = TIMER(0.001)
  25.  
  26. PRINT USING "##.#### seconds with CircleFillFast"; t2 - t1
  27. PRINT USING "##.#### seconds with CircleFill"; t3 - t2
  28.  
  29.  
  30. SUB PreCalcCircles
  31.     DIM i AS INTEGER, j AS INTEGER
  32.     FOR i = 0 TO Radius 'each circle, for all radius sizes from 1 to limit
  33.         FOR j = 0 TO i 'get the points for each line of those circles
  34.             CP(i, j) = SQR(i * i - j * j)
  35.         NEXT
  36.     NEXT
  37.  
  38.  
  39. SUB CircleFillFast (x AS INTEGER, y AS INTEGER, r AS INTEGER, k AS INTEGER)
  40.     DO UNTIL j > r
  41.         t = CP(r, j)
  42.         LINE (x - t, y + j)-(x + t, y + j), k, BF
  43.         LINE (x - t, y - j)-(x + t, y - j), k, BF
  44.         j = j + 1
  45.     LOOP
  46.  
  47. SUB CircleFill (CX AS LONG, CY AS LONG, R AS LONG, C AS LONG)
  48.     DIM Radius AS LONG, RadiusError AS LONG
  49.     DIM X AS LONG, Y AS LONG
  50.  
  51.     Radius = ABS(R)
  52.     RadiusError = -Radius
  53.     X = Radius
  54.     Y = 0
  55.  
  56.     IF Radius = 0 THEN PSET (CX, CY), C: EXIT SUB
  57.  
  58.     ' Draw the middle span here so we don't draw it twice in the main loop,
  59.     ' which would be a problem with blending turned on.
  60.     LINE (CX - X, CY)-(CX + X, CY), C, BF
  61.  
  62.     WHILE X > Y
  63.         RadiusError = RadiusError + Y * 2 + 1
  64.         IF RadiusError >= 0 THEN
  65.             IF X <> Y + 1 THEN
  66.                 LINE (CX - Y, CY - X)-(CX + Y, CY - X), C, BF
  67.                 LINE (CX - Y, CY + X)-(CX + Y, CY + X), C, BF
  68.             END IF
  69.             X = X - 1
  70.             RadiusError = RadiusError - X * 2
  71.         END IF
  72.         Y = Y + 1
  73.         LINE (CX - X, CY - Y)-(CX + X, CY - Y), C, BF
  74.         LINE (CX - X, CY + Y)-(CX + X, CY + Y), C, BF
  75.     WEND
  76.  
  77.  
  78.  

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Is this fast enough as general circle fill?
« Reply #34 on: June 26, 2018, 04:12:18 pm »
Just changing the types for     CircleFillFast x#, y#, 300, k# , breaks the program.  There is no x# or y# in use anywhere, so you're placing the circle at 0,0 with color 0....  75% of it is drawn off the screen, and that's going to make it appear to run faster than it actually is.

You can't compare speeds of drawing 1/4 of a circle, against speeds of drawing a whole circle.  ;)

You'd need to change all instances of X/Y to become single precision values, and then you end up with similar speed results as before.

Code: QB64: [Select]
  1. DIM SHARED Radius AS INTEGER: Radius = 1000
  2. DIM SHARED CP(Radius, Radius) AS INTEGER 'CirclePoints
  3.  
  4.  
  5. SCREEN _NEWIMAGE(800, 600, 256)
  6.  
  7.  
  8. PreCalcCircles
  9.  
  10. x# = _WIDTH / 2
  11. y# = _HEIGHT / 2
  12. k# = 15
  13. TestLoopLimit = 10000
  14.  
  15. t1 = TIMER(0.001)
  16. FOR i = 1 TO TestLoopLimit
  17.     CircleFillFast x#, y#, 300, k#
  18.  
  19.  
  20. t2 = TIMER(0.001)
  21. FOR i = 1 TO TestLoopLimit
  22.     CircleFill x#, y#, 300, k#
  23.  
  24. t3 = TIMER(0.001)
  25.  
  26. PRINT USING "##.#### seconds with CircleFillFast"; t2 - t1
  27. PRINT USING "##.#### seconds with CircleFill"; t3 - t2
  28.  
  29.  
  30. SUB PreCalcCircles
  31.     DIM i AS INTEGER, j AS INTEGER
  32.     FOR i = 0 TO Radius 'each circle, for all radius sizes from 1 to limit
  33.         FOR j = 0 TO i 'get the points for each line of those circles
  34.             CP(i, j) = SQR(i * i - j * j)
  35.         NEXT
  36.     NEXT
  37.  
  38.  
  39. SUB CircleFillFast (x AS INTEGER, y AS INTEGER, r AS INTEGER, k AS INTEGER)
  40.     DO UNTIL j > r
  41.         t = CP(r, j)
  42.         LINE (x - t, y + j)-(x + t, y + j), k, BF
  43.         LINE (x - t, y - j)-(x + t, y - j), k, BF
  44.         j = j + 1
  45.     LOOP
  46.  
  47. SUB CircleFill (CX AS LONG, CY AS LONG, R AS LONG, C AS LONG)
  48.     DIM Radius AS LONG, RadiusError AS LONG
  49.     DIM X AS LONG, Y AS LONG
  50.  
  51.     Radius = ABS(R)
  52.     RadiusError = -Radius
  53.     X = Radius
  54.     Y = 0
  55.  
  56.     IF Radius = 0 THEN PSET (CX, CY), C: EXIT SUB
  57.  
  58.     ' Draw the middle span here so we don't draw it twice in the main loop,
  59.     ' which would be a problem with blending turned on.
  60.     LINE (CX - X, CY)-(CX + X, CY), C, BF
  61.  
  62.     WHILE X > Y
  63.         RadiusError = RadiusError + Y * 2 + 1
  64.         IF RadiusError >= 0 THEN
  65.             IF X <> Y + 1 THEN
  66.                 LINE (CX - Y, CY - X)-(CX + Y, CY - X), C, BF
  67.                 LINE (CX - Y, CY + X)-(CX + Y, CY + X), C, BF
  68.             END IF
  69.             X = X - 1
  70.             RadiusError = RadiusError - X * 2
  71.         END IF
  72.         Y = Y + 1
  73.         LINE (CX - X, CY - Y)-(CX + X, CY - Y), C, BF
  74.         LINE (CX - X, CY + Y)-(CX + X, CY + Y), C, BF
  75.     WEND
  76.  
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Is this fast enough as general circle fill?
« Reply #35 on: June 26, 2018, 04:26:52 pm »
The best speed I can tend to generate with tweaking things is this (so far): 

Code: QB64: [Select]
  1. DIM SHARED Radius AS INTEGER: Radius = 1000
  2. DIM SHARED CP(Radius, Radius) AS INTEGER 'CirclePoints
  3.  
  4.  
  5. SCREEN _NEWIMAGE(800, 600, 256)
  6.  
  7.  
  8. PreCalcCircles
  9.  
  10. x = _WIDTH / 2
  11. y = _HEIGHT / 2
  12. k = 15
  13. TestLoopLimit = 10000
  14.  
  15. t1# = TIMER(0.001)
  16. FOR i = 1 TO TestLoopLimit
  17.     CircleFillFast x, y, 300, k
  18.  
  19.  
  20. t2# = TIMER(0.001)
  21. FOR i = 1 TO TestLoopLimit
  22.     CircleFill x, y, 300, k
  23.  
  24. t3# = TIMER(0.001)
  25.  
  26. PRINT USING "##.#### seconds with CircleFillFast"; t2# - t1#
  27. PRINT USING "##.#### seconds with CircleFill"; t3# - t2#
  28.  
  29.  
  30. SUB PreCalcCircles
  31.     FOR i = 0 TO Radius 'each circle, for all radius sizes from 1 to limit
  32.         FOR j = 0 TO i 'get the points for each line of those circles
  33.             CP(i, j) = SQR(i * i - j * j)
  34.         NEXT
  35.     NEXT
  36.  
  37.  
  38. SUB CircleFillFast (x, y, r, k)
  39.     dc = _DEFAULTCOLOR
  40.     COLOR k
  41.     DO UNTIL j > r
  42.         t = CP(r, j)
  43.         LINE (x - t, y + j)-STEP(t + t, 0), , BF
  44.         LINE (x - t, y - j)-STEP(t + t, 0), , BF
  45.         j = j + 1
  46.     LOOP
  47.     COLOR dc
  48.  
  49. SUB CircleFill (CX AS LONG, CY AS LONG, R AS LONG, C AS LONG)
  50.     DIM Radius AS LONG, RadiusError AS LONG
  51.     DIM X AS LONG, Y AS LONG
  52.  
  53.     Radius = ABS(R)
  54.     RadiusError = -Radius
  55.     X = Radius
  56.     Y = 0
  57.  
  58.     IF Radius = 0 THEN PSET (CX, CY), C: EXIT SUB
  59.     ' Draw the middle span here so we don't draw it twice in the main loop,
  60.     ' which would be a problem with blending turned on.
  61.     LINE (CX - X, CY)-(CX + X, CY), C, BF
  62.  
  63.     WHILE X > Y
  64.         RadiusError = RadiusError + Y * 2 + 1
  65.         IF RadiusError >= 0 THEN
  66.             IF X <> Y + 1 THEN
  67.                 LINE (CX - Y, CY - X)-(CX + Y, CY - X), C, BF
  68.                 LINE (CX - Y, CY + X)-(CX + Y, CY + X), C, BF
  69.             END IF
  70.             X = X - 1
  71.             RadiusError = RadiusError - X * 2
  72.         END IF
  73.         Y = Y + 1
  74.         LINE (CX - X, CY - Y)-(CX + X, CY - Y), C, BF
  75.         LINE (CX - X, CY + Y)-(CX + X, CY + Y), C, BF
  76.     WEND
  77.  

Speeds on my machine are 0.55 seconds versus 0.50 seconds, so both of them are plenty fast for most instances.  (After all, this is drawing a filled circle the whole size of my screen 10,000 times.)

Maybe if someone wanted/needed the endpoints for collision detection or some other use, then the precalculated values in CircleFillFast would be useful.  As it is though, I suppose I'm still going to have to call CircleFill the quickest routine I've personally seen so far. 
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline Petr

  • Forum Resident
  • Posts: 1720
  • The best code is the DNA of the hops.
    • View Profile
Re: Is this fast enough as general circle fill?
« Reply #36 on: June 26, 2018, 04:47:47 pm »
You're right. I did not realize that. Whatever I'm trying to do, it's not better.

Offline Petr

  • Forum Resident
  • Posts: 1720
  • The best code is the DNA of the hops.
    • View Profile
Re: Is this fast enough as general circle fill?
« Reply #37 on: June 26, 2018, 04:54:19 pm »
Steve, can you please look to this source, why it is so slow? https://www.qb64.org/forum/index.php?topic=287.msg1756#msg1756

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Is this fast enough as general circle fill?
« Reply #38 on: June 26, 2018, 05:06:03 pm »
Steve, can you please look to this source, why it is so slow? https://www.qb64.org/forum/index.php?topic=287.msg1756#msg1756

Try it with $CHECKING:OFF.

I'll give it a better looking over later tonight, but the wife has me heading out for supper here in a few moments.  $CHECKING might make a noticeable boost in performance for you though. 
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline Petr

  • Forum Resident
  • Posts: 1720
  • The best code is the DNA of the hops.
    • View Profile
Re: Is this fast enough as general circle fill?
« Reply #39 on: June 26, 2018, 05:09:53 pm »
I'll try it. Thank you. I'm going to sleep, it's almost midnight here. I'll see it tomorrow.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Is this fast enough as general circle fill?
« Reply #40 on: June 26, 2018, 05:16:02 pm »
Advice 2:

Remove:

FUNCTION IN& (x AS INTEGER, y AS INTEGER)
    IN& = (_WIDTH * y + x) * 4
END FUNCTION


Function calls are slow.  Do the math directly, and don't call on _WIDTH.

Early in the routine, use w = _WIDTH, then:

  _MEMPUT m, m.OFFSET + (w * y1 + x) * 4, clr&
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Is this fast enough as general circle fill?
« Reply #41 on: June 26, 2018, 06:24:58 pm »
Fellippe, when you started with comparing time, which is very beneficial to all, I give this code here. I made the analogy of the LINE command via MEMPUT. Even though I expected great speed, but  the result is to cry. It's a terrible lemur lazy. Could you tell me what I was doing here bad again? Thank you.

Code: QB64: [Select]
  1.  
  2. J& = _NEWIMAGE(800, 600, 32)
  3. m = _MEMIMAGE(J&)
  4.  
  5. T = TIMER
  6.  
  7. FOR test = 1 TO 1000
  8.     vinceCircleFill 400, 300, 200, _RGB32(0, 255, 0)
  9. NEXT test
  10.  
  11.  
  12. SUB vinceCircleFill (x AS LONG, y AS LONG, R AS LONG, C AS _UNSIGNED LONG)
  13.     x0 = R
  14.     y0 = 0
  15.     e = 0
  16.     DO WHILE y0 < x0
  17.         IF e <= 0 THEN
  18.             y0 = y0 + 1
  19.             MEM_LINE x - x0, y + y0, x + x0, y + y0, C
  20.             MEM_LINE x - x0, y - y0, x + x0, y - y0, C
  21.             e = e + 2 * y0
  22.         ELSE
  23.             MEM_LINE x - y0, y - x0, x + y0, y - x0, C
  24.             MEM_LINE x - y0, y + x0, x + y0, y + x0, C
  25.             x0 = x0 - 1
  26.             e = e - 2 * x0
  27.         END IF
  28.     LOOP
  29.     MEM_LINE x - R, y, x + R, y, C
  30.  
  31. SUB MEM_LINE (x1 AS INTEGER, y1 AS INTEGER, x2 AS INTEGER, y2 AS INTEGER, clr AS LONG)
  32.     DEFLNG A-Z
  33.     dX = x2 - x1
  34.     dY = y2 - y1
  35.     IF dX > dY OR dX = dY THEN
  36.         x = x1: y = y1
  37.         DO WHILE x <> x2
  38.             x = x + 1
  39.             y = y + (dY / dX)
  40.             _MEMPUT m, m.OFFSET + IN&(x, y), clr&
  41.         LOOP
  42.     END IF
  43.     IF dY > dX THEN
  44.         x = x1: y = y1
  45.         DO WHILE y <> y2
  46.             x = x + (dX / dY)
  47.             y = y + 1
  48.             _MEMPUT m, m.OFFSET + IN&(x, y), clr&
  49.         LOOP
  50.     END IF
  51.     IF x1 = x2 THEN
  52.         FOR d = y1 TO y2
  53.             _MEMPUT m, m.OFFSET + IN&(x1, d), clr&
  54.         NEXT d
  55.     END IF
  56.     IF y1 = y2 THEN
  57.         FOR d = x1 TO x2
  58.             _MEMPUT m, m.OFFSET + IN&(d, y1), clr&
  59.         NEXT d
  60.     END IF
  61.  
  62.     IN& = (_WIDTH * y + x) * 4
  63.  
  64.  

A few tweaks to the above code, and you'll see a slight improvement on run time:
Code: QB64: [Select]
  1. J& = _NEWIMAGE(800, 600, 32)
  2. m = _MEMIMAGE(J&)
  3.  
  4. T = TIMER
  5.  
  6. FOR test = 1 TO 1000
  7.     vinceCircleFill 400, 300, 200, _RGB32(0, 255, 0)
  8. NEXT test
  9.  
  10.  
  11. SUB vinceCircleFill (x AS LONG, y AS LONG, R AS LONG, C AS _UNSIGNED LONG)
  12.     x0 = R
  13.     y0 = 0
  14.     e = 0
  15.     DO WHILE y0 < x0
  16.         IF e <= 0 THEN
  17.             y0 = y0 + 1
  18.             MEM_LINE x - x0, y + y0, x + x0, y + y0, C
  19.             MEM_LINE x - x0, y - y0, x + x0, y - y0, C
  20.             e = e + 2 * y0
  21.         ELSE
  22.             MEM_LINE x - y0, y - x0, x + y0, y - x0, C
  23.             MEM_LINE x - y0, y + x0, x + y0, y + x0, C
  24.             x0 = x0 - 1
  25.             e = e - 2 * x0
  26.         END IF
  27.     LOOP
  28.     MEM_LINE x - R, y, x + R, y, C
  29.  
  30. SUB MEM_LINE (x1 AS INTEGER, y1 AS INTEGER, x2 AS INTEGER, y2 AS INTEGER, clr AS LONG)
  31.     DEFLNG A-Z
  32.     dX = x2 - x1
  33.     dY = y2 - y1
  34.     w = _WIDTH
  35.     x = x1: y = y1
  36.     IF dX >= dY THEN
  37.         DO WHILE x <> x2
  38.             x = x + 1
  39.             y = y + (dY / dX)
  40.             _MEMPUT m, m.OFFSET + (w * y + x) * 4, clr
  41.         LOOP
  42.     ELSE
  43.         DO WHILE y <> y2
  44.             x = x + (dX / dY)
  45.             y = y + 1
  46.             _MEMPUT m, m.OFFSET + (w * y + x) * 4, clr
  47.         LOOP
  48.     END IF
  49.     IF x1 = x2 THEN
  50.         d = y1
  51.         DO UNTIL d > y2
  52.             _MEMPUT m, m.OFFSET + (w * d + x1) * 4, clr
  53.             d = d + 1
  54.         LOOP
  55.     END IF
  56.     IF y1 = y2 THEN
  57.         d = x1
  58.         DO UNTIL d > x2
  59.             _MEMPUT m, m.OFFSET + (w * y1 + d) * 4, clr
  60.             d = d + 1
  61.         LOOP
  62.     END IF

As it was, it took 10.3 seconds to draw 1000 times on my machine.  (I changed the limit from 10,000 to 1,000 so I wouldn't have to wait all night for results.)  The modified version does the same thing in 3.4 seconds.

$CHECKING makes a small change, with 4.3 seconds vs 3.4 seconds for run time, but the greatest change comes from dropping the multiple FUNCTION calls.  They're SLOOOOOOW in relation to other things in QB64 and should generally be avoided as much as possible in situations where speed might be a critical consideration for your program.  Changing the FOR-NEXT loops to DO-LOOP saves a little time as well, though it's mainly one of those things where you'd worry about altering and using AFTER you're certain the code runs right the first time around for you. 

Still, I don't think it'll compare to the times of LINE (x1,y1)-(x2,y2), kolor, BF, since LINE BF has been extensively optimized at the C-level by Galleon, but it's still a nice improvement overall for us.  :)
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline Petr

  • Forum Resident
  • Posts: 1720
  • The best code is the DNA of the hops.
    • View Profile
Re: Is this fast enough as general circle fill?
« Reply #42 on: June 27, 2018, 10:40:52 am »
Steve, thank you for your pretty detailed analysis of the problem. Never thought me to try the reaction time of the functions. It's a shame that such a useful thing as the functions have such a reaction time.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Is this fast enough as general circle fill?
« Reply #43 on: June 27, 2018, 11:36:50 am »
Looking at it fresh this morning, I see another way you could speed it up even more, with the use of some creative math.

        DO WHILE y <> y2
            x = x + (dX / dY)
            y = y + 1
            _MEMPUT m, m.OFFSET + (w * y + x) * 4, clr
        LOOP

If you make y1 and y2 in relation to w to begin with, you could reduce a few math operations in the DO LOOPS...

Y1 = Y1 * w: Y2 = Y2 * w     <---- first line at the start of the SUB

Y = Y + w   <----  in the DO LOOP
_MEMPUT m, m.offset + (y + x) *4, clr    <----  internally in the routine now, Y increments by _WIDTH amount naturally, and we remove a multiplication step of operations.


*************

Since X and Y are interger values, you can probably change the math to be a little more efficient elsewhere as well:

Instead of X = X + (DX / DY), make it X = X + (DX \ DY)

Integer division (\) is considerably faster than real division (/), so if you can use it and need a program to optimize speed, do so. 

****************

Lots of little tricks and tweaks which can be used to optimize speed.  The main thing you have to be *really* careful of is not to obfuscate the code beyond the point of being able to understand it in the future.  Just because you *can* make it faster, it doesn't mean you always *should* -- especially if you alter it so much you can't figure it out and debug it or alter it, at a later date.

Fast is good, but understanding is better.  ;D
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Is this fast enough as general circle fill?
« Reply #44 on: June 27, 2018, 11:45:28 am »
In fact, you may be able to remove that * 4 operation completely, if you multiple the X/Y/W values by 4 at the beginning of the program as well.

Then it'd just be a case of _MEMPUT m, m.offset + y + x, clr, which would save several math operations for each DO..LOOP.
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!