Please bear with me - long post.
In working on a project that needs to do lots of array manipulation, I was experimenting with various techniques to quickly copy very large arrays. I initially just used PowerBASIC's POKE$/PEEK$ technique (take the result of PEEK$'ing from a pointer to the source array's first element for the number of bytes in the array, and POKE$ it to a pointer to the destination array).
I wondered if I could construct a "purer" block copy method that didn't use PEEK$/POKE$, since I wasn't sure if their implementation involved the use of temporary moves that might slow down the copy of very large arrays (it seems pretty fast, but I wasn't sure about memory usage and intermediate copy operations).
Anyway, I constructed a hybrid BLOCKCOPY routine which uses pointers to very large strings, and basic string assignment statements, which I was hoping compiled to very efficient machine code, and which I was at least fairly certain wouldn't use any memory outside of the source and destination arrays themselves.
I then discovered that PowerBASIC has the ability to directly "assign" a whole array to a variant, and I decided to experiement with that technique as well (even though I was nearly certain that wouldn't be the fastest method, as any array-copy implementation that uses it would *explicitly* be using additional memory and intermediate copy operations).
I wound up writing a test program that used all three techniques to repeatedly copy a large array to another array. As the arrays grew in size, I discovered that when they reach a certain size, the PEEK$/POKE$ technique doesn't work anymore, once you have performed a variant assignment. My own BLOCKCOPY routine continues to work just fine, but the PowerBASIC PEEK$/POKE$ method doesn't work anymore once the variant assignment technique has been done even one time.
Note that the program doesn't blow up or GPF, and I can still continue to use it, but the PEEK$/POKE$, even though it executes without apparent error, doesn't actually do anything - the destination array is just never filled again by this technique.
I have included the program's source in this posting in hopes that someone can tell me whether they see any problems (bugs) in my code. I don't think I've ever posted any code here, so I'm hoping folks will be gentle if they think I am doing something dumb. It is more than possible that my logic has an obvious flaw that I am somehow just not seeing.
I've tried to organize and comment the code to make it as easy as possible to follow, since I'm asking for help understanding what is going in with my code.
TIA for any help or information anyone sees fit to offer.
Oh, BTW I am using PBCC 5.0 - I'm not sure, but I *think* the array/variant assignment stuff might be new with that version. If that is true, then this code probably won't compile on anything before 5.0.
In working on a project that needs to do lots of array manipulation, I was experimenting with various techniques to quickly copy very large arrays. I initially just used PowerBASIC's POKE$/PEEK$ technique (take the result of PEEK$'ing from a pointer to the source array's first element for the number of bytes in the array, and POKE$ it to a pointer to the destination array).
I wondered if I could construct a "purer" block copy method that didn't use PEEK$/POKE$, since I wasn't sure if their implementation involved the use of temporary moves that might slow down the copy of very large arrays (it seems pretty fast, but I wasn't sure about memory usage and intermediate copy operations).
Anyway, I constructed a hybrid BLOCKCOPY routine which uses pointers to very large strings, and basic string assignment statements, which I was hoping compiled to very efficient machine code, and which I was at least fairly certain wouldn't use any memory outside of the source and destination arrays themselves.
I then discovered that PowerBASIC has the ability to directly "assign" a whole array to a variant, and I decided to experiement with that technique as well (even though I was nearly certain that wouldn't be the fastest method, as any array-copy implementation that uses it would *explicitly* be using additional memory and intermediate copy operations).
I wound up writing a test program that used all three techniques to repeatedly copy a large array to another array. As the arrays grew in size, I discovered that when they reach a certain size, the PEEK$/POKE$ technique doesn't work anymore, once you have performed a variant assignment. My own BLOCKCOPY routine continues to work just fine, but the PowerBASIC PEEK$/POKE$ method doesn't work anymore once the variant assignment technique has been done even one time.
Note that the program doesn't blow up or GPF, and I can still continue to use it, but the PEEK$/POKE$, even though it executes without apparent error, doesn't actually do anything - the destination array is just never filled again by this technique.
I have included the program's source in this posting in hopes that someone can tell me whether they see any problems (bugs) in my code. I don't think I've ever posted any code here, so I'm hoping folks will be gentle if they think I am doing something dumb. It is more than possible that my logic has an obvious flaw that I am somehow just not seeing.
I've tried to organize and comment the code to make it as easy as possible to follow, since I'm asking for help understanding what is going in with my code.
TIA for any help or information anyone sees fit to offer.
Oh, BTW I am using PBCC 5.0 - I'm not sure, but I *think* the array/variant assignment stuff might be new with that version. If that is true, then this code probably won't compile on anything before 5.0.
Code:
#COMPILE EXE #DIM ALL '%ELEMENTCOUNT = 98664440 'Largest value for ELEMENTSIZE where variant/array assignment doesn't break future POKE$/PEEK$ ops. %ELEMENTCOUNT = 98664441 'One larger, and MOVEDATA (POKE$/PEEK$) doesn't ever work after the first variant assignment is done %ELEMENTLAST = %ELEMENTCOUNT - 1 %ARRAYBYTES = %ELEMENTCOUNT * 4 %BLOCKSIZE = 8333375 'Largest value for BLOCKSIZE that works for me without causing a GPF in BLOCKCOPY subroutine. GLOBAL glArray() AS LONG 'Source array will be filled with random data one time. It is not changed. GLOBAL glArrayCopy() AS LONG 'Copy array will be repeatedly REDIM'd and filled with a copy of glArray's data. ' ' Basic method for block copying data - right from the PowerBASIC help file. ' SUB MOVEDATA(BYVAL pDestination AS BYTE POINTER, BYVAL pSource AS BYTE POINTER, BYVAL nBytes AS LONG) POKE$ pDestination, PEEK$(pSource, nBytes) END SUB ' ' Alternate method, involving assignment of very large fixed-length strings, using only Basic language statments (with ' a little help from PowerBASIC's pointers, of course). ' ' Note that except for arrays that are a multiple of %BLOCKSIZE, the final block will be smaller than %BLOCKSIZE, and ' therefore cannot use the same block assignment statement, since that would overrun the destination array, as well as ' the source array. Instead, a loop is performed that simply copies the final, smaller block a byte at a time. Worst ' case is that this loop is executed BLOCKSIZE-1 times. ' SUB BLOCKCOPY(BYVAL pDestination AS STRING POINTER * %BLOCKSIZE, BYVAL pSource AS STRING POINTER * %BLOCKSIZE, BYVAL nBytes AS LONG) DIM iLoops AS LONG,iLast AS LONG,i AS LONG DIM pDestinationByte AS BYTE POINTER, pSourceByte AS BYTE POINTER iLoops = nBytes \ %BLOCKSIZE 'Calculate the number of complete blocks contained in the source array iLast = nBytes MOD %BLOCKSIZE 'Calculate the number of leftover bytes after all block moves are done FOR i = 0 TO iLoops - 1 'Using 0 instead of 1 since we're using i as a pointer offset @pDestination[i] = @pSource[i] 'Move a block of data NEXT i IF iLast > 0 THEN 'If there are any leftover bytes to move pDestinationByte = VARPTR(@pDestination[iLoops]) 'Make a pointer to where to start in the destination array pSourceByte = VARPTR(@pSource[iLoops]) 'Make a poiter to where to start in the source array FOR i = 0 TO iLast - 1 'Again, use 0 instead of 1 since i is used as a pointer offset @pDestinationByte[i] = @pSourceByte[i] 'Copy 1 byte NEXT i END IF END SUB ' ' Main function - allocate array storage, initialize array with random data, drop into menu loop to repeatedly use the three ' copy techniques to copy the original array to the test copy, comparing the result for correctness each time. ' FUNCTION PBMAIN () AS LONG REDIM glArray(%ELEMENTLAST) REDIM glArrayCopy(%ELEMENTLAST) DIM i AS LONG, j AS LONG DIM iCopyErrors AS LONG DIM nSkip AS LONG, sKey AS STRING*1 DIM vVar AS VARIANT STDOUT "Filling array..." FOR i = 0 TO %ELEMENTLAST glArray(i) = RND(1,%ELEMENTLAST) NEXT i DO WHILE -1 sKey = "" DO WHILE INSTR("123Q",sKey) = 0 STDOUT STDOUT "1 = Basic statements only (BLOCKCOPY)" STDOUT "2 = PowerBASIC POKE$/PEEK$ technique (MOVEDATA)" STDOUT "3 = Assignment to/from a Variant" STDOUT "Q = Quit" STDOUT STDOUT "Press a key for one of the choices above..." sKey = UCASE$(WAITKEY$) LOOP STDOUT REDIM glArrayCopy(%ELEMENTLAST) SELECT CASE sKey CASE "1" STDOUT "Making a copy of the array using BLOCKCOPY..." BLOCKCOPY VARPTR(glArrayCopy(0)), VARPTR(glArray(0)), %ARRAYBYTES CASE "2" STDOUT "Making a copy of the array using MOVEDATA..." MOVEDATA VARPTR(glArrayCopy(0)), VARPTR(glArray(0)), %ARRAYBYTES CASE "3" STDOUT "Making a copy of the array using variant assignment..." vVar = glArray() glArrayCopy() = vVar CASE "Q" EXIT DO END SELECT STDOUT "Comparing the two arrays..." nSkip = 0 iCopyErrors = 0 FOR i = 0 TO %ELEMENTLAST IF glArray(i) <> glArrayCopy(i) THEN INCR iCopyErrors IF NOT nSkip THEN STDOUT USING$("Error: glArrayCopy(#) = #_, but glArray(#) = #", i, glArrayCopy(i), i, glArray(i)) sKey = UCASE$(WAITKEY$) IF sKey = "Q" THEN EXIT FOR IF sKey = "S" THEN nSkip = -1 END IF END IF NEXT i IF i < %ELEMENTCOUNT THEN STDOUT "Comparison stopped with " & STR$(iCopyErrors) & " copy errors detected." ELSE STDOUT "Comparison complete with " & STR$(iCopyErrors) & " copy errors detected." END IF LOOP END FUNCTION
Comment