【问题标题】:Why Dagger's Reusable scope is Slower than Singleton?为什么 Dagger 的 Reusable scope 比 Singleton 慢?
【发布时间】:2018-01-10 11:24:39
【问题描述】:

我一直认为,在使用 Dagger2 时,如果我们不需要保证始终获得相同的实例,我们应该使用 @Reusable 范围而不是 @Singleton,因为 @Singleton 使用了双重检查,又贵又慢……

不过,我做了一个简单的性能测试,结果如下:

Reusable  4474 ms
Singleton 3603 ms

代码如下:

@Singleton
@Component
interface AppComponent {

    fun getReusable(): ReusableFoo

    fun getSingleton(): SingletonFoo
}

@Reusable
class ReusableFoo @Inject constructor()

@Singleton
class SingletonFoo @Inject constructor()

class TestClass {
    @Test
    fun test() {
        val component = DaggerAppComponent.builder().build()
        measure {
            component.getReusable()
        }
        measure {
            component.getSingleton()
        }
    }

    private fun measure(block: () -> Unit) {
        val start = System.currentTimeMillis()
        (0..1000000000).forEach { block() }
        println(System.currentTimeMillis() - start)
    }
}

在构造较重的类(我尝试过使用Retrofit)和使用@Provide 注释方法而不是构造函数注入时出现同样的现象。

我是不是在测试中犯了一个错误,或者只是 @Reusable 比较慢?如果是这样,我们应该在哪里使用它?它比@Singleton 有什么好处吗?

【问题讨论】:

  • 实际上,要构建一个 DCL 惯用语比普通的、非线程安全的惰性初始化惯用语要昂贵得多的场景实际上并不容易。唯一的区别是,使用 DCL,您总是必须从“内存”中获取,但在一个紧密的循环中,您只是到达 CPU 缓存。在一个紧密的循环之外,这两个习语可能都需要内存访问并且处于相同的基础上。
  • 另一点:你已经测量了单例的 3.6 纳秒和可重用的 4.4 纳秒。那是 0.8 纳秒的差异,我敢打赌,有很多很多候选的微观级别的解释,所有这些都非常没有启发性。您真正的结论应该是“它根本没有区别”您使用哪个范围。或者你应该提出一个不同的基准来显示更明显的差异。
  • 尝试颠倒您首先测试的顺序,如果它突然相反,我不会感到惊讶,也可能在这里查看微基准:stackoverflow.com/a/513259/1837367
  • @DavidMedenjak 我试过了;)但没有区别。感谢您提供此链接 - 回复非常有价值,尤其是其下方的一条评论:另外,除非您对 + 或 - 15 毫秒的准确度满意,否则切勿使用 System.currentTimeMillis()

标签: performance kotlin dagger-2


【解决方案1】:

正如 David Medenjak 在 cmets 中提到并链接的那样,micro benchmarks in the JVM are difficult to get right。即使从表面上看你的结果,在一个紧密的十亿调用内部循环中,调用平均在 1ns 和 20% 之内。

虽然我已经写了separate SO answer 的更多详细信息,但我可以解决您“它有什么好处”的问题:

  1. @Reusable 的主要性能优势是在多线程应用程序中构建期间,因为在发生竞争条件时@Reusable可能为单独的线程创建单独的对象,而不是同步创建。一旦你支付了创建成本(就像你在每个块的第一次调用中所做的那样),接下来的十亿次调用是免费的(或接近免费的),尤其是使用 JVM 内联和缓存,并且在同一个线程的同一个堆栈中.尽管您的基准测试并未显示这一点,但如果您的绑定创建存在任何线程争用,您可能仍会看到 @Reusable 的更好性能。

  2. @Reusable 的主要内存 优势在于可重用实例保存在直接使用它的最窄组件中。如果您有一个 Android Fragment 组件作为 @Reusable 绑定的唯一使用者,那么当您销毁 Fragment 并收集其组件时,Android 将释放并回收该内存。

  3. @Reusable 的主要 Dagger 可用性 优势在于,与 @Singleton 和自定义范围不同,@Reusable 绑定可以包含在任何组件中,无论有多少范围注释在组件上。如果您有@Singleton 绑定,则绝对需要将绑定安装在使用@Singleton 注释的组件中。

  4. @Reusable 的主要开发人员可用性优势在于,与使用@Singleton@ActivityScoped 注释的绑定不同,您声明绑定是无状态或否则需要@Singleton。如果你想在 Dagger 之外使用你的绑定(或者你想有一天替换 Dagger),你需要记录或确定 @Singleton 绑定是否一定是 @Singleton 或者它是否只是一个优化机会。有了@Reusable,歧义就消失了。

【讨论】:

    猜你喜欢
    • 2017-01-01
    • 1970-01-01
    • 2011-03-23
    • 2020-04-02
    • 1970-01-01
    • 1970-01-01
    • 2019-12-18
    • 2015-10-04
    • 2012-09-16
    相关资源
    最近更新 更多