Swift的第一步,在BST中分配小对象时的性能问题
在学习Swift 2.2的过程中,当我尝试分配许多小对象(基本上是262144个元素的BST)时,我面临着严重的性能下降。我目前的基准测试是一个Java 1.8.0_74编译版,它是我几年前编写的一个旧剪报,在我的2012款Retina Macbook Pro上,执行时间为59秒(59036178微秒)。我可以通过仪器观察到的问题是,我每次迭代都会得到几十个swift_retain_和swift_release。不知道如何避免它们:Swift的第一步,在BST中分配小对象时的性能问题,swift,memory-management,Swift,Memory Management,在学习Swift 2.2的过程中,当我尝试分配许多小对象(基本上是262144个元素的BST)时,我面临着严重的性能下降。我目前的基准测试是一个Java 1.8.0_74编译版,它是我几年前编写的一个旧剪报,在我的2012款Retina Macbook Pro上,执行时间为59秒(59036178微秒)。我可以通过仪器观察到的问题是,我每次迭代都会得到几十个swift_retain_和swift_release。不知道如何避免它们: import Foundation import Darwin
import Foundation
import Darwin;
import Foundation
public class BinarySearchTree<T : Comparable> {
private var _value : T?;
private var _leftTree : BinarySearchTree<T>?;
private var _rightTree : BinarySearchTree<T>?;
public init(value : T) {
_value = value;
}
var value : T? {
get {
return self._value;
}
set {
self._value = newValue;
}
}
var leftTree : BinarySearchTree<T>? {
get {
return self._leftTree;
}
set {
self._leftTree = newValue;
}
}
var rightTree : BinarySearchTree<T>? {
get {
return self._rightTree;
}
set {
self._rightTree = newValue;
}
}
public func add(newValue : T) -> BinarySearchTree<T> {
var navigator : BinarySearchTree<T>?;
var subtree : BinarySearchTree<T>?;
var done : Bool?;
done = false;
navigator = self;
while (!done!) {
if (newValue < navigator?.value) {
subtree = navigator?.leftTree;
if (subtree != nil) {
navigator = subtree;
} else {
let newNode = BinarySearchTree<T>(value: newValue);
navigator!.leftTree = newNode;
done = true;
}
} else if (newValue > navigator?.value) {
subtree = navigator?.rightTree;
if (subtree != nil) {
navigator = subtree;
} else {
let newNode = BinarySearchTree<T>(value: newValue);
navigator?.rightTree = newNode;
done = true;
}
} else {
done = true;
}
}
return self;
}
} /* cut remove/search methods */
<代码>导入基础
输入达尔文;
进口基金会
公共类二进制搜索树{
私有var_值:T?;
私有var_leftTree:BinarySearchTree?;
私有var_rightree:BinarySearchTree?;
公共初始化(值:T){
_价值=价值;
}
var值:T{
得到{
返回自我价值;
}
设置{
自我价值=新价值;
}
}
var leftTree:BinarySearchTree{
得到{
返回自我。_leftTree;
}
设置{
self.\u leftTree=newValue;
}
}
var rightTree:BinarySearchTree{
得到{
回归自我;
}
设置{
self._rightree=newValue;
}
}
public func add(newValue:T)->二进制搜索树{
变量导航器:BinarySearchTree?;
变量子树:二进制搜索树?;
var-done:Bool?;
完成=错误;
导航器=自我;
而(!完成!){
if(newValuelet count : Int32 = 262144;
let base : Int32 = 65536;
let target : Int32 = count + 1;
var info = mach_timebase_info(numer:0, denom:0);
var timebase = mach_timebase_info(&info);
let numer = UInt64(info.numer);
let denom = UInt64(info.denom);
let norm = UInt64(numer/denom);
let check1 = (mach_absolute_time() * norm);
var root = BinarySearchTree<Int32>(value:base);
for var loop in 0 ... count-1 {
if (loop % 1000 == 0) {
print(loop);
}
root = root.add(loop);
}
let check2 = (mach_absolute_time() * norm);
print("Creation phase microseconds: [" + String((check2 - check1) / 1000) + "]");
let count:Int32=262144;
let base:Int32=65536;
让目标:Int32=count+1;
var info=马赫时基信息(数字:0,数字:0);
var时基=马赫时基信息(&info);
设numer=UInt64(info.numer);
设denom=UInt64(info.denom);
设norm=UInt64(数值/名称);
让检查1=(马赫绝对时间()*标准);
var root=BinarySearchTree(值:base);
对于0中的var循环。。。计数-1{
if(循环%1000==0){
打印(循环);
}
root=root.add(循环);
}
让检查2=(马赫绝对时间()*标准);
打印(“创建阶段微秒:[“+字符串((检查2-检查1)/1000)+”];
我尝试搜索特定的swift释放/保留问题,但运气不佳,我不确定如何继续。谢谢大家我简化了您的代码,删除了一些
选项
和getter/setter,因为它们是不必要的,可能会导致代码变慢
我分析了您的代码和我的代码,并在相同的随机元素数据集上得到了这个结果:
1000个要素:
您的:创建阶段微秒:[28680771]
矿山:创建阶段微秒:[8564279]
10000个要素:
您的:创建阶段微秒:[426233689]
矿山:创建阶段微秒:[126725800]
这是我的密码:
public class BinarySearchTree2<T : Comparable> {
public init(value : T) {
self.value = value
}
var value : T
var leftTree : BinarySearchTree2<T>?
var rightTree : BinarySearchTree2<T>?
public func add(newValue : T) -> BinarySearchTree2<T> {
var navigator = self
while (true) {
if (newValue < navigator.value) {
guard let subtree = navigator.leftTree else {
navigator.leftTree = BinarySearchTree2<T>(value: newValue)
break
}
navigator = subtree
continue
}
if (newValue > navigator.value) {
guard let subtree = navigator.rightTree else {
navigator.rightTree = BinarySearchTree2<T>(value: newValue)
break
}
navigator = subtree
continue
}
break
}
return self
}
} /* cut remove/search methods */
正如您所注意到的,问题是保留/释放(虽然它不是真的,但是保留/释放与Umm的力量相比是微不足道的……我们将在最后到达那里)。这实际上与分配无关。您没有分配额外的对象,您只是简单地保留它们,然后释放它们。我将从Kenneth的代码开始,它优化了原始版本中的许多性能问题,但仍然存在这个问题。(我不考虑递归代码,因为它在您当前的用例中崩溃。不过,它确实避免了一些冗余保留。) 值得一提的是,Kenneth的代码是好的,通常是您应该做事情的方式(随着我们的讨论,您将看到更多) 第一个注意事项:当您提到AST时,这是针对ObjC的,而不是Swift。Swift的标志只是
-O
。您还需要-整个模块优化
,但这并没有真正的帮助
还有一件小事,我们就要开始了。随时标记课程final
。这样可以确保没有动态调度。与保留/发布相比,这没什么大不了的,但是,嘿,简单一点
30%的折扣听起来好吗?
好的,现在是一个大的,这是一个骗局。我发现,通过重写以下内容,我可以减少约30%的时间(完全导入从~6分钟到~4分钟):
guard let subtree = navigator.leftTree else {
navigator.leftTree = BinarySearchTree<T>(value: newValue)
break
}
navigator = subtree
continue
这是一件需要非常小心的事情。事实证明,在这种情况下速度更快,但在其他输入中可能没有那么快。对于优化器的更改,这可能没有那么快(SIL生成有点奇怪,我怀疑这实际上可能是一个错误,因为在第二种情况下,它似乎双重保留了navigator
,但只有在if
成功之后)。但目前看来确实要快一些。(编辑:Swift团队对这一发现感到惊讶,现在有人反对这一发现。不要指望这在将来奏效。)
85%怎么样?听起来怎么样?
但是就像你说的,我们不能用结构避免所有这些吗?但如果我们每次触摸整棵树都要复制它,那就太贵了。当然,通过使用类似于写时拷贝的数组,我们可以极大地改进这一点。但是牛很漂亮
guard let subtree = navigator.leftTree else {
navigator.leftTree = BinarySearchTree<T>(value: newValue)
break
}
navigator = subtree
continue
let subtree = navigator.leftTree
if subtree == nil {
navigator.leftTree = BinarySearchTree(value: newValue)
break
}
navigator = subtree!
continue
private struct Node<Element: Comparable> {
let value: Element
var leftIndex = -1 // Ugly, but ~25% faster than using Int? in my tests
var rightIndex = -1
init(_ value: Element) { self.value = value }
}
// This works exactly the same if you make it a `final class`. Your choice.
public struct BinarySearchTree<Element: Comparable> {
private var storage: [Node<Element>] = []
init(value: Element) { storage.append(Node(value)) }
public mutating func add(newValue: Element) {
if storage.isEmpty {
storage.append(Node(newValue))
}
var index = 0
while (true) {
let node = storage[index]
if (newValue < node.value) {
if node.leftIndex < 0 {
storage.append(Node(newValue))
storage[index].leftIndex = storage.count - 1 // Don't use node here; remember value types!
break
}
index = node.leftIndex
continue
} else if (newValue > node.value) {
if node.rightIndex < 0 {
storage.append(Node(newValue))
storage[index].rightIndex = storage.count - 1
break
}
index = node.rightIndex
continue
} else {
break
}
}
}
}
var values = Array(0 ... count - 1)
values.shuffleInPlace()
for (loop, value) in values.enumerate() {
if (loop % 1000 == 0) {
print(loop)
}
root.add(value)
}