来自ArraySlice

时间:2016-01-07 19:25:28

标签: swift

我正在尝试确定创建自己的连续内存容器的“Swift-y”方式(在我的特定情况下,我正在构建n维数组)。我希望我的容器尽可能接近Swift的内置阵列 - 在功能和可用性方面。

我需要访问指向容器内存的指针,例如Accelerate和BLAS操作。

我想知道ArraySlice的指针是指向切片的第一个元素,还是指向其基数的第一个元素。

当我尝试测试UnsafePointer<Int>(array) == UnsafePointer<Int>(array[1...2])时,看起来Swift不允许从ArraySlices构建指针(或者我只是错误地做了)。

我正在寻找关于哪种方式最“Swift-y”的建议?

据我所知,在切片时,以下是真的:

let array = [1, 2, 3]
array[1] == array[1...2][1]

array[1...2][0] != 2 # index out of bounds error

换句话说,索引总是相对于基本数组执行。

建议:我们应该返回指向基地第一个元素的指针。因为切片是相对于它们的基础。

然而,通过切片的迭代(显然)只考虑该切片的元素:

for i in array[1..2] # i takes on 2 followed by 3

建议:我们应该返回指向切片第一个元素的指针。因为切片有自己的起点。

如果我的用户想要在BLAS操作中对切片进行操作,那么可以直观地预期:

mmul(matrix1[1...2, 0...1].pointer, matrix2[4...5, 0...1].pointer)

指向切片的第一个元素,但我不知道这是否是Swift ArraySlice可以做的事情。

我的问题:容器切片对象的指针是指向切片的第一个元素,还是基本容器的第一个元素

2 个答案:

答案 0 :(得分:2)

此操作不安全:

UnsafePointer<Int>(array)

你的意思是:

array.withUnsafeBufferPointer { ... }

这也适用于您的类型,并且是您应该用于与BLAS和Accelerate互操作的模式。您不应该尝试使用pointer方法IMO。

当你实际访问指针时,array没有承诺会继续存在,即使这种情况发生在同一行代码中。 ARC可以自由地迅速消灭这种记忆。

UnsafeBufferPointer实际上是一个非常好的类型,因为它已被承诺是连续的并且它表现为集合。

我的建议是在内部管理自己的记忆,可能只有ManagedBuffer,但可能只有UnsafeMutablePointer你自己allocdestroy。管理数据布局以使其与Accelerate兼容非常重要。你不想要Array<Array<UInt8>>。这将增加太多的结构。你想要一个字符串blob,你可以在good-ol&#39; C方式(行*宽度+列等)。您可能根本不希望切片直接返回指针。您的mmul函数可能需要特殊的逻辑来理解如何通过最少的复制从片中拉出所需的片段,以便它与vDSP_mmul一起使用。 &#34;通用&#34;和#34;加速&#34;很少一起去。

例如,考虑到这一点:

mmul(matrix1[1...2, 0...1].pointer, matrix2[4...5, 0...1].pointer)

(显然我假设你的真实矩阵要大得多;这种矩阵对发送到vDSP没有多大意义。)

由于此内存未正确布局,因此您必须在此处自行编写mmul。所以你不妨传递切片。然后你会做类似的事情(完全未经测试,未经编译,我确信语法错误):

mmul(m1: MatrixSlice, m2: MatrixSlice) -> Matrix {
    var s1 = UnsafeMutablePointer<Float>.alloc(m1.rows * m1.columns)
    // use vDSP_vgathr to copy each sliced row out of m1 into s1
    var s2 = UnsafeMutablePointer<Float>.alloc(m2.rows * m2.columns)
    // use vDSP_vgathr to copy each sliced row out of m2 into s2

    var result = UnsafeMutablePointer<Float>.alloc(m1.rows * m2.columns)

    vDSP_mmul(s1, 1, s2, 1, result, 1, m1.rows, m2.columns, m1.columns)
    s1.destroy()
    s2.destroy()

    // This will need to call result.move() or moveInitializeFrom or something
    return Matrix(result)
 }

我只是把东西扔到这里,但这可能就是你想要的那种结构。

关于是否通常由Swift传递指向容器或数据的指针的基本问题,答案很遗憾&#34; magic&#34;对于Array,没有其他人。将一个数组传递给需要指针的东西会神奇地(由编译器而不是stdlib)传递一个指向存储器的指针。没有其他类型获得这种魔力。甚至ContiguousArray都没有获得这种魔力。如果你将ContiguousArray传递给需要指针的东西,你就会将指针传递给容器(如果它是可变的,那么容器就会破坏;真实的故事,讨厌那个...)< / p>

答案 1 :(得分:1)

部分感谢@RobNapier我的问题的答案是:ArraySlice的指针应该指向切片的第一个元素。

我验证这一点的方式很简单:

var array = [5,4,3,325,67,7,3]
array.withUnsafeBufferPointer{ $0 } != array[3...6].withUnsafeBufferPointer{ $0 } # true
  ^--- points to 5's address             ^--- points to 325's address