Swift性能优化:大型数组布尔逻辑与比较运算高效实现方案
问题描述
需要处理大型Double和Boolean数组/向量,执行以下操作:
- 对
Double数组:加减乘、大小比较等运算 - 对
Bool数组:AND、OR、NOT等逻辑操作
发现Accelerate框架的vDSP在简单算术运算上性能优异,但大小比较及逻辑运算速度极慢,推测是因为用了map函数实现。求更高效的实现方式。
代码示例
import Accelerate let myDoubleArray1: [Double] = Array<Double>(repeating: 1.123, count: 1000000) let myDoubleArray2: [Double] = Array<Double>(repeating: 2.123, count: 1000000) let myBoolArray1: [Bool] = Array<Bool>(repeating: false, count: 1000000) let myBoolArray2: [Bool] = Array<Bool>(repeating: true, count: 1000000) _ = vDSP.multiply(myDoubleArray1, myDoubleArray2) // 耗时约0.5秒 - 性能出色 _ = zip(myDoubleArray1, myDoubleArray2).map {$0 > $1} // 耗时约7秒 - 太慢 _ = zip(myBoolArray1, myBoolArray2).map {$0 && $1} // 耗时约7秒 - 太慢 _ = zip(myBoolArray1, myBoolArray2).map {$0 == $1} // 耗时约7秒 - 太慢 _ = myBoolArray1.map {!$0} // 耗时约7秒 - 太慢
高效实现方案
一、Double数组的大小比较:用Accelerate批量比较函数
Accelerate提供了专门的向量比较函数,直接利用SIMD指令批量处理,性能和算术运算处于同一级别:
示例代码(大于比较)
// 判断array1每个元素是否大于array2对应元素 func compareGreater(_ array1: [Double], _ array2: [Double]) -> [Bool] { precondition(array1.count == array2.count, "数组长度必须一致") var result = [Bool](repeating: false, count: array1.count) // 调用vDSP的批量大于比较函数 vDSP_vgtD(array1, 1, array2, 1, &result, 1, vDSP_Length(array1.count)) return result } // 调用示例 let greaterResult = compareGreater(myDoubleArray1, myDoubleArray2)
其他比较操作对应函数:
- 小于:
vDSP_vltD - 等于:
vDSP_veqD - 大于等于:
vDSP_vgeD - 小于等于:
vDSP_vleD - 不等于:
vDSP_vneD
二、Bool数组的逻辑运算:转UInt8后用Accelerate位运算
Swift的Bool占1字节,Accelerate对整数向量的逻辑运算支持更完善。先将Bool转成UInt8(false→0,true→1),用vDSP位运算处理后再转回Bool:
1. NOT运算(取反)
func boolNot(_ array: [Bool]) -> [Bool] { let uint8Array = array.map { $0 ? 1 : 0 } var resultUInt8 = [UInt8](repeating: 0, count: array.count) vDSP_bitwiseNot(uint8Array, 1, &resultUInt8, 1, vDSP_Length(array.count)) return resultUInt8.map { $0 != 0 } } // 调用示例 let notResult = boolNot(myBoolArray1)
2. AND运算
func boolAnd(_ array1: [Bool], _ array2: [Bool]) -> [Bool] { precondition(array1.count == array2.count, "数组长度必须一致") let uint8Array1 = array1.map { $0 ? 1 : 0 } let uint8Array2 = array2.map { $0 ? 1 : 0 } var resultUInt8 = [UInt8](repeating: 0, count: array1.count) vDSP_bitwiseAND(uint8Array1, 1, uint8Array2, 1, &resultUInt8, 1, vDSP_Length(array1.count)) return resultUInt8.map { $0 != 0 } } // 调用示例 let andResult = boolAnd(myBoolArray1, myBoolArray2)
3. OR运算
func boolOr(_ array1: [Bool], _ array2: [Bool]) -> [Bool] { precondition(array1.count == array2.count, "数组长度必须一致") let uint8Array1 = array1.map { $0 ? 1 : 0 } let uint8Array2 = array2.map { $0 ? 1 : 0 } var resultUInt8 = [UInt8](repeating: 0, count: array1.count) vDSP_bitwiseOR(uint8Array1, 1, uint8Array2, 1, &resultUInt8, 1, vDSP_Length(array1.count)) return resultUInt8.map { $0 != 0 } } // 调用示例 let orResult = boolOr(myBoolArray1, myBoolArray2)
4. 相等判断(==)
func boolEqual(_ array1: [Bool], _ array2: [Bool]) -> [Bool] { precondition(array1.count == array2.count, "数组长度必须一致") let uint8Array1 = array1.map { $0 ? 1 : 0 } let uint8Array2 = array2.map { $0 ? 1 : 0 } var resultUInt8 = [UInt8](repeating: 0, count: array1.count) vDSP_veqU(uint8Array1, 1, uint8Array2, 1, &resultUInt8, 1, vDSP_Length(array1.count)) return resultUInt8.map { $0 != 0 } } // 调用示例 let equalResult = boolEqual(myBoolArray1, myBoolArray2)
性能说明
以上方案完全基于Accelerate的SIMD指令,能将原本7秒左右的操作压缩到毫秒级。如果需要极致性能,可以直接操作UnsafeBufferPointer避免数组拷贝,上述代码已能应对大部分场景。
内容的提问来源于stack exchange,提问作者Jay
相关产品推荐
相关产品推荐

