From 5384873018de1450272a91fd5c0fc445c023f696 Mon Sep 17 00:00:00 2001 From: Daniel Strobusch <1847260+dastrobu@users.noreply.github.com> Date: Sat, 2 Apr 2022 17:15:44 +0200 Subject: [PATCH] expose data as UnsafeMutableBufferPointer --- Sources/NdArray/Equitable.swift | 8 +- Sources/NdArray/Matrix.swift | 28 +++---- Sources/NdArray/NdArray.swift | 29 ++++--- Sources/NdArray/NdArraySlice.swift | 4 +- Sources/NdArray/Vector.swift | 22 +++--- Sources/NdArray/VectorSequence.swift | 6 +- Sources/NdArray/apply.swift | 4 +- Sources/NdArray/arithmetic.swift | 82 ++++++++++---------- Sources/NdArray/basic_functions.swift | 8 +- Sources/NdArray/copy.swift | 2 +- Sources/NdArray/init.swift | 12 +-- Sources/NdArray/reduce.swift | 4 +- Tests/NdArrayTests/VectorSequenceTests.swift | 3 +- 13 files changed, 109 insertions(+), 103 deletions(-) diff --git a/Sources/NdArray/Equitable.swift b/Sources/NdArray/Equitable.swift index cd25a23..1c2aca8 100644 --- a/Sources/NdArray/Equitable.swift +++ b/Sources/NdArray/Equitable.swift @@ -24,8 +24,8 @@ extension NdArray: Equatable where T: Equatable { case 0: return true case 1: - var l = lhs.data - var r = rhs.data + var l = lhs.dataStart + var r = rhs.dataStart let ls = lhs.strides[0] let rs = rhs.strides[0] for _ in 0..: NdArray { for i in 0...stride) + memcpy(dataStart + i * strides[0], row, colCount * MemoryLayout.stride) } case .F: for i in 0..: NdArray { precondition(row.count == colCount, "\(row.count) == \(colCount) at row \(i)") // manual memcopy for strided data for j in 0.., x: Vector) -> Vector { let lda: Int32 = Int32(a.shape[0]) let incX: Int32 = Int32(x.strides[0]) let incY: Int32 = Int32(y.strides[0]) - cblas_dgemv(order, CblasNoTrans, m, n, 1, a.data, lda, x.data, incX, 0, y.data, incY) + cblas_dgemv(order, CblasNoTrans, m, n, 1, a.dataStart, lda, x.dataStart, incX, 0, y.dataStart, incY) return y } @@ -481,7 +481,7 @@ public func * (A: Matrix, B: Matrix) -> Matrix { let lda: Int32 = Int32(a.shape[0]) let ldb: Int32 = Int32(b.shape[0]) let ldc: Int32 = Int32(c.shape[0]) - cblas_dgemm(order, CblasNoTrans, CblasNoTrans, m, n, k, 1, a.data, lda, b.data, ldb, 0, c.data, ldc) + cblas_dgemm(order, CblasNoTrans, CblasNoTrans, m, n, k, 1, a.dataStart, lda, b.dataStart, ldb, 0, c.dataStart, ldc) return c } @@ -510,7 +510,7 @@ public func * (A: Matrix, x: Vector) -> Vector { let lda: Int32 = Int32(a.shape[0]) let incX: Int32 = Int32(x.strides[0]) let incY: Int32 = Int32(y.strides[0]) - cblas_sgemv(order, CblasNoTrans, m, n, 1, a.data, lda, x.data, incX, 0, y.data, incY) + cblas_sgemv(order, CblasNoTrans, m, n, 1, a.dataStart, lda, x.dataStart, incX, 0, y.dataStart, incY) return y } @@ -544,6 +544,6 @@ public func * (A: Matrix, B: Matrix) -> Matrix { let lda: Int32 = Int32(a.shape[0]) let ldb: Int32 = Int32(b.shape[0]) let ldc: Int32 = Int32(c.shape[0]) - cblas_sgemm(order, CblasNoTrans, CblasNoTrans, m, n, k, 1, a.data, lda, b.data, ldb, 0, c.data, ldc) + cblas_sgemm(order, CblasNoTrans, CblasNoTrans, m, n, k, 1, a.dataStart, lda, b.dataStart, ldb, 0, c.dataStart, ldc) return c } diff --git a/Sources/NdArray/NdArray.swift b/Sources/NdArray/NdArray.swift index 24a3ded..a9f571d 100644 --- a/Sources/NdArray/NdArray.swift +++ b/Sources/NdArray/NdArray.swift @@ -9,8 +9,13 @@ public enum Contiguous { open class NdArray: CustomDebugStringConvertible, CustomStringConvertible { - /// data buffer - internal(set) public var data: UnsafeMutablePointer + /// data buffer start + internal var dataStart: UnsafeMutablePointer + + /// data buffer for row data access + public var data: UnsafeMutableBufferPointer { + UnsafeMutableBufferPointer(start: dataStart, count: count) + } /// length of the buffer internal var count: Int @@ -53,7 +58,7 @@ open class NdArray: CustomDebugStringConvertible, /// create a new array without initializing any memory public required init(empty count: Int = 0) { self.count = count - data = UnsafeMutablePointer.allocate(capacity: count) + dataStart = UnsafeMutablePointer.allocate(capacity: count) if count == 0 { shape = [0] } else { @@ -95,7 +100,7 @@ open class NdArray: CustomDebugStringConvertible, } else { self.owner = a.owner } - self.data = a.data + self.dataStart = a.dataStart self.count = a.count self.shape = a.shape self.strides = a.strides @@ -104,7 +109,7 @@ open class NdArray: CustomDebugStringConvertible, deinit { if ownsData { - data.deallocate() + dataStart.deallocate() } } @@ -160,7 +165,7 @@ open class NdArray: CustomDebugStringConvertible, /// create an 1D NdArray from a plain array public convenience init(_ a: [T]) { self.init(empty: a.count) - data.initialize(from: a, count: a.count) + dataStart.initialize(from: a, count: a.count) } /// create an 2D NdArray from a plain array @@ -179,7 +184,7 @@ open class NdArray: CustomDebugStringConvertible, for i in 0...stride) + memcpy(dataStart + i * strides[0], row, colCount * MemoryLayout.stride) } case .F: for i in 0..: CustomDebugStringConvertible, for j in 0...stride) + memcpy(dataStart + i * strides[0] + j * strides[1], aij, kCount * MemoryLayout.stride) } } case .F: @@ -235,11 +240,11 @@ open class NdArray: CustomDebugStringConvertible, if isEmpty { return [] } - return Array(UnsafeBufferPointer(start: data, count: count)) + return Array(UnsafeBufferPointer(start: dataStart, count: count)) } public var debugDescription: String { - let address = String(format: "%p", Int(bitPattern: data)) + let address = String(format: "%p", Int(bitPattern: dataStart)) return "NdArray(shape: \(shape), strides: \(strides), data: \(address))" } @@ -473,11 +478,11 @@ extension NdArray { /// - Returns: true if data regions of this array overlap with data region of the other array public func overlaps(_ other: NdArray) -> Bool { // check if other starts within our memory - if other.data >= self.data && other.data < self.data + self.count { + if other.dataStart >= self.dataStart && other.dataStart < self.dataStart + self.count { return true } // check if our memory starts within other memory - if self.data >= other.data && self.data < other.data + other.count { + if self.dataStart >= other.dataStart && self.dataStart < other.dataStart + other.count { return true } return false diff --git a/Sources/NdArray/NdArraySlice.swift b/Sources/NdArray/NdArraySlice.swift index 9cb3d0a..4611fa6 100644 --- a/Sources/NdArray/NdArraySlice.swift +++ b/Sources/NdArray/NdArraySlice.swift @@ -28,7 +28,7 @@ internal class NdArraySlice: NdArray { super.init(a) let start = a.flatIndex(startIndex) - data = a.data + start + dataStart = a.dataStart + start count = a.len } @@ -318,7 +318,7 @@ internal class NdArraySlice: NdArray { } public override var debugDescription: String { - let address = String(format: "%p", Int(bitPattern: data)) + let address = String(format: "%p", Int(bitPattern: dataStart)) var sliceDescription = sliceDescription.joined() if sliceDescription == "" { sliceDescription = "-" diff --git a/Sources/NdArray/Vector.swift b/Sources/NdArray/Vector.swift index fe234f9..5295ccb 100644 --- a/Sources/NdArray/Vector.swift +++ b/Sources/NdArray/Vector.swift @@ -18,7 +18,7 @@ open class Vector: NdArray, Sequence { /// create an 1D NdArray from a plain array public convenience init(_ a: [T]) { self.init(empty: a.count) - data.initialize(from: a, count: a.count) + dataStart.initialize(from: a, count: a.count) } public required convenience init(copy a: NdArray) { @@ -64,12 +64,12 @@ public extension Vector where T == Double { Precondition failed while trying to compute dot product for vectors from \(debugDescription) and \(y.debugDescription). """) let n = Int32(shape[0]) - return cblas_ddot(n, data, Int32(strides[0]), y.data, Int32(y.strides[0])) + return cblas_ddot(n, dataStart, Int32(strides[0]), y.dataStart, Int32(y.strides[0])) } func norm2() -> T { let n = Int32(shape[0]) - return cblas_dnrm2(n, data, Int32(strides[0])) + return cblas_dnrm2(n, dataStart, Int32(strides[0])) } func sort(order: SortOrder = .ascending) { @@ -83,18 +83,18 @@ public extension Vector where T == Double { } if isContiguous { - vDSP_vsortD(data, n, sortOrder) + vDSP_vsortD(dataStart, n, sortOrder) } else { // make a copy sort it and copy back if array is not contiguous let cpy = Vector(copy: self) - vDSP_vsortD(cpy.data, n, sortOrder) + vDSP_vsortD(cpy.dataStart, n, sortOrder) self[[0...]] = cpy[[0...]] } } func reverse() { let n = vDSP_Length(shape[0]) - vDSP_vrvrsD(data, strides[0], n) + vDSP_vrvrsD(dataStart, strides[0], n) } } @@ -106,12 +106,12 @@ public extension Vector where T == Float { Precondition failed while trying to compute dot product for vectors from \(debugDescription) and \(y.debugDescription). """) let n = Int32(shape[0]) - return cblas_sdot(n, data, Int32(strides[0]), y.data, Int32(y.strides[0])) + return cblas_sdot(n, dataStart, Int32(strides[0]), y.dataStart, Int32(y.strides[0])) } func norm2() -> T { let n = Int32(shape[0]) - return cblas_snrm2(n, data, Int32(strides[0])) + return cblas_snrm2(n, dataStart, Int32(strides[0])) } func sort(order: SortOrder = .ascending) { @@ -125,18 +125,18 @@ public extension Vector where T == Float { } if isContiguous { - vDSP_vsort(data, n, sortOrder) + vDSP_vsort(dataStart, n, sortOrder) } else { // make a copy sort it and copy back if array is not contiguous let cpy = Vector(copy: self) - vDSP_vsort(cpy.data, n, sortOrder) + vDSP_vsort(cpy.dataStart, n, sortOrder) self[[0...]] = cpy[[0...]] } } func reverse() { let n = vDSP_Length(shape[0]) - vDSP_vrvrs(data, strides[0], n) + vDSP_vrvrs(dataStart, strides[0], n) } } diff --git a/Sources/NdArray/VectorSequence.swift b/Sources/NdArray/VectorSequence.swift index 258b1a0..9887b87 100644 --- a/Sources/NdArray/VectorSequence.swift +++ b/Sources/NdArray/VectorSequence.swift @@ -32,9 +32,9 @@ public extension Vector { func makeIterator() -> VectorIterator { if isEmpty { - return VectorIterator(baseAddress: data, stride: 0, count: 0) + return VectorIterator(baseAddress: dataStart, stride: 0, count: 0) } - return VectorIterator(baseAddress: data, stride: strides[0], count: shape[0]) + return VectorIterator(baseAddress: dataStart, stride: strides[0], count: shape[0]) } /// - Returns: shape[0] or 0 if vector is empty @@ -46,7 +46,7 @@ public extension Vector { /// If the vector is not contiguous, body is not called and nil is returned. func withContiguousStorageIfAvailable(_ body: (UnsafeBufferPointer) throws -> R) rethrows -> R? { if isContiguous { - return try body(UnsafeBufferPointer(start: data, count: count)) + return try body(UnsafeBufferPointer(start: dataStart, count: count)) } else { return nil } diff --git a/Sources/NdArray/apply.swift b/Sources/NdArray/apply.swift index f459f8b..7f63445 100644 --- a/Sources/NdArray/apply.swift +++ b/Sources/NdArray/apply.swift @@ -71,13 +71,13 @@ public extension NdArray { func apply(_ f: (T) throws -> T) rethrows { try apply1d(f1d: { n in let s = strides[0] - var p = data + var p = dataStart for _ in 0.. T { var r = T.zero apply1d(f1d: { n in - vDSP_sveD(data, strides[0], &r, vDSP_Length(n)) + vDSP_sveD(dataStart, strides[0], &r, vDSP_Length(n)) }, fContiguous: { n in - vDSP_sveD(data, 1, &r, vDSP_Length(n)) + vDSP_sveD(dataStart, 1, &r, vDSP_Length(n)) }, fSlice: { s in r += s.sum() }) @@ -264,9 +264,9 @@ public extension NdArray where T == Float { } var r = data[0] apply1d(f1d: { n in - vDSP_maxv(data, strides[0], &r, vDSP_Length(n)) + vDSP_maxv(dataStart, strides[0], &r, vDSP_Length(n)) }, fContiguous: { n in - vDSP_maxv(data, 1, &r, vDSP_Length(n)) + vDSP_maxv(dataStart, 1, &r, vDSP_Length(n)) }, fSlice: { s in r = Swift.max(r, s.max()!) }) @@ -280,9 +280,9 @@ public extension NdArray where T == Float { } var r = data[0] apply1d(f1d: { n in - vDSP_minv(data, strides[0], &r, vDSP_Length(n)) + vDSP_minv(dataStart, strides[0], &r, vDSP_Length(n)) }, fContiguous: { n in - vDSP_minv(data, 1, &r, vDSP_Length(n)) + vDSP_minv(dataStart, 1, &r, vDSP_Length(n)) }, fSlice: { s in r = Swift.min(r, s.min()!) }) @@ -298,9 +298,9 @@ public extension NdArray where T == Float { Precondition failed while trying to add \(x.debugDescription) to \(debugDescription). """) apply1d(other: x, f1d: { n in - cblas_saxpy(Int32(n), alpha, x.data, Int32(x.strides[0]), data, Int32(strides[0])) + cblas_saxpy(Int32(n), alpha, x.dataStart, Int32(x.strides[0]), dataStart, Int32(strides[0])) }, fContiguous: { n in - cblas_saxpy(Int32(n), alpha, x.data, 1, data, 1) + cblas_saxpy(Int32(n), alpha, x.dataStart, 1, dataStart, 1) }, fSlice: { s, o in s.add(alpha, o) }) @@ -315,9 +315,9 @@ public extension NdArray where T == Float { Precondition failed while trying to add \(x.debugDescription) to \(debugDescription). """) apply1d(other: x, f1d: { n in - catlas_saxpby(Int32(n), alpha, x.data, Int32(x.strides[0]), beta, data, Int32(strides[0])) + catlas_saxpby(Int32(n), alpha, x.dataStart, Int32(x.strides[0]), beta, dataStart, Int32(strides[0])) }, fContiguous: { n in - catlas_saxpby(Int32(n), alpha, x.data, 1, beta, data, 1) + catlas_saxpby(Int32(n), alpha, x.dataStart, 1, beta, dataStart, 1) }, fSlice: { s, o in s.add(alpha, o, beta) }) @@ -326,9 +326,9 @@ public extension NdArray where T == Float { /// in place multiplication by a scalar func multiply(by x: T) { apply1d(f1d: { n in - cblas_sscal(Int32(n), x, data, Int32(strides[0])) + cblas_sscal(Int32(n), x, dataStart, Int32(strides[0])) }, fContiguous: { n in - cblas_sscal(Int32(n), x, data, 1) + cblas_sscal(Int32(n), x, dataStart, 1) }, fSlice: { s in s *= x }) @@ -341,9 +341,9 @@ public extension NdArray where T == Float { /// set all values to a new constant value func set(_ alpha: T) { apply1d(f1d: { n in - catlas_sset(Int32(n), alpha, data, Int32(strides[0])) + catlas_sset(Int32(n), alpha, dataStart, Int32(strides[0])) }, fContiguous: { n in - catlas_sset(Int32(n), alpha, data, 1) + catlas_sset(Int32(n), alpha, dataStart, 1) }, fSlice: { s in s.set(alpha) }) @@ -353,9 +353,9 @@ public extension NdArray where T == Float { func sum() -> T { var r = T.zero apply1d(f1d: { n in - vDSP_sve(data, strides[0], &r, vDSP_Length(n)) + vDSP_sve(dataStart, strides[0], &r, vDSP_Length(n)) }, fContiguous: { n in - vDSP_sve(data, 1, &r, vDSP_Length(n)) + vDSP_sve(dataStart, 1, &r, vDSP_Length(n)) }, fSlice: { s in r += s.sum() }) diff --git a/Sources/NdArray/basic_functions.swift b/Sources/NdArray/basic_functions.swift index 58f891a..40aac9d 100644 --- a/Sources/NdArray/basic_functions.swift +++ b/Sources/NdArray/basic_functions.swift @@ -18,9 +18,9 @@ public func abs>(_ a: T) -> T where K: Comparabl /// see ``vDSP_vabsD``. public func abs>(_ a: T, out b: T) { a.apply1d(other: b, f1d: { _ in - vDSP_vabsD(a.data, a.strides[0], b.data, b.strides[0], vDSP_Length(a.shape[0])) + vDSP_vabsD(a.dataStart, a.strides[0], b.dataStart, b.strides[0], vDSP_Length(a.shape[0])) }, fContiguous: { n in - vDSP_vabsD(a.data, 1, b.data, 1, vDSP_Length(n)) + vDSP_vabsD(a.dataStart, 1, b.dataStart, 1, vDSP_Length(n)) }, fSlice: { ai, bi in abs(ai, out: bi) }) @@ -149,9 +149,9 @@ public func logb>(_ a: T) -> T { /// see ``vDSP_vabsD``. public func abs>(_ a: T, out b: T) { a.apply1d(other: b, f1d: { _ in - vDSP_vabs(a.data, a.strides[0], b.data, b.strides[0], vDSP_Length(a.shape[0])) + vDSP_vabs(a.dataStart, a.strides[0], b.dataStart, b.strides[0], vDSP_Length(a.shape[0])) }, fContiguous: { n in - vDSP_vabs(a.data, 1, b.data, 1, vDSP_Length(n)) + vDSP_vabs(a.dataStart, 1, b.dataStart, 1, vDSP_Length(n)) }, fSlice: { ai, bi in abs(ai, out: bi) }) diff --git a/Sources/NdArray/copy.swift b/Sources/NdArray/copy.swift index fa23def..f54dbf8 100644 --- a/Sources/NdArray/copy.swift +++ b/Sources/NdArray/copy.swift @@ -19,7 +19,7 @@ public extension NdArray { if (isCContiguous && out.isCContiguous) || (isFContiguous && out.isFContiguous) { // since buffers may overlap, use memmove instead of memcpy - memmove(out.data, data, count * MemoryLayout.stride) + memmove(out.dataStart, dataStart, count * MemoryLayout.stride) } else if overlaps(out) { // if memory overlaps and is not 1d aligned make an intermediate copy if out.isFContiguous { diff --git a/Sources/NdArray/init.swift b/Sources/NdArray/init.swift index a46680e..d1562a0 100644 --- a/Sources/NdArray/init.swift +++ b/Sources/NdArray/init.swift @@ -94,7 +94,7 @@ public extension NdArray where T == Double { static func range(from start: T, to stop: T, by step: T = 1) -> Self { let n = arange(start: start, stop: stop, step: step) let a = NdArray(empty: n) - vramp(start: start, step: step, data: a.data, n: n, vramp: vDSP_vrampD) + vramp(start: start, step: step, data: a.dataStart, n: n, vramp: vDSP_vrampD) let r = self.init(a) r.stealOwnership() return r @@ -138,7 +138,7 @@ public extension NdArray where T == Double { static func repeating(_ x: T, count: Int) -> Self { let a = NdArray(empty: count) - catlas_dset(Int32(count), x, a.data, 1) + catlas_dset(Int32(count), x, a.dataStart, 1) let r = self.init(a) r.stealOwnership() return r @@ -158,7 +158,7 @@ public extension NdArray where T == Float { static func range(from start: T, to stop: T, by step: T = 1) -> Self { let n = arange(start: start, stop: stop, step: step) let a = NdArray(empty: n) - vramp(start: start, step: step, data: a.data, n: n, vramp: vDSP_vramp) + vramp(start: start, step: step, data: a.dataStart, n: n, vramp: vDSP_vramp) let r = self.init(a) r.stealOwnership() return r @@ -202,7 +202,7 @@ public extension NdArray where T == Float { static func repeating(_ x: T, count: Int) -> Self { let a = NdArray(empty: count) - catlas_sset(Int32(count), x, a.data, 1) + catlas_sset(Int32(count), x, a.dataStart, 1) let r = self.init(a) r.stealOwnership() return r @@ -223,7 +223,7 @@ public extension NdArray where T == Int { static func range(from start: T, to stop: T, by step: T = 1) -> Self { let n = arange(start: Double(start), stop: Double(stop), step: Double(step)) let a = self.init(empty: n) - var p = a.data + var p = a.dataStart for i in stride(from: start, to: stop, by: step) { p.initialize(to: i) p += 1 @@ -235,7 +235,7 @@ public extension NdArray where T == Int { static func zeros(_ count: Int) -> Self { let a = self.init(empty: count) - memset(a.data, 0, count * MemoryLayout.stride) + memset(a.dataStart, 0, count * MemoryLayout.stride) let r = self.init(a) r.stealOwnership() return r diff --git a/Sources/NdArray/reduce.swift b/Sources/NdArray/reduce.swift index a762440..2b73407 100644 --- a/Sources/NdArray/reduce.swift +++ b/Sources/NdArray/reduce.swift @@ -13,13 +13,13 @@ public extension NdArray { var r = initialResult try apply1d(f1d: { n in let s = strides[0] - var p = data + var p = dataStart for _ in 0..([1, 2, 3]) XCTAssertTrue(a.isContiguous) let r: Int? = a.withContiguousStorageIfAvailable { - XCTAssertEqual($0.baseAddress!, a.data) + XCTAssertEqual($0.baseAddress!, a.data.baseAddress!) + XCTAssertEqual($0.count, a.data.count) XCTAssertEqual($0.count, a.shape[0]) return 42 }