Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Performance Swift Dictionary即使进行了优化也很慢:是否执行了不必要的保留/发布?_Performance_Swift_Swift2 - Fatal编程技术网

Performance Swift Dictionary即使进行了优化也很慢:是否执行了不必要的保留/发布?

Performance Swift Dictionary即使进行了优化也很慢:是否执行了不必要的保留/发布?,performance,swift,swift2,Performance,Swift,Swift2,以下代码将简单值持有者映射为布尔值,在Java中的运行速度比Swift 2-XCode 7 beta3、“最快、积极的优化[-Ofast]”和“快速、整个模块优化”快20倍。在Java中,我可以每秒查找2800多万次,但在Swift中只能查找1000万次 当我在Instruments中查看它时,我发现大部分时间都在进行一对与映射查找相关联的retain/release调用。如果您对发生这种情况的原因或解决方法有任何建议,我们将不胜感激 代码的结构是我的真实代码的简化版本,它有一个更复杂的键类,并

以下代码将简单值持有者映射为布尔值,在Java中的运行速度比Swift 2-XCode 7 beta3、“最快、积极的优化[-Ofast]”和“快速、整个模块优化”快20倍。在Java中,我可以每秒查找2800多万次,但在Swift中只能查找1000万次

当我在Instruments中查看它时,我发现大部分时间都在进行一对与映射查找相关联的retain/release调用。如果您对发生这种情况的原因或解决方法有任何建议,我们将不胜感激

代码的结构是我的真实代码的简化版本,它有一个更复杂的键类,并且还存储其他类型(尽管布尔是我的实际情况)。另外,请注意,我使用单个可变键实例进行检索,以避免在循环中分配对象,根据我的测试,这在Swift中比不可变键更快

编辑:我也尝试过切换到NSMutableDictionary,但当使用Swift对象作为键时,速度似乎非常慢

EDIT2:我尝试过用objc实现这个测试(没有可选的解包开销),它比Java更快,但仍然慢一个数量级。。。我将提出这个例子,作为另一个问题,看看是否有人有想法

第三版-回答。我在下面的答案中公布了我的结论和解决方法

public final class MyKey : Hashable {
    var xi : Int = 0
    init( _ xi : Int ) { set( xi ) }  
    final func set( xi : Int) { self.xi = xi }
    public final var hashValue: Int { return xi }
}
public func == (lhs: MyKey, rhs: MyKey) -> Bool {
    if ( lhs === rhs ) { return true }
    return lhs.xi==rhs.xi
}

...
var map = Dictionary<MyKey,Bool>()
let range = 2500
for x in 0...range { map[ MyKey(x) ] = true }
let runs = 10
for _ in 0...runs
{
    let time = Time()
    let reps = 10000
    let key = MyKey(0)
    for _ in 0...reps {
        for x in 0...range {
            key.set(x)
            if ( map[ key ] == nil ) { XCTAssertTrue(false) }
        }
    }
    print("rate=\(time.rate( reps*range )) lookups/s")
}
公共最终类MyKey:Hashable{
席曦:int=0
init(席席:int){SET(XI)}
最终FUNC集(席:int){Self.Xi=席}
公共最终var hash值:int {返回席}
}
公共函数==(左:MyKey,右:MyKey)->Bool{
如果(lhs==rhs){返回真值}
返回LHS。席= = RH.XI
}
...
var map=Dictionary()
让范围=2500
对于0中的x…范围{map[MyKey(x)]=true}
让我们跑10次
对于0…次运行中的uu
{
让时间=时间()
让重复次数=10000
let key=MyKey(0)
对于uu0…次{
对于0…范围内的x{
密钥集(x)
if(map[key]==nil){xctasertrue(false)}
}
}
打印(“速率=\(时间速率(重复次数*范围))查找/s”)
}
下面是相应的Java代码:

public class MyKey  {
    public int xi;
    public MyKey( int xi ) { set( xi ); }
    public void set( int xi) { this.xi = xi; }

    @Override public int hashCode() { return xi; }

    @Override
    public boolean equals( Object o ) {
        if ( o == this ) { return true; }
        MyKey mk = (MyKey)o;
        return mk.xi == this.xi;
    }
}
...
    Map<MyKey,Boolean> map = new HashMap<>();
    int range = 2500;    
    for(int x=0; x<range; x++) { map.put( new MyKey(x), true ); }

    int runs = 10;
    for(int run=0; run<runs; run++)
    {
        Time time = new Time();
        int reps = 10000;
        MyKey buffer = new MyKey( 0 );
        for (int it = 0; it < reps; it++) {
            for (int x = 0; x < range; x++) {
                buffer.set( x );
                if ( map.get( buffer ) == null ) { Assert.assertTrue( false ); }
            }
        }
        float rate = reps*range/time.s();
        System.out.println( "rate = " + rate );
    }
公共类MyKey{
公众席;
公共MyKey(int席){SET(席);}
公共无效集(int席){TIS.X=席;}
“重写公共int HASCODE()){返回席;}
@凌驾
公共布尔等于(对象o){
如果(o==this){return true;}
MyKey mk=(MyKey)o;
返回MK.X= = THE.XI;
}
}
...
Map Map=newhashmap();
整数范围=2500;

对于(intx=0;x经过多次实验,我得出了一些结论,并找到了一个解决办法(尽管有些极端)

首先,我要说的是,我认识到这种紧密循环中的细粒度数据结构访问并不能代表总体性能,但它确实会影响我的应用程序,我正在想象其他应用程序,如游戏和大量数字应用程序。我也要说,我知道Swift是一个移动的目标,我相信它会提高性能ove-也许在您阅读本文时,我下面的解决方法(hacks)是不必要的。但是如果您今天尝试做类似的事情,并且您正在查看工具,并且看到您的大部分应用程序时间都花在保留/发布上,并且您不想在objc中重写您的整个应用程序,请继续阅读

我发现,在Swift中,几乎任何涉及对象引用的操作都会产生ARC保留/释放惩罚。此外,可选值——甚至可选原语——也会产生此成本。这几乎排除了使用Dictionary或NSDictionary的可能性

以下是一些快速解决方案中可以包含的内容:

a) 基元类型的数组

b) 只要数组在堆栈上而不是堆上,就可以创建最终对象的数组。e、 g.在方法体中声明一个数组(当然是在循环之外),并迭代地将值复制到其中。不要数组(数组)复制它

把这些放在一起,您可以基于存储例如int的数组构造一个数据结构,然后将对象的数组索引存储在该数据结构中。在循环中,您可以通过快速局部数组中的索引查找对象。在您询问“数据结构不能为我存储阵列吗?”之前,请回答“不能”,因为这将导致我上面提到的两种惩罚:(

考虑到这一解决方案的所有方面都不算太糟糕-如果您可以枚举要存储在字典/数据结构中的实体,那么您应该能够将它们存储在所述的数组中。使用上述技术,我能够在Swift中将Java性能提高2倍


如果有人还在阅读并且感兴趣的话,我会考虑更新我的示例代码和发布。


编辑:我要添加一个选项:c)也可以在Swift中使用UnsafeMutablePointer或Unmanaged来创建一个在传递时不会保留的引用。我在开始时没有意识到这一点,我会犹豫是否推荐它,因为它是一种黑客行为,但我曾在一些情况下使用它来包装一个频繁使用的数组,该数组在每次被引用时都会产生保留/释放。

您是否尝试将MyKey从类更改为结构或使用
索引(key:key)
查找数据?结构有不同的内存管理,
indexworky
可能不同,因为它不返回对象,只返回索引。我正在尝试运行它&它向我显示错误未解析标识符
时间
。为了保持代码的重点,我没有包括我的琐碎计时器代码。我把它放在这里(Java和Swift):使用IndexWorkey()的速度大致相同。“使用IndexWorkey()的速度大致相同”,原因也一样——每次都会生成一个额外的可选项。“如果有人