我有一个测试问题(我用它来测试其他语言),我将两个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")
}
“这是一个众所周知的问题:2D阵列……可能会导致极差的性能,因为它们所基于的写时复制(COW)优化在某些情况下会失败……
对它的修复只是差点错过了6.1版本,因为它需要一些内部基础设施工作.也就是说,它将在swift编译器的下一个重要更新中出现.
与此同时,您可以使用(丑陋但有效)的解决方法.例如,如果数组是矩形,则可以使用大小为m * n个元素的单个数组,并手动索引.
-克里斯”