Accelerate框架SparseMultiply随机触发EXC_BAD_ACCESS崩溃问题排查
Accelerate框架SparseMultiply随机异常问题排查
问题描述
使用Apple Accelerate框架的Sparse Solvers组件调用SparseMultiply计算SparseMatrix_Double与DenseVector_Double的乘积时,出现随机异常:有时运行正常,有时触发EXC_BAD_ACCESS崩溃,有时返回错误结果。已开启地址 sanitizer及方案设置中“Diagnostics”标签页的各类内存检测选项,但均未检测到问题。
示例代码
func matrixProductExperiment() { // Given sparse matrix A, and dense vector X, calculate product of dense vector Y, i.e., y = Ax: // // A X = Y // ( 10.0 1.0 2.5 ) ( 2.20 ) = ( 32.025 ) // ( 1.0 12.0 -0.3 1.1 ) ( 2.85 ) = ( 38.720 ) // ( -0.3 9.5 ) ( 2.79 ) = ( 25.650 ) // ( 2.5 1.1 6.0 ) ( 2.87 ) = ( 25.855 ) // We use a format known as Compressed Sparse Column (CSC) to store the data. Further, // as the matrix is symmetric, we only need to store half the data. The CSC format // stores the matrix as a series of column vectors where only the non-zero entries are // specified, stored as the pair of (row index, value), although in separate arrays: let rowCount: Int32 = 4 let columnCount: Int32 = 4 var matrixValues = [ 10.0, 1.0, 2.5, 12.0, -0.3, 1.1, 9.5, 6.0 ] var rowIndices: [Int32] = [ 0, 1, 3, 1, 2, 3, 2, 3 ] var columnStarts = [ 0, 3, 6, 7 ] // vector X var xValues = [ 2.20, 2.85, 2.79, 2.87 ] // In this library, this raw information is all wrapper into a flexible data type // that allows for more complex use cases in other situations. rowIndices.withUnsafeMutableBufferPointer { rowIndicesPointer in columnStarts.withUnsafeMutableBufferPointer { columnStartsPointer in matrixValues.withUnsafeMutableBufferPointer { valuesPointer in xValues.withUnsafeMutableBufferPointer { xPointer in let a = SparseMatrix_Double( // Structure of the matrix, without any values structure: SparseMatrixStructure( rowCount: rowCount, columnCount: columnCount, columnStarts: columnStartsPointer.baseAddress!, rowIndices: rowIndicesPointer.baseAddress!, // Matrix meta-data attributes: SparseAttributes_t( transpose: false, triangle: SparseLowerTriangle, kind: SparseSymmetric, _reserved: 0, _allocatedBySparse: false ), blockSize: 1), // Numerical values of the matrix data: valuesPointer.baseAddress! ) let x = DenseVector_Double(count: columnCount, data: xPointer.baseAddress!) let y = [Double](unsafeUninitializedCapacity: Int(rowCount)) { resultBuffer, count in let y = DenseVector_Double(count: rowCount, data: resultBuffer.baseAddress!) SparseMultiply(a, x, y) count = Int(rowCount) } print(y) // [32.025, 38.72, 25.65, 25.855] – Correct } } } } }
问题根源与修复
核心原因:CSC格式的columnStarts数组不符合规范
Accelerate的Sparse Solvers对压缩稀疏列(CSC)格式的columnStarts数组有强制要求:
- 数组长度必须等于矩阵列数 + 1
- 最后一个元素必须等于矩阵的非零元素总数
你的代码中:
- 矩阵列数为4,
columnStarts仅包含4个元素(缺少1个) - 非零元素总数是8,但
columnStarts最后一个元素是7,而非8
这种不完整的数组会导致SparseMultiply在遍历矩阵时出现内存越界访问。由于内存越界的行为具有随机性——有时刚好访问到进程地址空间内的合法内存(表现为运行正常或返回错误结果),有时访问到非法内存(触发EXC_BAD_ACCESS崩溃),内存检测工具无法精准捕获这类“合法但错误”的内存访问。
修复步骤
- 修正
columnStarts数组,补充最后一个元素为非零元素总数:
var columnStarts = [0, 3, 6, 7, 8]
- 验证矩阵属性参数:确认
SparseAttributes_t中SparseSymmetric和SparseLowerTriangle的设置与实际矩阵结构匹配,确保仅存储了下三角部分的非零元素,避免因矩阵结构描述错误导致的计算异常。
内容的提问来源于stack exchange,提问作者Rob
相关产品推荐
相关产品推荐

