- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
给定以下三个简单函数:
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/
以下片段的效果之间有什么区别(如果有的话): cout << "Some text" << s1 << "some more text\n"; cout << "Some text" + s1 + "
在解释器模式下运行 python 时,什么是 more-ing 或 less-ing 多行输出的最佳替代方案? 假设,存在一个对象变量foo,它有很多属性。 dir(foo) 会转储到屏幕上。我们无法
在阅读“Lucene in Action 2nd edition”时,我看到了关于 Filter 的描述。可用于在 Lucene 中进行结果过滤的类。 Lucene 有很多过滤器重复 Query类。例
为了满足我对 C 知识的渴求,在连接到我的家庭网络的两个 linux 机器上,我正在编写一个基本的 telnet,它包含 send() 和 recv( ) 的字符串(只是为了获得套接字和线程的一些经验
给定以下三个简单函数: func twice_Array_of_Int(a: [Int]) -> [Int] { return a + a } func twice_Array_of_T(a:
如我的 previous question 中所述,我正在尝试在功能上制作一些有点像向导的东西。我已经确定了一个单独的框架,并添加了一个sizer。我为希望用户看到的每个屏幕构建面板,将它们添加到框架
据我了解,range-v3 库的 View 操作(目前需要 C++17,但要成为 C++20 中 STL 的正式部分)提供了可链接的类 STL 算法,这些算法是延迟计算的。作为实验,我创建了以下代码来
如何选择来自一位特定作者的所有项目?可能这样吗?或者,如果我也想要很多项目类型和项目包(项目有很多项目),我该如何编辑实体? 元素 /** * @ORM\Table() * @ORM\Entity
我想选择以正则表达式结尾的单词,但我想排除以 thing 结尾的单词。例如: everything running catching nothing 这几个词中,选running和catching,排
使用 Julia 1.5.3 和 Julia 1.6.0 两个版本似乎都不支持 & 用于 BitArrays。 我有两个 BitArray,例如 x = BitArray([1,0,1]) 和 y=B
我有一个读取二进制文件然后使用 struct.unpack() 解压文件内容的函数。我的功能工作得很好。如果/当我使用长的“格式”字符串解压缩整个文件时,它会更快。问题是有时字节对齐会发生变化,因此我
阅读维基百科上的“ARM 架构”,发现以下说法: Registers R0-R7 are the same across all CPU modes; they are never banked. R
您好,我需要 ssh 到一个 IP 地址并通过 shell 脚本运行我的 Java 代码我就是这样做的 ssh $LINE java -Djava.library.path=/N/u/sbpatil/
该程序应该读取字符串输入的值并返回结果。 但是,当我使用 System.out.println(Arrays.toString(stack.toArray())); 为了检查堆栈在最后甚至在程序期间的
在 Alpine 镜像中构建的 GO 可执行文件存在一个奇怪的行为,其中标准 LD_PRELOAD 功能无法正常工作。 看起来像 构造函数未被调用 由动态加载器! 我有一个示例 go 应用程序(get
已关闭。此问题不符合Stack Overflow guidelines 。目前不接受答案。 要求提供代码的问题必须表现出对所解决问题的最低限度的了解。包括尝试的解决方案、为什么它们不起作用以及预期结果
我在下面写这段代码,发现了这个奇怪的行为: #include #include #include using namespace std; int main() { map map1;
我对 JS 的 .sort() 函数理解得很好,对多维数组的理解也比较松散,但我有点卡住了。这是我得到的: var player1 = ["bob", 20]; var player2 = ["jon
在 python 中有更好的方法吗?: ((w.endswith('<') or w.endswith('')) 也许可以使用任何 最佳答案 字符串上的 endswith 方法可以将元组作为参数:
在 Javascript 中,有没有一种方法可以从数组中选择包含某些内容的单词。例如,["swimming", "basketball", "chess", "rowing"],我只想选择包含“-in
我是一名优秀的程序员,十分优秀!