给定以下三个简单函数:
func twice_Array_of_Int(a: [Int]) -> [Int] {
return a + a
}
func twice_Array_of_T<T>(a: [T]) -> [T] {
return a + a
}
func twice_Array_of_Any(a: [AnyObject]) -> [AnyObject] {
return a + a
}
假设发布版本 (-Os),您预计它们的性能如何比较?
我的期望是 [Int] -> [Int] 会比 [AnyObject] -> [AnyObject] 快得多...而且它是.. . 快几个数量级。
但是,我还期望 [T] -> [T] 的性能比 [AnyObject] -> [AnyObject] 好得多,而且几乎和 一样快>[Int] -> [Int]...对吗?
这里我被证明是错误的:甚至 [AnyObject] -> [AnyObject](甚至包括转换回 [Int])比 快 5 倍[T] -> [T]!这令人失望,尤其是因为泛型是 Swift 最有前途的特性之一。
在他们的一个 WWDC 视频中,Apple 工程师提到他们正在本地实现泛型,即使用它们不会导致代码膨胀。这是否解释了 [T] -> [T] 的糟糕表现?如果他们只是在编译时扩展泛型函数,[T] -> [T] 和 [Int] -> [Int] 的性能应该是一样的,对吧?
测试代码如下:
func testPerformance_twice_Array_of_Int() {
let a = Array(1...100_000)
self.measureBlock {
let twice_a = twice_Array_of_Int(a)
}
// average: 0.000, relative standard deviation: 76.227%
}
func testPerformance_twice_Array_of_T() {
let a = Array(1...100_000)
self.measureBlock {
let twice_a = twice_Array_of_T(a)
}
// measured [Time, seconds] average: 0.554, relative standard deviation: 7.846%
}
func testPerformance_twice_Array_of_Any() {
let a = Array(1...100_000)
self.measureBlock {
let twice_a = twice_Array_of_Any(a) as [Int]
}
// average: 0.115, relative standard deviation: 8.303%
// without the cast to [Int] = average: 0.039, relative standard deviation: 2.931%
}
我很想听听您的意见,以及您打算如何将其纳入您的代码设计。
编辑
我刚刚做了一个更简单的测量,结果更令人吃惊:
func ==(lhs: (Int, Int), rhs: (Int, Int)) -> Bool {
return lhs.0 == rhs.0 && lhs.1 == rhs.1
}
相比于:
func ==<T: Equatable>(lhs: (T, T), rhs: (T, T)) -> Bool {
return lhs.0 == rhs.0 && lhs.1 == rhs.1
}
结果:
func testPerformance_Equals_Tuple_Int() {
let a = (2, 3)
let b = (3, 2)
XCTAssertFalse(a == b)
let i = 1_000_000
self.measureBlock() {
for _ in 1...i {
let c = a == b
}
// average: 0.002, relative standard deviation: 9.781%
}
}
相比于:
func testPerformance_Equals_Tuple_T() {
let a = (2, 3)
let b = (3, 2)
XCTAssertFalse(a == b)
let i = 1_000_000
self.measureBlock() {
for _ in 1...i {
let c = a == b
}
// average: 2.080, relative standard deviation: 5.118%
}
}
中缀函数的通用版本慢了 1000 多倍!
编辑 2
8 月 21 日,Austin Zheng 在 Swift 语言用户组聚会上发表了关于“枚举、模式匹配和泛型”的演讲(Chris Lattner 是特邀嘉宾)。他说 Swift 会发出针对常见类型优化的代码,但会根据运行时的需要回退到针对其他类型的函数的原生通用版本。请参阅:http://realm.io/news/swift-enums-pattern-matching-generics/ (从 32:00 开始)。
编辑 3
随着 Swift 2 的发布,这更新早就该更新了(就在我喘口气的时候)......
最佳答案
I'd love to hear your opinion and how you plan to factor this into your code design.
您不应该将此考虑到您的代码设计中。 Swift 编译器正在快速发展,优化器也在不断地、激进地发展。根据优化器早期版本的微基准更改编码实践是“过早优化”的最糟糕形式。
为清晰起见的代码。正确性代码。当您看到性能问题时,请调查它们。没有比崩溃的程序慢的程序了。 [Int] 和 [T] 都比 [AnyObject](你必须经常投并验证)。选择应该不难。当您有一些实时代码表明 Instruments 中的 [T] 存在问题时,您应该研究其他选项(尽管我仍会将 [AnyObject] 放在底部;上面代码中的明显解决方案是编写一个特殊情况的重载来处理 [Int] 如果真的更快的话)。
由于您有一个有趣的测试用例,展示了通用和 native 之间的惊人差异,因此打开雷达 (bugreport.apple.com) 是合适的。这样,当问题得到解决时,您清晰、正确的代码将免费获得速度提升。
编辑:我还没有查看汇编器输出(你应该),但我确实有几个理论可以解释为什么这是真的(如果它确实是真的;我也没有复制它)。 [AnyObject] 可以在这里替换为 NSArray,它有 radically different performance characteristics来自 数组。这就是你永远不应该认为“[AnyObject] 更快”的关键原因是基于一些不是你的真实代码的微基准测试。 a+a 的性能可能与其他一些操作的性能完全无关。
关于 [Int] 与 [T],你可能会误解 Swift 是如何处理泛型的。 Swift 不会为每种类型的每个函数都创建一个全新的版本。它创建了一个通用版本。该版本可能不会像特定类型版本那样优化所有内容。例如,在这种情况下,[T] 版本可能会进行内存管理,而 [Int] 版本不会(我完全是在猜测)。优化器可以生成一个优化版本(这就是为什么你不应该尝试事后猜测它),但它可能不会(这就是为什么你有时可能不得不帮助它处理一个特殊的过载)。 Swift Yeti has a nice article explaining further .
同样,您永远不要假设您知道优化器将要做什么而不对至少与您关心的代码相似的实时代码进行测试(并且直到你有理由相信这是一个性能瓶颈)。很容易写出“疯狂的代码,因为它更快”,实际上速度要慢得多,但仍然很疯狂。
优化器知识就是力量,但如果您不知道何时使用它,它就是一种危险的力量。
关于performance - +ing Swift 的 AnyObject 数组比 +ing 的 T 数组快得多,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25959857/
我有多个ActiveRecord子类Item的实例数组,我需要根据最早的事件循环打印。在这种情况下,我需要打印付款和维护日期,如下所示:ItemAmaintenancerequiredin5daysItemBpaymentrequiredin6daysItemApaymentrequiredin7daysItemBmaintenancerequiredin8days我目前有两个查询,用于查找maintenance和payment项目(非排他性查询),并输出如下内容:paymentrequiredin...maintenancerequiredin...有什么方法可以改善上述(丑陋的)代
我的代码目前看起来像这样numbers=[1,2,3,4,5]defpop_threepop=[]3.times{pop有没有办法在一行中完成pop_three方法中的内容?我基本上想做类似numbers.slice(0,3)的事情,但要删除切片中的数组项。嗯...嗯,我想我刚刚意识到我可以试试slice! 最佳答案 是numbers.pop(3)或者numbers.shift(3)如果你想要另一边。 关于ruby-多次弹出/移动ruby数组,我们在StackOverflow上找到一
我需要读入一个包含数字列表的文件。此代码读取文件并将其放入二维数组中。现在我需要获取数组中所有数字的平均值,但我需要将数组的内容更改为int。有什么想法可以将to_i方法放在哪里吗?ClassTerraindefinitializefile_name@input=IO.readlines(file_name)#readinfile@size=@input[0].to_i@land=[@size]x=1whilex 最佳答案 只需将数组映射为整数:@land边注如果你想得到一条线的平均值,你可以这样做:values=@input[x]
我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server
我正在使用puppet为ruby程序提供一组常量。我需要提供一组主机名,我的程序将对其进行迭代。在我之前使用的bash脚本中,我只是将它作为一个puppet变量hosts=>"host1,host2"我将其提供给bash脚本作为HOSTS=显然这对ruby不太适用——我需要它的格式hosts=["host1","host2"]自从phosts和putsmy_array.inspect提供输出["host1","host2"]我希望使用其中之一。不幸的是,我终其一生都无法弄清楚如何让它发挥作用。我尝试了以下各项:我发现某处他们指出我需要在函数调用前放置“function_”……这
这个问题在这里已经有了答案:Checktoseeifanarrayisalreadysorted?(8个答案)关闭9年前。我只是想知道是否有办法检查数组是否在增加?这是我的解决方案,但我正在寻找更漂亮的方法:n=-1@arr.flatten.each{|e|returnfalseife
我有一个这样的哈希数组:[{:foo=>2,:date=>Sat,01Sep2014},{:foo2=>2,:date=>Sat,02Sep2014},{:foo3=>3,:date=>Sat,01Sep2014},{:foo4=>4,:date=>Sat,03Sep2014},{:foo5=>5,:date=>Sat,02Sep2014}]如果:date相同,我想合并哈希值。我对上面数组的期望是:[{:foo=>2,:foo3=>3,:date=>Sat,01Sep2014},{:foo2=>2,:foo5=>5:date=>Sat,02Sep2014},{:foo4=>4,:dat
我正在尝试在Ruby中制作一个cli应用程序,它接受一个给定的数组,然后将其显示为一个列表,我可以使用箭头键浏览它。我觉得我已经在Ruby中看到一个库已经这样做了,但我记不起它的名字了。我正在尝试对soundcloud2000中的代码进行逆向工程做类似的事情,但他的代码与SoundcloudAPI的使用紧密耦合。我知道cursesgem,我正在考虑更抽象的东西。广告有没有人见过可以做到这一点的库或一些概念证明的Ruby代码可以做到这一点? 最佳答案 我不知道这是否是您正在寻找的,但也许您可以使用我的想法。由于我没有关于您要完成的工作
我使用Ember作为我的前端和GrapeAPI来为我的API提供服务。前端发送类似:{"service"=>{"name"=>"Name","duration"=>"30","user"=>nil,"organization"=>"org","category"=>nil,"description"=>"description","disabled"=>true,"color"=>nil,"availabilities"=>[{"day"=>"Saturday","enabled"=>false,"timeSlots"=>[{"startAt"=>"09:00AM","endAt"=>
我正在尝试按0-9和a-z的顺序创建数字和字母列表。我有一组值value_array=['0','1','2','3','4','5','6','7','8','9','a','b','光盘','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','','u','v','w','x','y','z']和一个组合列表的数组,按顺序,这些数字可以产生x个字符,比方说三个list_array=[]和一个当前字母和数字组合的数组(在将它插入列表数组之前我会把它变成一个字符串,]current_combo['0','0','0']