Author Topic: Steve's Video Tutorials  (Read 21328 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: Steve's Video Tutorials
« Reply #60 on: October 01, 2019, 04:27:38 pm »
Steve, in your _MEMPUT examples you have been using whole numbers - ie _MEMPUT m, m.OFFSET, 986543.
Would _MEMPUT also calculate the value? for example _MEMPUT m, m.OFFSET, 98000 + 6543 

or is it better coding practice to have the value calculated before the PUT such as  V1 = 98000 + 6543 : _MEMPUT m, m.OFFSET, V1 ?
 
Really liking your videos : finished #3 with popcorn to spare.

_MEMPUT m, m.OFFSET, 98000 + 6543 would work just fine, as long as you remember to tell it what type that numeric value represents.   (_MEMPUT m, m.OFFSET, 98000 + 6543 AS LONG, or _MEMPUT m, m.OFFSET, 98000 + 6543 AS _FLOAT, for example)

Since you generally only worry about the extra typing and complexity when you need speed, it’s usually a good idea to calculate as much as is feasible outside any MEM loop, such as I was showing a few posts above.  Referencing a normal variable takes a sliver of time, but there comes a point where you can reference it quicker than you can do calculations without it.

FOR I = 1 TO 1000000
    PRINT 1+2+3+4+5+6+7+8+9+10+11+12+13+14+15+16+17+18+19+20+21+22+23+24+25
NEXT

Temp = 1+2+3+4+5+6+7+8+9+10+11+12+13+14+15+16+17+18+19+20+21+22+23+24+25
FOR I = 1 TO 1000000
    PRINT Temp
NEXT

Given the two routines above, which is faster?  ;)

PRINT 1 would probably be faster than PRINT Temp.
PRINT 1+2 might be faster than PRINT Temp.
But I seriously doubt PRINT 1+...25, as above, is faster.



So, for your example (V1 = 98000 + 6543 : _MEMPUT m, m.OFFSET, V1), if that V1 is calculated outside your MEM loop, then it might be better (as in faster) than doing the math and calculations with the numeric values. 

If calculated inside the MEM loop, you just created a little extra work for the routine to process and look up, before finishing.  ;)
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline Qwerkey

  • Forum Resident
  • Posts: 755
    • View Profile
Re: Steve's Video Tutorials
« Reply #61 on: October 02, 2019, 01:16:28 pm »
Steve, thanks for your guidance above on achieving the best balances for speed when converting to _MEM.  For my trials, I chose something simple but related to what needs to happen in my Crossword Generator program (where all complicated numerical manipulations are, of course, pre-calculated for that extra bit of speed).  Now that I'm beginning to get a slight grip of the _MEM environment (I've even used _MEMCOPY now), I'll carry out some speed comparisons within the Crossword Generator program to see what works best for speed.  Will keep you in touch (if all this wittering hasn't put you to sleep).

Offline _vince

  • Seasoned Forum Regular
  • Posts: 422
    • View Profile
Re: Steve's Video Tutorials
« Reply #62 on: October 03, 2019, 10:42:01 am »
It’s a trade off: Speed vs readability and complexity, with increased coding time.  Onlyyou can determine when it’s worth the trouble for your program’s needs.  ;)

Perhaps _MEMx should never be used for anything unless absolutely necessary. It's a kind of dirty hack that was thrown in to make DECLARE LIBRARY viable and probably wasn't meant for anything else. Some would say mem pointers are certainly not BASIC. FreeBASIC for instance implements full blown C-style pointers which are a lot more convenient than _MEMx but most would argue that they're certainly not BASIC and I remember Galleon resented that and did not want it for QB64.  I'd love to hear your opinion on the matter, Steve.

You could also talk about using _MEMx where there isn't a really good alternative, ie lists, trees, dynamic memory, etc, see my example below. Anyways, nice tutorials, Steve, I've watched them all.  I'd love to see graphic demo videos, like trying to implement some kind of simple demo/animation/pattern/game and just talking through your thinking process and so on.  Just a suggestion ;-)

Code: QB64: [Select]
  1. defint a-z
  2. type nodeType
  3.         str as _mem
  4.         strLen as integer
  5.  
  6.         n as _mem
  7.         p as _mem
  8.  
  9. type listType
  10.         head as _mem
  11.         tail as _mem
  12.  
  13.         cur as _mem
  14.  
  15.         cx as integer
  16.         cy as integer
  17.         scroll as integer
  18.         scrollmax as integer
  19.  
  20.  
  21. sub addNodeNext (new as _mem, s$, cur as _mem, list1 as listType)
  22.         dim node as nodeType
  23.         dim temp as _mem
  24.         dim n as _mem
  25.  
  26.         list1.scrollmax = list1.scrollmax + 1
  27.  
  28.         temp = _memnew(len(node))
  29.  
  30.         nextNode n, cur
  31.  
  32.         node.strLen = len(s$)
  33.         if node.strLen > 0 then
  34.                 node.str = _memnew(len(s$))
  35.                 _memput node.str, node.str.offset, s$
  36.         end if
  37.         node.n = n
  38.         node.p = cur
  39.         _memput temp, temp.offset, node
  40.  
  41.         node = _memget(cur, cur.offset, nodeType)
  42.         node.n = temp
  43.         _memput cur, cur.offset, node
  44.  
  45.         node = _memget(n, n.offset, nodeType)
  46.         node.p = temp
  47.         _memput n, n.offset, node
  48.  
  49.         new = temp
  50.  
  51. sub addNodePrev (new as _mem, s$, cur as _mem, list1 as listType)
  52.         dim node as nodeType
  53.         dim temp as _mem
  54.         dim p as _mem
  55.  
  56.         list1.scrollmax = list1.scrollmax + 1
  57.         temp = _memnew(len(node))
  58.  
  59.         prevNode p, cur
  60.  
  61.         node.strLen = len(s$)
  62.         if node.strLen > 0 then
  63.                 node.str = _memnew(len(s$))
  64.                 _memput node.str, node.str.offset, s$
  65.         end if
  66.  
  67.         node.n = cur
  68.         node.p = p
  69.         _memput temp, temp.offset, node
  70.  
  71.         node = _memget(cur, cur.offset, nodeType)
  72.         node.p = temp
  73.         _memput cur, cur.offset, node
  74.  
  75.         node = _memget(p, p.offset, nodeType)
  76.         node.n = temp
  77.         _memput p, p.offset, node
  78.  
  79.         new = temp
  80.  
  81. sub rmNode (cur as _mem, list1 as listType)
  82.         dim node as nodeType
  83.         dim n as _mem
  84.         dim p as _mem
  85.  
  86.         list1.scrollmax = list1.scrollmax - 1
  87.         'remove the string first
  88.         node = _memget(cur, cur.offset, nodeType)
  89.         if node.strLen > 0 then
  90.                 _memfree node.str
  91.         end if
  92.  
  93.         nextNode n, cur
  94.         prevNode p, cur
  95.  
  96.         node = _memget(p, p.offset, nodeType)
  97.         node.n = n
  98.         _memput p, p.offset, node
  99.  
  100.         node = _memget(n, n.offset, nodeType)
  101.         node.p = p
  102.         _memput n, n.offset, node
  103.  
  104.         _memfree cur
  105.  
  106. sub nextNode (new as _mem, old as _mem)
  107.         dim node as nodeType
  108.  
  109.         node = _memget(old, old.offset, nodeType)
  110.         new = node.n
  111.  
  112. sub prevNode (new as _mem, old as _mem)
  113.         dim node as nodeType
  114.  
  115.         node = _memget(old, old.offset, nodeType)
  116.         new = node.p
  117.  
  118. function lenNode (cur as _mem)
  119.         dim node as nodeType
  120.  
  121.         node = _memget(cur, cur.offset, nodeType)
  122.         lenNode = node.strLen
  123.  
  124. function readNode$ (cur as _mem)
  125.         dim node as nodeType
  126.  
  127.         node = _memget(cur, cur.offset, nodeType)
  128.         if node.strLen = 0 then
  129.                 readNode$ = ""
  130.                 exit function
  131.         end if
  132.         s$ = string$(node.strLen, 0)
  133.         _memget node.str, node.str.offset, s$
  134.  
  135.         readNode$ = s$
  136.  
  137. sub writeNode (cur as _mem, s$)
  138.         dim node as nodeType
  139.  
  140.         'remove old string, free memory
  141.         node = _memget(cur, cur.offset, nodeType)
  142.         if node.strLen > 0 then _memfree node.str
  143.  
  144.         'add new string
  145.         node.strLen = len(s$)
  146.         if node.strLen > 0 then
  147.                 node.str = _memnew(len(s$))
  148.                 _memput node.str, node.str.offset, s$
  149.         end if
  150.         _memput cur, cur.offset, node
  151.  
  152. sub newList (new as listType)
  153.         dim node as nodeType
  154.  
  155.         new.head = _memnew(len(node))
  156.         new.tail = _memnew(len(node))
  157.         new.cx = 0
  158.         new.cy = 0
  159.         new.scroll = 0
  160.         new.scrollmax = 0
  161.  
  162.         s$ = "head"
  163.         node.strLen = len(s$)
  164.         node.str = _memnew(len(s$))
  165.         node.n = new.tail
  166.         node.p = new.tail
  167.         _memput node.str, node.str.offset, s$
  168.         _memput new.head, new.head.offset, node
  169.  
  170.         s$ = "tail"
  171.         node.strLen = len(s$)
  172.         node.str = _memnew(len(s$))
  173.         node.n = new.head
  174.         node.p = new.head
  175.         _memput node.str, node.str.offset, s$
  176.         _memput new.tail, new.tail.offset, node
  177.  
  178.  
  179. sub printList (cur as listType)
  180.         dim temp as _mem
  181.  
  182.         nextNode temp, cur.head
  183.         do
  184.                 if temp.offset = cur.tail.offset then exit do
  185.  
  186.                 print readNode$ (temp)
  187.                 nextNode temp, temp
  188.         loop
  189.  
  190. sub rmList (cur as listType)
  191.         dim temp as _mem
  192.         dim temp2 as _mem
  193.  
  194.         nextNode temp, cur.head
  195.         do
  196.                 if temp.offset = cur.tail.offset then exit do
  197.                
  198.                 temp2 = temp
  199.                 nextNode temp, temp2
  200.                 rmNode temp2, cur
  201.         loop
  202.  
  203.         rmNode cur.head, cur
  204.         rmNode cur.tail, cur
  205.  

Offline Qwerkey

  • Forum Resident
  • Posts: 755
    • View Profile
Re: Steve's Video Tutorials
« Reply #63 on: October 04, 2019, 06:17:33 am »
Perhaps _MEMx should never be used for anything unless absolutely necessary. It's a kind of dirty hack that was thrown in to make DECLARE LIBRARY viable and probably wasn't meant for anything else.

Gosh!  That was a bit of a surprise.  And taking Steve's comments on board, I thought that I'd look further into _MEM for my application - Crossword Generator.  In that program, I need to do lots of comparisons of letters at different places along a string (the new tried word) and at different places in a grid (it's a crossword!).  In my existing program, INSTR() is used a lot, for example.

So, I've written some more test code closer to what this program needs.
Code: QB64: [Select]
  1. 'Comparisons of string, numeral and _MEM processing (cf. Crossword Generator)
  2.  
  3. CONST False = 0, True = NOT False
  4. DIM EWord%%(9), WWord%%(9)
  5.  
  6. E$ = "**********"
  7. W$ = "@@@@@@@@@@"
  8. FOR P%% = 0 TO 9
  9.     EWord%%(P%%) = ASC(E$, P%% + 1)
  10.     WWord%%(P%%) = ASC(W$, P%% + 1)
  11. NEXT P%%
  12. DIM EMem AS _MEM
  13. EMem = _MEMNEW(10)
  14. _MEMPUT EMem, EMem.OFFSET, E$
  15. DIM WMem AS _MEM
  16. WMem = _MEMNEW(10)
  17. _MEMPUT WMem, WMem.OFFSET, W$
  18.  
  19. Start! = TIMER
  20. 'Check the new word fits existing crossing words
  21. FOR N& = 1 TO 100000000
  22.     P%% = 0
  23.     WHILE P%% < 10
  24.         IF _MEMGET(EMem, EMem.OFFSET + P%%, _UNSIGNED _BYTE) <> 41 AND _MEMGET(EMem, EMem.OFFSET + P%%, _UNSIGNED _BYTE) <> _MEMGET(WMem, WMem.OFFSET + P%%, _UNSIGNED _BYTE) THEN
  25.             'Nothing doing
  26.             P%% = P%% + 1
  27.         ELSE
  28.             P%% = P%% + 1
  29.         END IF
  30.     WEND
  31. NEXT N&
  32. PRINT "Mem", TIMER - Start!, P%%
  33. Start! = TIMER
  34. 'Check the new word fits existing crossing words
  35. FOR N& = 1 TO 100000000
  36.     P%% = 0
  37.     WHILE P%% < 10
  38.         IF EWord%%(P%%) <> 41 AND EWord%%(P%%) <> WWord%%(P%%) THEN
  39.             'Nothing doing
  40.             P%% = P%% + 1
  41.         ELSE
  42.             P%% = P%% + 1
  43.         END IF
  44.     WEND
  45. NEXT N&
  46. PRINT "Num", TIMER - Start!, P%%
  47. Start! = TIMER
  48. 'Check the new word fits existing crossing words
  49. FOR N& = 1 TO 100000000
  50.     P%% = 0
  51.     WHILE P%% < 10
  52.         IF MID$(E$, P%%, 1) <> "@" AND MID$(E$, P%%, 1) <> MID$(W$, P%%, 1) THEN
  53.             'Nothing doing
  54.             P%% = P%% + 1
  55.         ELSE
  56.             P%% = P%% + 1
  57.         END IF
  58.     WEND
  59. NEXT N&
  60. PRINT "Char", TIMER - Start!, P%%
  61. Start! = TIMER
  62. 'Check the new word fits existing crossing words
  63. FOR N& = 1 TO 100000000
  64.     P%% = 0
  65.     WHILE P%% < 10
  66.         IF _MEMGET(EMem, EMem.OFFSET + P%%, _UNSIGNED _BYTE) <> 41 AND _MEMGET(EMem, EMem.OFFSET + P%%, _UNSIGNED _BYTE) <> _MEMGET(WMem, WMem.OFFSET + P%%, _UNSIGNED _BYTE) THEN
  67.             'Nothing doing
  68.             P%% = P%% + 1
  69.         ELSE
  70.             P%% = P%% + 1
  71.         END IF
  72.     WEND
  73. NEXT N&
  74. PRINT "Mem", TIMER - Start!, P%%
  75.  
  76.  

Here, I give comparisons between _MEM processing, numeral processing and string processing doing exactly the same thing...  and the results are in:

Without $CHECKING:OFF
_MEM     31s
Numeric  21.5s
String     327.5s

With $CHECKING:OFF
_MEM     7.3s
Numeric  18.7s
String     321s

So the string processing is much much slower (no surprises), but the _MEM processing is faster than standard numeric processing with $CHECKING:OFF, but slower without this (a bit of a surprise).

What I now need to do is remove all string processing from the Crossword Generator and see what happens, and see where _MEM is useful.  Weeks and weeks of intense amusement.
« Last Edit: October 04, 2019, 06:48:24 am by Qwerkey »

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Steve's Video Tutorials
« Reply #64 on: October 04, 2019, 09:02:18 am »
Quote
”Perhaps _MEMx should never be used for anything unless absolutely necessary. It's a kind of dirty hack that was thrown in to make DECLARE LIBRARY viable and probably wasn't meant for anything else.”

Gosh!  That was a bit of a surprise.

A surprise for me as well!  I’ve used _MEM for a lot of things in the past, but I’ve never used it (or saw anyone else use it) with DECLARE LIBRARY.  If _vince has an example of something that utilizes _MEM for DECLARE LIBRARY, I’d love to see it, just to learn when/where it helps us interact with DECLARE.



As for _MEM being slower in your tests than numbers, without checking:off, I’m not all that surprised.  _MEM does a lot of error checking to protect us from ourselves, as _MEM errors tend to completely crash a program rather than just toss an error which we can continue from.  It’s that error checking which is using your time in that loop (as you can tell from the change when you turn the checking off).

One thing that’s always certain, from my experience, is that strings are sloooooow.  If you can replace them for numbers anywhere, your code will run much faster.  When dealing with single bytes like this, swapping MID$ comparisons out to ASC comparisons instead, will speed up a program a ton.  _MEM is the fastest way we can go, but just about anything is faster than trying to work directly with strings.
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline _vince

  • Seasoned Forum Regular
  • Posts: 422
    • View Profile
Re: Steve's Video Tutorials
« Reply #65 on: October 04, 2019, 03:17:51 pm »
A surprise for me as well!  I’ve used _MEM for a lot of things in the past, but I’ve never used it (or saw anyone else use it) with DECLARE LIBRARY.  If _vince has an example of something that utilizes _MEM for DECLARE LIBRARY, I’d love to see it, just to learn when/where it helps us interact with DECLARE.

That is what I believe I've heard, a lot of libraries require passing a pointer and that's pretty much the reason that prompted Galleon to add it in.  Not that it isn't handy for other things.  I believe Phil may have more insight on this.  I also think that Galleon was interested in building up the QB64 community and it was important to him that the code we post and share is as true to his idea of BASIC so that newcomers and others could easily read and translate and quickly benefit and contribute.  Imagine if someone is learning QB and wants to study a program but is faced with a bunch of complicated alien _MEMs rather than a pure BASIC solution that is totally possible. 

Not that I am at all discouraging what you guys are doing, just antagonizing, carry on ;-)

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Steve's Video Tutorials
« Reply #66 on: October 04, 2019, 03:50:34 pm »
That is what I believe I've heard, a lot of libraries require passing a pointer and that's pretty much the reason that prompted Galleon to add it in.  Not that it isn't handy for other things.  I believe Phil may have more insight on this.  I also think that Galleon was interested in building up the QB64 community and it was important to him that the code we post and share is as true to his idea of BASIC so that newcomers and others could easily read and translate and quickly benefit and contribute.  Imagine if someone is learning QB and wants to study a program but is faced with a bunch of complicated alien _MEMs rather than a pure BASIC solution that is totally possible. 

Not that I am at all discouraging what you guys are doing, just antagonizing, carry on ;-)

I think you’re getting a little confused.  You can use _OFFSET to pass pointers, whereas _MEM is actually a UDT which holds a lot of extra information other than just that offset for you.  http://www.qb64.org/wiki/OFFSET

DIM X AS LONG
PRINT _OFFSET(X)
DIM M AS _MEM: M = _MEM(X)
PRINT M.OFFSET

The above will print the same value for you twice — both are the pointer to where X is stored at in memory. 

Generally, in DECLARE LIBRARY, you simply pass the _OFFSET to what you want, and not the internal QB64 type which we call _MEM.



Quote
…a bunch of complicated alien _MEMs rather than a pure BASIC solution that is totally possible. 

In comparison to what?  A bunch of DEF SEG, PEEK, POKE statements which aren’t documented anywhere and require looking up stuff from an external chart to use, and are OS specific?

DEF SEG = &hA000 ‘Screen 13 memory color put
    OffY = Y * 320&
    OffSet = OffY + X
    Poke OffSet, C
DEF SEG

Verses:

DIM M AS _MEM: M = _MEMIMAGE(0) ‘current display screen
_MEMPUT M, M.OFFSET + Y * 320 + X, C
_MEMFREE M
« Last Edit: October 04, 2019, 04:08:36 pm by SMcNeill »
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline Qwerkey

  • Forum Resident
  • Posts: 755
    • View Profile
Re: Steve's Video Tutorials
« Reply #67 on: October 05, 2019, 07:56:48 am »
Steve, is there a _MEM equivalent of the string equality Word1$ = Word2$?  I'm avoiding everthing string-like and using only _MEM objects (of variable length).

I have an array of _MEM objects GW() and a _MEM object MInWord and wish to check that MInWord is not identical to any of the existing GW() _MEM elements (not the same length and at least one byte not identical).

The following code works, but I'd like something equivalent to Word1$ <> Word2$
Code: QB64: [Select]
  1. IF MInWord.SIZE <> GW(P%%).SIZE THEN
  2.     C%% = 0
  3.     GoodMidEnd%% = False
  4.     WHILE C%% < MInWord.SIZE AND NOT GoodMidEnd%%
  5.       IF _MEMGET(MInWord, MInWord.OFFSET + C%%, _UNSIGNED _BYTE) <> _MEMGET(GW(P%%), GW(P%%).OFFSET + C%%, _UNSIGNED _BYTE) THEN
  6.          GoodMidEnd%% = True
  7.          ELSE
  8.          C%% = C%% + 1
  9.       END IF
  10.    WEND


Offline Qwerkey

  • Forum Resident
  • Posts: 755
    • View Profile
Re: Steve's Video Tutorials
« Reply #68 on: October 08, 2019, 09:22:57 am »
Steve, at last I've found time to watch Tutorial 3.  Very instructive about _MEM usage with arrays.  I still find it unsettling that you can point the same memory block to different variables/arrays without having to do a _MEMFREE beforehand: but that's how it is.  This tutorial is quite a marathon to watch (no criticism implied - rather the reverse).  So it must have been a considerable effort to produce it.  Thanks.

Would it be true to say that
_MEMPUT m, m.OFFSET + Index * m.ELEMENTSIZE, 123 AS (Variable Type)
would be (slightly) faster if m.ELEMENTSIZE is replaced by a fixed number if you know that you will never change the ELEMENTSIZE?  It looks as if the program has to look that up every time.

Your tutorial is also instructive in showing that we all make the same types of mistakes in coding.  We all must have together expended many coding hours staring at a screen wondering where exactly we typed something somewhat awry.

I'm looking forward to Tutorial 4 (>1D arrays).  This might be my Holy Grail for a complete _MEM version of the Crossword Generator.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Steve's Video Tutorials
« Reply #69 on: October 08, 2019, 09:54:20 am »
Quote
Would it be true to say that
_MEMPUT m, m.OFFSET + Index * m.ELEMENTSIZE, 123 AS (Variable Type)
would be (slightly) faster if m.ELEMENTSIZE is replaced by a fixed number if you know that you will never change the ELEMENTSIZE?  It looks as if the program has to look that up every time.

Generally speaking, anytime you can remove an operation, or lookup, you save processing power and run faster. 

If you know your ELEMENTSIZE isn’t going to change, the best practice is to precalculate the index first, if you can.

Example:

DIM Array(10) AS LONG
DIM M AS _MEM: M = _MEM(Array())
FOR INDEX = 0 TO 10
   _MEMPUT M, M.OFFSET + INDEX *M.ELEMENTSIZE, 123 AS LONG
NEXT

Now, with the above, our memblock is pointing to a LONG Array with 11 elements (from 0 to 10).

We could do as you suggest, and use a constant value of 4 inside that loop, instead of M.ELEMENTSIZE, and our program would be slightly faster... 

OR, we could do it as so:

DIM Array(10) AS LONG
DIM M AS _MEM: M = _MEM(Array())
FOR INDEX = M.OFFSET + 0 * M.ELEMENTSIZE TO M.OFFSET + 10 * M.ELEMENTSIZE STEP M.ELEMENTSIZE
   _MEMPUT M, INDEX, 123 AS LONG
NEXT

Now our program looks up those values once, in the FOR statement, does the math once there, and we remove that whole sequence of lookups and math formulas in our _MEMPUT statement....

And you won’t get any quicker than that.
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: Steve's Video Tutorials
« Reply #70 on: October 08, 2019, 10:02:33 am »
Quote
I still find it unsettling that you can point the same memory block to different variables/arrays without having to do a _MEMFREE beforehand: but that's how it is. 

Part of this depends on what MEM points to.

DIM X AS LONG
M = _MEM(X) <— You can reuse this memblock without much worry on freeing it.  It just points to where X already exists in memory.

M = _MEMNEW(4) <— This you’d need to *always* free when finished.  If you reset the value of M to point elsewhere, how the heck would you ever free that block in the future??  Pointing M elsewhere, without freeing that MEMNEW block you reserved, just gave your program a memory leak, and it’ll eventually crash and die on you.

In my examples, I was pointing to already defined variables and arrays, so I could just point it to different things without any real worries.  Had I been using MEMNEW, I couldn’t have done that.

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: Steve's Video Tutorials
« Reply #71 on: October 12, 2019, 04:36:32 am »
Project update: In case you guys are wondering why I haven’t uploaded the next video yet, it’s all due to my ISP.  We’re *finally* being upgraded from DSL to Fiber, so my upload speed should jump from 780 KBS to 400-500 MBS, with GBS download speeds.  Only thing is, they’re ripping out the old lines (which were shitty shoddy anyway) for the new lines and my current service is even crappier than normal...  I’m supposed to be upgraded and switched over by Halloween, so I’ll try and get a “treat” up for everyone by then, if’n they finish my service swap over by then.  ;D
« Last Edit: October 12, 2019, 07:38:31 pm by SMcNeill »
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline Cobalt

  • QB64 Developer
  • Forum Resident
  • Posts: 878
  • At 60 I become highly radioactive!
    • View Profile
Re: Steve's Video Tutorials
« Reply #72 on: October 16, 2019, 07:07:36 pm »
hope they get that done before your Roll20 game comes around.
Granted after becoming radioactive I only have a half-life!

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Steve's Video Tutorials
« Reply #73 on: October 16, 2019, 08:11:32 pm »
hope they get that done before your Roll20 game comes around.

We’ve already got it up and going, though it’s only sound and no video, for now.  The overall forum is here: https://app.roll20.net/campaigns/forum/4971370/ , and you can read my group’s first week’s story here: https://app.roll20.net/forum/post/7831591/the-darkness-rising-ongoing-story-slash-summary.

I’m still catching up to where we stopped at, but there’s not much left to write up.  Our next game is Friday, 8:00 EST, and so far, it’s been a blast!
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: Steve's Video Tutorials
« Reply #74 on: November 07, 2019, 05:26:32 am »
Sorry guys.  It seems as if I fibbed to you all.  :(

I didn't manage to get a video up in time for Halloween -- my apologies.  My ISP simply didn't make it to my home in time to make the swap over to fiber for me, and I've been sitting here waiting on them to move me on up so I can upload and download without having to wait all day for things to finish...

Well, they've finally got me on the schedule, and should be here today to come inside and finish up things.  The line from the junction to my house has already been laid.  They've made all the changes to the box outside my house which was necessary, and have dug and laid the cable from the box to my house.   All that's left is for them to basically  come in today and swap out the old modem which worked with DSL for the new one for the fiber, and then I should be good to go!

And what's this mean for all of you??

I'll be a fibber who can once again upload videos of decent quality and stuff to the interwebs for everyone!!

And yes, it seems I'll even fibbed about what the next video will be about.  I promised it was going to be about "Beyond the 1st Dimension", and to go into multidimensional arrays with mem, but...

it's not.  :(

As I was working on the blueprint which I wanted to follow for the next mem tutorial, I realized that there's an important bit of information which I really should share and stress first for everyone BEFORE I get into the formulas for calculating offsets for multidimensional arrays and such with mem...

... and that's REDIM _PRESERVE and why the heck it's so frustrating to work with!  ARRRGGHHH!! 

I've heard a ton of folks complain about how it doesn't really preserve our data (it does), and how it's useless (not quite, but sometimes it can be), and since its behavior is all memory related, I thought I'd go ahead and talk about it for the next video and then the one after that will be about how to do the math to calculate multidimensional array offsets with mem.

My question for all you guys to ponder on, while I wait on the ISP folks to show up today and swap out my internets, is this one:

How do you think an array is stored in memory?

Take an array and let it be: DIM A(x, y)

Now obviously, it's just going to be a chunk of continuous memory and not some elaborate grid of data.  (If we could use grids of data, that'd be cool, but how would we layout something like DIM A(x, y, z, p, d, q)??  Do we have 6-dimensional memory chips to hold that information???)

Since we don't have multi-dimensional RAM, it's just going to be stored in a simple continuous list in memory.

A(0,0) = whatever
A(0,1) = whatever
A(0,2) = whatever

But HOW is it stored???  Remember my lesson on Little Endians and Big Chiefs?  We could store that data left to right, or right to left, or top to bottom, or bottom to top....   As long as the computer maps to the same spot each and every time, we can store it in whatever dang direction we want!

So the question is, how do we actually store it??

And that's what the video I'll be sharing later today (or next week, as reliable as my ISP isn't) will cover for everyone!

The actual code which I'll explain and use to illustrate this lesson is already written, and I'll go ahead and share it so you guys can look it over early if you want.  By itself, it's not going to help explain everything the best, as my intention is to talk and explain what it's doing step by step as I go along.  I'll show one thing, then uncomment out another and show how it'd behave if we looked at it in another way, and hopefully by the time I'm finished with it, people will understand why REDIM _PRESERVE works the way it does.  Hopefully they'll also be able to use it properly in their code with multidimensional arrays as well, after this!  ;)

Code: QB64: [Select]
  1. REDIM A(3, 4)
  2. FOR f = 0 TO 3
  3.     FOR s = 0 TO 4
  4.         total = total + 1 'Assign some totals to that arras
  5.         A(f, s) = total
  6.     NEXT
  7.  
  8. 'Let's ask a simple question: How is that data stored in memory?
  9. 'Is it stored in a left to right, top to bottom style grid?
  10.  
  11. FOR f = 0 TO 3
  12.     FOR s = 0 TO 4
  13.         LOCATE 4 * f + 1, 4 * s + 1: PRINT A(f, s)
  14.     NEXT
  15.     PRINT
  16.  
  17.  
  18. 'could if be in a top to bottom, left to right stsle grid?
  19. FOR f = 0 TO 3
  20.     FOR s = 0 TO 4
  21.         LOCATE 4 * s + 1, 4 * f + 1: PRINT A(f, s)
  22.     NEXT
  23.     PRINT
  24.  
  25.  
  26. 'heck, masbe it's top to bottom, right to left stsle storage?
  27. FOR f = 0 TO 3
  28.     FOR s = 0 TO 4
  29.         LOCATE 4 * s + 1, 4 * f + 1: PRINT A(3 - f, s)
  30.     NEXT
  31.     PRINT
  32.  
  33.  
  34. 'or it could even be bottom to top, right to left...  Right?
  35. FOR f = 0 TO 3
  36.     FOR s = 0 TO 4
  37.         LOCATE 4 * s + 1, 4 * f + 1: PRINT A(3 - f, 4 - s)
  38.     NEXT
  39.     PRINT
  40.  
  41.  
  42. 'Just just how the HELL is the data actualls stored?
  43. DIM m AS _MEM 'let's take a mem block
  44. DIM o AS _OFFSET 'we alwass need an eftra offset or three when working with mem blocks
  45. m = _MEM(A()) 'and point it at the arras
  46. DIM s1 AS SINGLE 'a single variable, as we didn't bother to dim our arras ansthing different than the default
  47. DO UNTIL finished
  48.     _MEMGET m, m.OFFSET + o, s1
  49.     PRINT s1;
  50.     'count = count + 1                    'These lines are commented out to just to help us understand what we're seeing
  51.     'IF count MOD 4 = 0 THEN PRINT        'I'll uncomment them as I run the program.
  52.     o = o + m.ELEMENTSIZE
  53.     IF o >= m.SIZE THEN finished = -1 'we're done once we've got all the elements from memors
  54.  
  55. 'END 'our actual useful lesson (I suppose) starts here.  Prior is just kind of background information...
  56.  
  57.  
  58. 'And why the hell does the direction we store our data in even matter??
  59. 'Can you explain that to us, Steve??
  60. '(Probably not, but I can at least pretend to...)
  61. REDIM _PRESERVE A(3, 4) 'this is what our original array was, simply placed here again to refresh our memory.
  62. 'REDIM _PRESERVE A(4, 4) 'what would happen to our data if we added a row to it?
  63. 'REDIM _PRESERVE A(3, 5) 'and what would happen if we added a column to it?
  64. m = _MEM(A()) 'don't forget to point m back to our memblock, since we resized it and moved where its at in memory.
  65.  
  66. finished = 0: count = 0: o = 0 'reset our variables, and let's check both results
  67. DO UNTIL finished
  68.     _MEMGET m, m.OFFSET + o, s1
  69.     PRINT s1;
  70.     o = o + m.ELEMENTSIZE
  71.     IF o >= m.SIZE THEN finished = -1 'we're done once we've got all the elements from memors
  72.  
  73. END 'And WTH?  It didn't really seem to affect things very much, no matter whether we redimed the first element
  74. 'or the second element.  Now, did it??
  75. 'Let's actually check...
  76.  
  77. FOR f = 0 TO UBOUND(A, 1)
  78.     FOR s = 0 TO UBOUND(A, 2)
  79.         LOCATE 4 * s + 1, 4 * f + 1: PRINT A(f, s)
  80.     NEXT
  81.     PRINT
  82.  
  83.  
  84.  
  85.  
  86. 'And this is why, we can safely REDIM _PRESERVE the last element in an array, and preserve our data structure,
  87. 'And why it'll get all corrupted if we REDIM _PRESERVE any other element in that array.
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!