为什么Dagger的可重用范围比Singleton慢?

时间:2018-01-10 11:24:40

标签: performance kotlin dagger-2

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

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

@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有什么好处吗?

1 个答案:

答案 0 :(得分:4)

David Medenjak所述,并在评论中链接micro benchmarks in the JVM are difficult to get right。即使将您的结果看成是面值,在一个紧密的十亿次呼叫内循环中,这些呼叫的平均值平均在1ns之内,彼此之间的比例为20%。

尽管我写了separate SO answer并提供了更多详细信息,但我可以解决您的“它有没有好处”的问题:

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

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

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

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