我正在Swift实现一个算法,注意到性能非常差。在深入挖掘之后,我意识到瓶颈之一就像排序数组一样简单。相关部分在这里:
let n = 1000000
let x = Int[](count: n,repeatedValue: 0)
for i in 0..n {
    x[i] = random()
}
// start clock here
let y = sort(x)
// stop clock here

在C中,类似的操作在我的计算机上需要0.06秒。

在Python中,它需要0.6秒(没有技巧,只有y = sorted(x)为整数列表)。

在Swift中,如果我使用以下命令编译它需要6秒:

xcrun swift -O3 -sdk `xcrun --show-sdk-path --sdk macosx`

如果我用下面的命令编译它需要88秒的时间:

xcrun swift -O0 -sdk `xcrun --show-sdk-path --sdk macosx`

“Xcode”中的“发布”与“调试”构建的时间是类似的。

这里有什么问题?我可以理解一些性能损失与C相比,但不是一个10倍的放缓与纯Python相比。

编辑:mweathers注意到,将-O3更改为-Ofast使此代码运行几乎与C版本一样快!但是,-Ofast很多地改变了语言的语义 – 在我的测试中,它禁用了对整数溢出和数组索引溢出的检查。例如,使用-Ofast,以下Swift代码静默运行而不会崩溃(并打印出一些垃圾):

let n = 10000000
println(n*n*n*n*n)
let x = Int[](count: n,repeatedValue: 10)
println(x[n])

所以 – 快速不是我们想要的; Swift的整个观点是我们有安全网。当然,安全网对性能有一些影响,但它们不应该使程序慢100倍。记住,Java已经检查了数组边界,在典型的情况下,减速的大小要小于2.在Clang和GCC中,我们有-ftrapv用于检查(有符号)整数溢出,而不是那么慢。

因此,问题:我们如何在不失去安全网的情况下在Swift中获得合理的性能?

编辑2:我做了更多的基准,有非常简单的循环

for i in 0..n {
    x[i] = x[i] ^ 12345678
}

(这里的xor操作只是为了我可以更容易找到相关的循环在汇编代码。我试图选择一个容易发现的操作,但也“无害”的意义上,它不应该要求任何检查相关到整数溢出。)

同样,-O3和-Ofast之间的性能有很大的不同。所以我看了汇编代码:

>用-Ofast我几乎得到了我的期望。相关部分是一个带有5个机器语言指令的循环。
>用-O3我得到的东西,超出了我最疯狂的想象力。内循环跨越88行汇编代码。我没有试图理解所有的,但最可疑的部分是13调用“callq _swift_retain”和另外13个调用“callq _swift_release”。也就是说,在内循环中有26个子程序调用!

编辑3:在评论中,Ferruccio要求公平的基准,他们不依赖于内置函数(例如排序)。我认为以下程序是一个相当好的例子:

let n = 10000
let x = Int[](count: n,repeatedValue: 1)
for i in 0..n {
    for j in 0..n {
        x[i] = x[j]
    }
}

没有算术,所以我们不需要担心整数溢出。我们做的只是很多数组引用。结果是这里 – Swift -O3损失的因子差不多500与相比-Ofast:

> C-O3:0.05s
> C-O0:0.4s
> Java:0.2 s
> Python with PyPy:0.5 s
> Python:12秒
>快速:快速:0.05秒
> Swift -O3:23秒
> Swift -O0:443 s

(如果你担心编译器可能会完全优化无意义的循环,你可以把它改成例如x [i] ^ = x [j],并添加一个输出x [0]的print语句。 ;时间将非常类似。)

是的,这里的Python实现是一个愚蠢的纯Python实现与一列int和嵌套for循环。它应该比未优化的Swift慢得多。一些似乎严重破坏与Swift和数组索引。

编辑4:这些问题(以及一些其他性能问题)似乎已经修复在Xcode 6 beta 5。

对于排序,我现在有以下定时:

> ang -O3:0.06s
> swiftc -Ofast:0.1 s
> swiftc -O:0.1 s
> swiftc:4秒

对于嵌套循环:

> ang -O3:0.06s
> swiftc -Ofast:0.3 s
> swiftc -O:0.4s
> swiftc:540 s

似乎没有理由再使用不安全的-Ofast(a.k.a. -Ounchecked); plain -O产生同样好的代码。

tl; dr Swift现在使用默认发布优化级别[-O],这个基准使用的速度与C一样快。

这里是一个在Swift的就地快速:

func quicksort_swift(inout a:CInt[],start:Int,end:Int) {
    if (end - start < 2){
        return
    }
    var p = a[start + (end - start)/2]
    var l = start
    var r = end - 1
    while (l <= r){
        if (a[l] < p){
            l += 1
            continue
        }
        if (a[r] > p){
            r -= 1
            continue
        }
        var t = a[l]
        a[l] = a[r]
        a[r] = t
        l += 1
        r -= 1
    }
    quicksort_swift(&a,start,r + 1)
    quicksort_swift(&a,r + 1,end)
}

和C一样:

void quicksort_c(int *a,int n) {
    if (n < 2)
        return;
    int p = a[n / 2];
    int *l = a;
    int *r = a + n - 1;
    while (l <= r) {
        if (*l < p) {
            L++;
            continue;
        }
        if (*r > p) {
            r--;
            continue;
        }
        int t = *l;
        *L++ = *r;
        *r-- = t;
    }
    quicksort_c(a,r - a + 1);
    quicksort_c(l,a + n - l);
}

两者工作:

var a_swift:CInt[] = [0,5,2,8,1234,-1,2]
var a_c:CInt[] = [0,2]

quicksort_swift(&a_swift,a_swift.count)
quicksort_c(&a_c,CInt(a_c.count))

// [-1,1234]
// [-1,1234]

两者都在同一个程序中调用。

var x_swift = CInt[](count: n,repeatedValue: 0)
var x_c = CInt[](count: n,repeatedValue: 0)
for var i = 0; i < n; ++i {
    x_swift[i] = CInt(random())
    x_c[i] = CInt(random())
}

let swift_start:UInt64 = mach_absolute_time();
quicksort_swift(&x_swift,x_swift.count)
let swift_stop:UInt64 = mach_absolute_time();

let c_start:UInt64 = mach_absolute_time();
quicksort_c(&x_c,CInt(x_c.count))
let c_stop:UInt64 = mach_absolute_time();

这将绝对时间转换为秒:

static const uint64_t NANOS_PER_USEC = 1000ULL;
static const uint64_t NANOS_PER_MSEC = 1000ULL * NANOS_PER_USEC;
static const uint64_t NANOS_PER_SEC = 1000ULL * NANOS_PER_MSEC;

mach_timebase_info_data_t timebase_info;

uint64_t abs_to_nanos(uint64_t abs) {
    if ( timebase_info.denom == 0 ) {
        (void)mach_timebase_info(&timebase_info);
    }
    return abs * timebase_info.numer  / timebase_info.denom;
}

double abs_to_seconds(uint64_t abs) {
    return abs_to_nanos(abs) / (double)NANOS_PER_SEC;
}

下面是编译器优化级别的摘要:

[-Onone] no optimizations,the default for debug.
[-O]     perform optimizations,the default for release.
[-Ofast] perform optimizations and disable runtime overflow checks and runtime type checks.

对于n = 10_000,使用[-Onone]的时间(以秒为单位):

Swift:            0.895296452
C:                0.001223848

这里是Swift的内置sort()对于n = 10_000:

Swift_builtin:    0.77865783

这里是[-O]为n = 10_000:

Swift:            0.045478346
C:                0.000784666
Swift_builtin:    0.032513488

正如你所看到的,Swift的性能提高了20倍。

根据mweathers’ answer,设置[-Ofast]产生真正的区别,导致n = 10_000的这些时间:

Swift:            0.000706745
C:                0.000742374
Swift_builtin:    0.000603576

对于n = 1_000_000:

Swift:            0.107111846
C:                0.114957179
Swift_sort:       0.092688548

为了比较,对于n = 1_000_000使用[-Onone]

Swift:            142.659763258
C:                0.162065333
Swift_sort:       114.095478272

因此,在这个基准测试中,Swift在没有优化的情况下比C的性能差了近1000倍。另一方面,两个编译器都设置为[-Ofast] Swift实际上至少执行,如果不是稍好于C.

已经指出,[-Ofast]改变语言的语义,使其可能不安全。这是苹果在Xcode 5.0发行说明中说的:

A new optimization level -Ofast,available in LLVM,enables aggressive optimizations. -Ofast relaxes some conservative restrictions,mostly for floating-point operations,that are safe for most code. It can yield significant high-performance wins from the compiler.

他们都提倡。无论是聪明还是不,我不能说,但从我可以告诉它似乎足够合理的使用[-Ofast]在一个版本,如果你不做高精度浮点运算,你没有信心没有整数或阵列溢出可能在您的程序。如果你需要高性能和溢出检查/精确算术,现在选择另一种语言。

BETA 3更新:

n = 10_000,其中[-O]:

Swift:            0.019697268
C:                0.000718064
Swift_sort:       0.002094721

Swift通常有点快,看起来Swift的内置排序已经发生了很大的变化。

最终更新:

[-在一个]:

Swift:   0.678056695
C:       0.000973914

[-O]:

Swift:   0.001158492
C:       0.001192406

[-Ounchecked]:

Swift:   0.000827764
C:       0.001078914

Swift性能:排序数组的更多相关文章

  1. html5使用canvas实现弹幕功能示例

    这篇文章主要介绍了html5使用canvas实现弹幕功能示例的相关资料,需要的朋友可以参考下

  2. 前端实现弹幕效果的方法总结(包含css3和canvas的实现方式)

    这篇文章主要介绍了前端实现弹幕效果的方法总结(包含css3和canvas的实现方式)的相关资料,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  3. H5 canvas实现贪吃蛇小游戏

    本篇文章主要介绍了H5 canvas实现贪吃蛇小游戏,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  4. ios – parse.com用于键,预期字符串的无效类型,但是得到了数组

    我尝试将我的数据保存到parse.com.我已经预先在parse.com上创建了一个名为’SomeClass’的类.它有一个名为’mySpecialColumn’的列,其数据类型为String.这是我尝试使用以下代码保存数据的代码:如果我运行这个我得到:错误:密钥mySpecialColumn的无效类型,预期字符串,但得到数组这就是我在parse.com上的核心外观:有谁知道我为什么会收到这个错误?

  5. ios – Swift相当于`[NSDictionary initWithObjects:forKeys:]`

    Swift的原生字典是否与[NSDictionaryinitWithObjects:forKeys:]相当?假设我有两个带键和值的数组,并希望将它们放在字典中.在Objective-C中,我这样做:当然我可以通过两个数组迭代一个计数器,使用vardict:[String:Int]并逐步添加东西.但这似乎不是一个好的解决方案.使用zip和enumerate可能是同时迭代两者的更好方法.然而,这种方法

  6. ios – 上下文类型’NSFastEnumeration’不能与数组文字一起使用

    斯威夫特3,你会这样做吗?解决方法正如您所发现的,您不能使用as-casting将数组文字的类型指定为NSFastEnumeration.您需要找到一个符合NSFastEnumeration的正确类,在您的情况下它是NSArray.通常写这样的东西:

  7. ios – 无法识别的选择器发送到实例NSTimer Swift

    解决方法让updateTime成为一个类方法.如果它是在一个纯粹的Swift类中,你需要在@objc前面说明该方法的声明,如:

  8. ios – 在Swift中获取Cocoa Touch Framework项目版本字符串

    有谁知道这是否是我的项目设置中的缺陷,Xcode中的一个错误,或者是否有一种方法可以将Swift中的框架版本作为String或数组获取,这样我可以提供比major.minor更精细的版本控制?

  9. ios – 搜索数组swift中的对象

    我正在尝试使用UISearchController创建搜索功能.但是,我似乎无法使其与我的团队对象一起工作.我首先创建了一个包含id,name和shortname的TeamObject.然后我从一个url中检索teamData,并将TeamObjects添加到一个填充到tableView中的数组中.这个tableView包含一个searchController,它假设过滤数据,但没有任何反应.阵列

  10. ios – 获取资产目录文件夹中所有图像的数组

    在iOS中,是否可以获取资产目录文件夹中的图像数组?我不确定为什么会对此进行投票.我真的不知道从哪里开始.我的另一种方法是创建文件夹中所有文件的plist,但它似乎是多余的.我无法添加任何代码,因为我会添加什么?

随机推荐

  1. Swift UITextField,UITextView,UISegmentedControl,UISwitch

    下面我们通过一个demo来简单的实现下这些控件的功能.首先,我们拖将这几个控件拖到storyboard,并关联上相应的属性和动作.如图:关联上属性和动作后,看看实现的代码:

  2. swift UISlider,UIStepper

    我们用两个label来显示slider和stepper的值.再用张图片来显示改变stepper值的效果.首先,这三个控件需要全局变量声明如下然后,我们对所有的控件做个简单的布局:最后,当slider的值改变时,我们用一个label来显示值的变化,同样,用另一个label来显示stepper值的变化,并改变图片的大小:实现效果如下:

  3. preferredFontForTextStyle字体设置之更改

    即:

  4. Swift没有异常处理,遇到功能性错误怎么办?

    本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  5. 字典实战和UIKit初探

    ios中数组和字典的应用Applicationschedule类别子项类别名称优先级数据包contactsentertainment接触UIKit学习用Swift调用CocoaTouchimportUIKitletcolors=[]varbackView=UIView(frame:CGRectMake(0.0,0.0,320.0,CGFloat(colors.count*50)))backView

  6. swift语言IOS8开发战记21 Core Data2

    上一话中我们简单地介绍了一些coredata的基本知识,这一话我们通过编程来实现coredata的使用。还记得我们在coredata中定义的那个Model么,上面这段代码会加载这个Model。定义完方法之后,我们对coredata的准备都已经完成了。最后强调一点,coredata并不是数据库,它只是一个框架,协助我们进行数据库操作,它并不关心我们把数据存到哪里。

  7. swift语言IOS8开发战记22 Core Data3

    上一话我们定义了与coredata有关的变量和方法,做足了准备工作,这一话我们来试试能不能成功。首先打开上一话中生成的Info类,在其中引用头文件的地方添加一个@objc,不然后面会报错,我也不知道为什么。

  8. swift实战小程序1天气预报

    在有一定swift基础的情况下,让我们来做一些小程序练练手,今天来试试做一个简单地天气预报。然后在btnpressed方法中依旧增加loadWeather方法.在loadWeather方法中加上信息的显示语句:运行一下看看效果,如图:虽然显示出来了,但是我们的text是可编辑状态的,在storyboard中勾选Editable,再次运行:大功告成,而且现在每次单击按钮,就会重新请求天气情况,大家也来试试吧。

  9. 【iOS学习01】swift ? and !  的学习

    如果不初始化就会报错。

  10. swift语言IOS8开发战记23 Core Data4

    接着我们需要把我们的Rest类变成一个被coredata管理的类,点开Rest类,作如下修改:关键字@NSManaged的作用是与实体中对应的属性通信,BinaryData对应的类型是NSData,CoreData没有布尔属性,只能用0和1来区分。进行如下操作,输入类名:建立好之后因为我们之前写的代码有些地方并不适用于coredata,所以编译器会报错,现在来一一解决。

返回
顶部