Why does using `cblas_ccopy` cause intermittent memory errors?

142 Views Asked by At

The code below simply tries to copy values from one pointer to another, using cblas_ccopy, but it results in an malloc: *** error ... incorrect checksum for freed object error about one third of the time. Why doesn't it always work?

import Accelerate

func testCopy() {

    // set capacity
    let capacity: Int = 1000

    // destination array
    let destinationArray = UnsafeMutablePointer<Float>.allocate(capacity: capacity)
    destinationArray.initialize(repeating: 0, count: capacity)

    // source array
    let sourceArray = UnsafeMutablePointer<Float>.allocate(capacity: capacity)
    sourceArray.initialize(repeating: 1, count: capacity)

    // copy values
    cblas_ccopy(Int32(capacity),
                UnsafeRawPointer(sourceArray),
                1,
                UnsafeMutableRawPointer(destinationArray),
                1)

    // check to see if values were copied
    for idx in 0..<capacity {
        print(idx, destinationArray[idx])
    }
}

testCopy()

When running this as a unit test, the error is objc[44736]: autorelease pool page 0x7fecb903c000 corrupted. When running it as a script, the error is incorrect checksum.

I tried setting a breakpoint in malloc_error_break but I don't understand how to interpret the output.

I also tried passing sourceArray and destinationArray to cblas_ccopy as arguments, without converting them to raw pointers, but that did not help.

2

There are 2 best solutions below

0
On BEST ANSWER

Use cblas_scopy instead of cblas_ccopy. cblas_ccopy copies (single precision) complex numbers which are twice the size of the single precision numbers you actually are using, so you're overrunning the end of the buffer.

0
On

The c-prefix in _ccopy means the element type is single-precision complex. So, in your cblas_ccopy(Int32(capacity),...), both pointers need to contain capacity elements of single-precision complex numbers, which is 2 * capacity elements of single-precision floating point numbers.

You just allocate capacity elements of single-precision floating point numbers. You may know what happens when memory is accessed exceeding the memory limit.

Try doubling the allocation size:

let destinationArray = UnsafeMutablePointer<Float>.allocate(capacity: 2 * capacity)
destinationArray.initialize(repeating: 0, count: 2 * capacity)

// source array
let sourceArray = UnsafeMutablePointer<Float>.allocate(capacity: 2 * capacity)
sourceArray.initialize(repeating: 1, count: 2 * capacity)

// copy values
cblas_ccopy(Int32(capacity), //<- do not double here.
            UnsafeRawPointer(sourceArray),
            1,
            UnsafeMutableRawPointer(destinationArray),
            1)

(Or try allocating capacity elements of single-precision complex numbers, not Float.)