当前位置: 首页 > 知识库问答 >
问题:

快速字典即使优化也很慢:做不完的保留/释放?

籍光熙
2023-03-14

下面的代码将简单的值持有者映射到布尔值,它在Java中的运行速度比Swift2-xCode7Beta3快20倍以上,“最快的、积极的优化[-ofast]”和“快速的、整个模块的优化”已经开启。我在Java中每秒可以得到超过2.8亿次查找,但在Swift中只能得到大约1000万次查找。

当我在Instruments中查看它时,我看到大部分时间都是进入一对与映射查找相关联的retrain/release调用。任何关于为什么会发生这种情况的建议或解决办法都将受到欢迎。

代码的结构是我的真实代码的简化版本,它有一个更复杂的键类,还存储了其他类型(尽管Boolean对我来说是一个实际的案例)。另外,请注意,我使用了一个可变键html" target="_blank">实例进行检索,以避免在循环中分配对象,根据我的测试,这在Swift中比不可变键更快。

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

编辑2:我尝试过在objc中实现测试(它不会有可选的解包装开销),它更快,但仍然比Java慢一个数量级以上...我将把这个例子作为另一个问题提出来,看看是否有人有想法。

编辑3-答案。我已经张贴了我的结论和我的解决办法在一个答案下面。

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")
}
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 );
    }

共有1个答案

易波涛
2023-03-14

经过大量的实验,我得出了一些结论,并找到了一个变通方法(尽管有些极端)。

首先让我说,我认识到这种在紧密循环中非常细粒度的数据结构访问并不能代表一般性能,但它确实影响了我的应用程序,我想象的是其他应用程序,比如游戏和大量数字应用程序。我也要说,我知道Swift是一个移动的目标,我确信它会有所改进--也许我下面的变通方法(hacks)在你读到这篇文章的时候已经不是必要的了。但是,如果您今天正在尝试这样做,并且您正在查看仪器,并且看到您的应用程序的大部分时间都花在保留/发布上,而您不想用objc重写整个应用程序,请继续阅读。

我发现的是,在Swift中几乎任何接触对象引用的操作都会招致弧保留/释放惩罚。另外,可选值--甚至是可选原语--也会产生这种代价。这几乎排除了使用Dictionary或NSDictionary的可能性。

a)基元类型的数组。

b)final对象的数组,只要数组在堆栈上而不是堆上。例如,在方法体中声明一个数组(当然是在循环之外),并迭代地将值复制到它。不要array(数组)复制它。

将这些结合起来,您可以基于存储例如int的数组构造一个数据结构,然后在该数据结构中存储对象的数组索引。在循环中,您可以根据对象在快速本地数组中的索引来查找对象。在您问“数据结构不能为我存储数组吗”之前--不,因为这将招致我上面提到的两个惩罚:(

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

编辑:我会添加一个选项:c)也可以在Swift中使用unsafeMutablePointer<>或unmanaged<>来创建一个在传递时不会被保留的引用。当我开始时我并不知道这一点,我会犹豫推荐它,因为它是一个黑客,但我在一些情况下使用它来包装一个大量使用的数组,它在每次被引用时都会引起保留/释放。

 类似资料:
  • 问题内容: 下面的代码将简单的值持有者映射为布尔值,在Java中的运行速度比Swift 2快20倍-XCode 7 beta3,“最快,积极的优化[-Ofast]”和“最快,完整的模块优化”处于打开状态。在Java中,每秒可以进行2.8亿次以上的查询,但是在Swift中,只能达到1000万次。 当我在Instruments中查看它时,我看到大多数时间都在进行与映射查找相关的一对保留/释放调用。关于

  • 我正在使用Swift中的实现实质上是一个缓存。演出远未达到我的预期。我读过一些其他问题,例如关于数组排序的问题,它似乎暗示是答案(如果您准备接受它带来的更改)。但是,即使在编译时,性能也比其他语言差。我使用的是Swift 1.0版(Swift-600.0.34.4.8)。 下面是一个简单的例子来说明这个问题: 使用编译时,运行时间超过两秒: 相比之下,这个Java实现: 又快了6倍: 难道仅仅是复

  • 问题内容: 我试图弄清楚如何在MySQL中优化一个非常慢的查询(我没有设计这个): 比较一下: 说明语句对我没有帮助: 好的,它仍然认为它需要大约400万个条目才能计数,但是我可以计算文件中的行数比这还要快!我不明白为什么MySQL要花这么长时间。 这是表的定义: 版: 有什么明显的我想念的东西吗?(是的,我已经尝试过“ SELECT COUNT(change_event_id)”,但是没有性能差

  • 问题内容: 我正在尝试复制在目标C中执行的for循环,但遇到“’AnyObject’没有名为’GeneratorType’的成员错误: 这是我的雨燕 我试过为字典定义一个holder变量。任何人都可以看到我在做什么错吗? 问题答案: 这不是字典的循环。它循环通过存储在其中一个字典键中的数组。例如,如果您有一个字符串数组作为字典的键之一,这就是想要做的事情。 如果您确实想只遍历字典,这在Swift中

  • 问题内容: (重要)编辑3: 单独运行testajax2.php而 _不是_Ajax。持续时间大致相同,为1.02-1.03秒。所以我想这意味着问题出在PHP- MySQL或XAMPP中? 当我通过phpMyAdmin查询运行它时,结果如下:显示第0-29行(总计约50行。查询用时 0.0015秒 )。看来问题根本不在于Ajax,而可能在于PHP。我怎样才能解决这个问题?(我也刚刚编辑了问题标题。

  • 我正在尝试在Swift中构建一个数据结构,将一个整数映射到一个对象数组(一个以int为键、数组为值的字典)。这些对象非常小,它们只包装了一个UIColor和一个Int。我有两个实现,一个使用Swift数组作为字典的值类型,而另一个使用NSMutableArray作为值类型。我的Objective-C代码运行速度非常快,但我的Swift代码运行速度非常慢。理想情况下,我不想使用NSMutableAr