BlitzMax comes with a number of data structures. Arrays are a staple of the BASIC dialect, but they can be a pain to use if items need to be frequently added and removed. BlitzMax’s TList structure is more flexible, but comes with a performance hit when casting objects.

Resizing arrays

Normally I’d do something like this to resize an array:

Framework brl.basic

' Create initial array.
Local original:Int[] = [1, 2, 3, 4, 5]
Print original.length
' => 5

' Resize to hold 6 elements.
original = ResizeArray(original, 6)
Print original.length
' => 6

Function ResizeArray:Int[](target:Int[], newSize:Int)
    Local dest:Int[newSize]

    For Local i:Int = 0 to target.Length - 1
        dest[i] = target[i]
    Next
    
    Return dest
End Function

It works, but it’s not the most efficient. Things slow down when the target array is large. The main For loop can be unrolled or converted to a Repeat...Until loop, but that’s a small optimization. It would be nice to remove the loop entirely.

bmk-ng comes with a handy function called ArrayCopy for copying the contents of one array to another.

I wrote a quick performance test to compare ArrayCopy against copying elements using a For...Next loop. It’s probably not that accurate, but the results are encouraging.

Time to copy a 1,000 element array 1,000,000 times:

Method Time (millisecs)
Copy (For...Next) 2249
ArrayCopy 1127
Object Copy (For...Next) 3264
Object ArrayCopy 2152

For simple Integer arrays, ArrayCopy is about twice as fast as copying using a For...Next loop. Copying objects is slower, but still shows a good speed improvement. Nice!

But I wondered if there was a better way to implement resizing.

BlitzMax has built-in (and mostly undocumented) functionality for copying and resizing arrays using “slices”.

Instead of creating a larger array and copying the contents, resizing can be done in a single command:

Local myArray:Int[10]

' Resize array to be 20 elements.
myArray = myArray[..20]

Copying can be done in a single line too:

Local target:Int[10]
Local destination:Int[10]

destination = myArray[..]

Using slices reduces the ResizeArray method to this:

Method ResizeArray:Int[](target:Int[], newCapacity:Int)
    Return target[..newCapacity]
End Method

I’ve resized arrays using For...Next loops in a few of my modules. ObjectBag#_grow used it when resizing the internal storage array.

A quick performance test gave the following results for adding 10,000 items to a bag 10,000 times:

Method Time (millisecs)
Original _grow 821
ArrayCopy _grow 559
Slices _grow 482

ArrayCopy is a big improvement, but slices are even faster (and work with vanilla BlitzMax too).


The original issue that spawned this post: #2 Array growing using ArrayCopy