加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

在Swift中访问多维数组的最有效方法?

发布时间:2020-12-14 05:34:04 所属栏目:百科 来源:网络整理
导读:我有一个固定大小的多维数字数组(浮点数,通常,但在我的示例代码中使用int来避免被转换开销分散注意力),我想有效地操作它. Swift不提供多维数组,但您可以通过一维数组阵列获得效果.然而,这些似乎非常非常缓慢.有没有更好的办法? 我有一个测试问题(我用它来测
我有一个固定大小的多维数字数组(浮点数,通常,但在我的示例代码中使用int来避免被转换开销分散注意力),我想有效地操作它. Swift不提供多维数组,但您可以通过一维数组阵列获得效果.然而,这些似乎非常非常缓慢.有没有更好的办法?

我有一个测试问题(我用它来测试其他语言),我将两个2D数组传递给一个子程序,该子程序将一个元素的每个元素设置为另一个元素的相应元素加上两个索引值的总和. (这意味着每个元素发生的事情取决于它的坐标,这是大多数现实世界中发生的情况.)

我使用-Ounchecked标志编译.

选项1:使用一维数组阵列,性能非常慢.
10次??传球需要1.5秒.

选项2:使用http://blog.trolieb.com/trouble-multidimensional-arrays-swift中相当简洁的想法,其中Array2D类使用底层1D数组并实现下标()使其看起来像2D数组,事情加速了很多(2个数量级):
1000次传球需要1.0秒

选项3:回到曾经在C中使用的非常笨拙的代码类型,你使用1D数组并明确地执行index =(row * columns)列计算,事情再次加速(不是2阶的大小)
100000次传球需要3.6秒.

选项3在我用clang中用-O3编译的等效C代码得到的因子2之内,因此对于早期编译器来说是好的.问题是它真的很难看,很尴尬,容易出错.可以在C中使用一些技巧,比如将指针数组分配到每一行的开头(C中的Numerical Recipes这样做)允许你对数组使用2D语法,并且使用面向对象的C你可以做到这一点相当优雅和高效.我的问题是在Swift中是否有一种方法可以获得像数组[Iy] [Ix](或数组[Iy,Ix]或其他任何代码,而不是数组[Iy * Ny Ix])来快速运行的代码?

我应该说我对Swift很新,而且我喜欢到目前为止看到的内容,并且我很欣赏编译器只会变得更快.我使用固定大小的多维数组对科学应用程序进行了大量编码,我对将来某些时候使用Swift的可能性感兴趣.或者我应该要求Apple为Swift添加真正的多维数组支持?

这是我一直在使用的测试代码:

//
//  main.swift
//
//  Tests 3 ways of handling 2D arrays in Swift. Test takes a 2D array and calls a routine
//  that takes each element of an input array and adds the X and Y index values to it and
//  returns an array with the result.
//
//  Command line arguments: Option Nrpt Nx Ny
//
//  Option is type of array used (1: Swift array of arrays,//                                2: Array2D 1D array looking like a 2D array
//                                3: 1D array used like a 2D array with explicit index calculation)
//  Nrpt is number of repeats of subroutine call
//  Nx,Ny are array dimensions.
//

import Darwin

//  Array2D comes from http://blog.trolieb.com/trouble-multidimensional-arrays-swift/

class Array2D {
    var cols:Int,rows:Int
    var matrix: [Int]

    init(cols:Int,rows:Int) {
        self.cols = cols
        self.rows = rows
        matrix = Array(count:cols*rows,repeatedValue:0)
    }
    subscript(col:Int,row:Int) -> Int {
        get { return matrix[cols * row + col] }
        set { matrix[cols*row+col] = newValue }
    }
    func colCount() -> Int { return self.cols }
    func rowCount() -> Int { return self.rows }
}

//  Using a 'proper' Swift '2D' array - ie an array of 1D arrays
func Subr (Input: Array<Array<Int>>,Nx: Int,Ny : Int,inout Output: Array<Array<Int>>) {
    for Iy in 0...Ny-1 {
        for Ix in 0...Nx-1 {
            Output[Iy][Ix] = Input[Iy][Ix] + (Ix + Iy)
        }
    }
}

//  Using an Array2D array - wrapping up a 1D array to act as a 2D one.
func Subr2d (Input: Array2D,inout Output: Array2D) {
    for Iy in 0...Ny-1 {
        for Ix in 0...Nx-1 {
            Output[Ix,Iy] = Input[Ix,Iy] + (Ix + Iy)
        }
    }
}

//  Using a 1D Swift array and doing the indexing explicitly
func Subr1d (Input: [Int],Ny: Int,inout Output: [Int]) {
    for Iy in 0...Ny-1 {
        for Ix in 0...Nx-1 {
            Output[Iy * Nx + Ix] = Input[Iy * Nx + Ix] + (Ix + Iy)
        }
    }
}

var Option:Int = 1
if let argStr = String.fromCString(C_ARGV[1]) {
    if let argInt = argStr.toInt() { Option = argInt }
}

var Nrpt:Int = 100
if let argStr = String.fromCString(C_ARGV[2]) {
    if let argInt = argStr.toInt() { Nrpt = argInt }
}

var Nx:Int = 2000;
if let argStr = String.fromCString(C_ARGV[3]) {
    if let argInt = argStr.toInt() { Nx = argInt }
}

var Ny:Int = 10;
if let argStr = String.fromCString(C_ARGV[4]) {
    if let argInt = argStr.toInt() { Ny = argInt }
}


println("Repeats: (Nrpt),Array (Nx) by (Ny)")

switch Option {
case 1:

    println ("Using an ordinary Swift '2D' array of arrays")

    var array = Array(count:Ny,repeatedValue:Array(count:Nx,repeatedValue:Int()))

    for Iy in 0...Ny-1 {
        for Ix in 0...Nx-1 {
            array[Iy][Ix] = (Ix + Iy)
        }
    }

    var output = Array(count:Ny,repeatedValue:Int()))

    let start : UInt64 = mach_absolute_time()

    for Irpt in 0...Nrpt-1 {
       Subr(array,Nx,Ny,&output)
    }

    let duration : UInt64 = mach_absolute_time() - start

    check:
    for Iy in 0...Ny-1 {
        for Ix in 0...Nx-1 {
            let Expected = array[Iy][Ix] + (Ix + Iy)
            if (output[Iy][Ix] != Expected) {
                println("Error at (Ix),(Iy) Got (output[Iy][Ix]) expected (Expected)")
                break check
            }
        }
    }

    var info : mach_timebase_info = mach_timebase_info(numer: 0,denom: 0)
    mach_timebase_info(&info)

    let total = (duration * UInt64(info.numer) / UInt64(info.denom)) / 1_000_000
    println("2D array took:(total) ms.")

case 2:

    println ("Using the Array2D class")

    var array2 = Array2D(cols: Nx,rows: Ny)
    var output2 = Array2D(cols: Nx,rows: Ny)

    for Iy in 0...Ny-1 {
        for Ix in 0...Nx-1 {
            array2[Ix,Iy] = (Ix + Iy)
        }
    }

    println("Timing array2D version")

    let start2 : UInt64 = mach_absolute_time()

    for Irpt in 0...Nrpt-1 {
        Subr2d(array2,&output2)
    }

    let duration2 : UInt64 = mach_absolute_time() - start2

    check:
    for Iy in 0...Ny-1 {
        for Ix in 0...Nx-1 {
            let Expected = array2[Ix,Iy] + (Ix + Iy)
            if (output2[Ix,Iy] != Expected) {
                println("Error at (Ix),(Iy) Got (output2[Ix,Iy]) expected (Expected)")
                break check
            }
        }
    }


    var info2 : mach_timebase_info = mach_timebase_info(numer: 0,denom: 0)
    mach_timebase_info(&info2)

    let total2 = (duration2 * UInt64(info2.numer) / UInt64(info2.denom)) / 1_000_000
    println("Array2D version took:(total2) ms.")

case 3:

    println ("Using an a 1D array and handling the indexing explicitly")

    var array3 = Array(count:Ny * Nx,repeatedValue:Int())

    for Iy in 0...Ny-1 {
        for Ix in 0...Nx-1 {
            array3[Iy * Nx + Ix] = (Ix + Iy)
        }
    }

    var output3 = Array(count:Ny * Nx,repeatedValue:Int())

    let start3 : UInt64 = mach_absolute_time()

    for Irpt in 0...Nrpt-1 {
        Subr1d(array3,&output3)
    }

    let duration3 : UInt64 = mach_absolute_time() - start3

    check:
    for Iy in 0...Ny-1 {
        for Ix in 0...Nx-1 {
            let Expected = array3[Iy * Nx + Ix] + (Ix + Iy)
            if (output3[Iy * Nx + Ix] != Expected) {
                println("Error at (Ix),(Iy) Got (output3[Iy * Nx + Ix]) expected (Expected)")
                break check
            }
        }
    }

    var info3 : mach_timebase_info = mach_timebase_info(numer: 0,denom: 0)
    mach_timebase_info(&info3)

    let total3 = (duration3 * UInt64(info3.numer) / UInt64(info3.denom)) / 1_000_000
    println("1D array took:(total3) ms.")

default:
    println ("Invalid option code. Must be 1,2,or 3")
}
Chris Lattner自己在Apple开发论坛上对此做出了回应,听起来像#2 /#3是我们最好的解决方案,直到即将进行编译器修复.

“这是一个众所周知的问题:2D阵列……可能会导致极差的性能,因为它们所基于的写时复制(COW)优化在某些情况下会失败……

对它的修复只是差点错过了6.1版本,因为它需要一些内部基础设施工作.也就是说,它将在swift编译器的下一个重要更新中出现.

与此同时,您可以使用(丑陋但有效)的解决方法.例如,如果数组是矩形,则可以使用大小为m * n个元素的单个数组,并手动索引.

-克里斯”

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读