JetBrains / compose-multiplatform

Compose Multiplatform, a modern UI framework for Kotlin that makes building performant and beautiful user interfaces easy and enjoyable.
https://jetbrains.com/lp/compose-multiplatform
Apache License 2.0
16.17k stars 1.17k forks source link

Improve startup time on Desktop #3319

Closed igordmn closed 1 month ago

igordmn commented 1 year ago

Compose 1.4.1

Code that measures time from the start to the begining of the second frame (when the first frame is on the display)

import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.window.singleWindowApplication

var isFirstFrame by mutableStateOf(true)

fun main() {
    val t1 = System.currentTimeMillis()
    singleWindowApplication {
        Canvas(Modifier.fillMaxSize()) {
            if (!isFirstFrame) {
                val t2 = System.currentTimeMillis()
                println(t2 - t1)
            }
            isFirstFrame = false
        }
    }
}

Windows 11, OpenJDK 17/20, Intel i9-13900K: 1.5 sec macOs, OpenJDK 20, Intel i9, macBook Pro 2019: 1 sec (OpenJDK 17 - 3.5-4 sec)

These numbers are too high for an empty window. It also will be significantly greater for low-end machines

Child issues: https://github.com/JetBrains/compose-multiplatform/issues/2645 https://github.com/JetBrains/compose-multiplatform/issues/2517

igordmn commented 1 year ago

When I check only a Swing window:

import java.awt.Dimension
import java.awt.Graphics
import javax.swing.JFrame
import javax.swing.SwingUtilities
fun main() {
    val t1 = System.currentTimeMillis()
    SwingUtilities.invokeLater {
        val f = object : JFrame() {
            override fun paint(g: Graphics?) {
                super.paint(g)
                val t2 = System.currentTimeMillis()
                println(t2 - t1)
            }
        }
        f.size = Dimension(200, 200)
        f.isVisible = true
    }
}

Windows 11, OpenJDK 17/20, Intel i9-13900K: 200ms macOs, OpenJDK 20, Intel i9, macBook Pro 2019: 600ms (OpenJDK 17 - 2.5-3 sec)

m-sasha commented 1 year ago

Note that at the very least we load ~70% more classes. The AWT sample loads ~2200 classes, the Compose one loads ~3700.

m-sasha commented 1 year ago

On my Mac the AWT example is 270ms, the Compose one is 500ms. If I preload all the androidx.compose classes, the Compose example drops to 420ms.

m-sasha commented 1 year ago

Making a list of all the classes loaded by the app (by running the JVM with -verbose:class) and then starting their loading in a background thread improves the startup time significantly.

okushnikov commented 2 months ago

Please check the following ticket on YouTrack for follow-ups to this issue. GitHub issues will be closed in the coming weeks.