Praktik terbaik untuk performa startup aplikasi WinUI Anda

Buat aplikasi WinUI dengan Windows App SDK yang dimulai dengan cepat dengan mengurangi pekerjaan awal, menyederhanakan frame pertama, dan memuat fitur non-kritis setelah jendela menjadi interaktif.

Praktik terbaik untuk performa startup aplikasi Anda

Sebagian, pengguna melihat apakah aplikasi Anda cepat atau lambat berdasarkan berapa lama waktu yang dibutuhkan untuk memulai. Untuk tujuan topik ini, waktu mulai aplikasi dimulai ketika pengguna memulai aplikasi dan berakhir ketika pengguna dapat berinteraksi dengan aplikasi dengan cara yang bermakna. Artikel ini memberikan saran tentang cara mendapatkan performa startup yang lebih baik dari aplikasi WinUI.

Mengukur waktu mulai aplikasi Anda

Pastikan untuk memulai aplikasi beberapa kali sebelum Anda benar-benar mengukur waktu mulainya. Ini memberi Anda garis besar untuk pengukuran Anda dan membantu memastikan bahwa Anda mengukur sesingkat mungkin waktu startup.

Lakukan pengukuran yang mewakili apa yang akan dialami pengguna akhir. Ukur build rilis pada perangkat keras representatif, lihat startup dingin dan hangat, dan fokus pada waktu untuk mencapai bingkai interaktif pertama daripada hanya waktu sampai proses dimulai.

Tangguhkan pekerjaan selama mungkin

Untuk meningkatkan waktu mulai aplikasi Anda, lakukan hanya pekerjaan yang benar-benar perlu dilakukan untuk memungkinkan pengguna mulai berinteraksi dengan aplikasi. Ini bisa sangat bermanfaat jika Anda dapat menunda pemuatan rakitan tambahan. Common Language Runtime memuat assembly saat pertama kali digunakan. Jika Anda dapat meminimalkan jumlah rakitan yang dimuat, Anda mungkin dapat meningkatkan waktu mulai aplikasi dan konsumsi memorinya.

Lakukan pekerjaan jangka panjang secara independen

Aplikasi Anda dapat interaktif meskipun ada bagian aplikasi yang tidak berfungsi penuh. Misalnya, jika aplikasi Anda menampilkan data yang membutuhkan waktu cukup lama untuk diambil, Anda dapat membuat kode tersebut dijalankan secara independen dari kode startup aplikasi dengan mengambil data secara asinkron. Saat data tersedia, isi antarmuka pengguna aplikasi dengan data.

Banyak API yang mengambil data tidak sinkron, jadi Anda mungkin akan mengambil data secara asinkron. Untuk informasi selengkapnya, lihat Pemrograman asinkron dengan asinkron dan tunggu. Jika Anda melakukan pekerjaan yang tidak menggunakan API asinkron, Anda dapat menggunakan Task kelas untuk melakukan pekerjaan jangka panjang sehingga Anda tidak memblokir pengguna untuk berinteraksi dengan aplikasi. Ini membuat aplikasi Anda tetap responsif saat data dimuat.

Jika aplikasi Anda membutuhkan waktu yang sangat lama untuk memuat bagian dari UI-nya, pertimbangkan untuk menampilkan pesan di area tersebut seperti "Mendapatkan data terbaru" sehingga pengguna Anda tahu bahwa aplikasi masih diproses.

Meminimalkan waktu mulai

Semua kecuali aplikasi paling sederhana memerlukan jumlah waktu yang dapat dirasakan untuk memuat sumber daya, mengurai XAML, menyiapkan struktur data, dan menjalankan logika selama peluncuran. Untuk aplikasi WinUI, ini membantu memikirkan startup dalam empat tahap: peluncuran proses, pembuatan jendela, pembuatan halaman utama, dan tata letak/render untuk bingkai pertama.

Periode startup adalah waktu antara saat pengguna memulai aplikasi dan saat aplikasi menjadi berfungsi. Ini adalah waktu penting karena ini adalah kesan pertama pengguna tentang aplikasi Anda. Pengguna mengharapkan umpan balik instan dan berkelanjutan dari sistem dan dari aplikasi. Sistem dan aplikasi dianggap rusak atau dirancang dengan buruk saat aplikasi tidak dimulai dengan cepat.

Pengantar tahapan startup

Startup melibatkan sejumlah bagian yang bergerak, dan semuanya perlu dikoordinasikan untuk pengalaman pengguna terbaik. Langkah-langkah berikut terjadi antara pengguna yang meluncurkan aplikasi Anda dan konten aplikasi yang ditampilkan.

  • Proses ini dimulai dan memanggil kode startup yang dihasilkan oleh templat Main.
  • Objek Application dibuat.
    • Konstruktor aplikasi memanggil InitializeComponent, yang menyebabkan App.xaml diurai dan objek dibuat.
  • Application.OnLaunched dinaikkan.
    • Kode aplikasi membuat jendela utama, menetapkan konten awal, dan panggilan Activate.
    • Konstruktor halaman utama memanggil InitializeComponent, yang menyebabkan halaman XAML diurai dan objek dibuat.
  • Kerangka kerja XAML melakukan proses tata letak, termasuk mengukur dan mengatur.
    • ApplyTemplate menyebabkan konten template kontrol dibuat untuk setiap kontrol, yang biasanya memerlukan waktu tata letak paling besar selama proses startup.
  • Render membuat visual untuk konten jendela.
  • Bingkai pertama disajikan, dan pekerjaan pasca-startup berlanjut secara asinkron.

Lakukan lebih sedikit di jalur startup Anda

Biarkan jalur kode startup Anda bebas dari apa pun yang tidak diperlukan untuk frame pertama Anda.

  • Jika Anda memiliki DLL pengguna yang berisi kontrol yang tidak diperlukan selama bingkai pertama, pertimbangkan untuk menunda pemuatannya.
  • Jika Anda memiliki sebagian UI yang bergantung pada data dari cloud, pisahkan UI tersebut. Pertama, munculkan UI yang tidak bergantung pada data cloud, lalu munculkan UI yang bergantung pada cloud secara asinkron. Anda juga harus mempertimbangkan penembolokan data secara lokal sehingga aplikasi dapat bekerja offline atau tidak terpengaruh oleh konektivitas jaringan yang lambat.
  • Tampilkan UI kemajuan jika UI Anda sedang menunggu data.
  • Berhati-hatilah dengan desain aplikasi yang melibatkan banyak penguraian file konfigurasi, atau UI yang dihasilkan secara dinamis oleh kode.

Kurangi jumlah elemen

Performa startup di aplikasi XAML berkorelasi langsung dengan jumlah elemen yang Anda buat selama startup. Semakin sedikit elemen yang Anda buat, semakin sedikit waktu yang diperlukan aplikasi Anda untuk memulai. Sebagai tolok ukur kasar, pertimbangkan bahwa setiap elemen memerlukan 1 ms untuk dibuat.

  • Templat yang digunakan dalam kontrol item dapat memiliki dampak terbesar, karena diulang beberapa kali. Lihat Pengoptimalan UI ListView dan GridView.
  • UserControls dan templat kontrol diperluas, jadi keduanya harus diperhitungkan.
  • Jika Anda membuat XAML apa pun yang tidak muncul di layar, maka Anda harus membenarkan apakah potongan-potongan XAML tersebut harus dibuat selama startup.

Jendela Pohon Visual Live di Visual Studio menampilkan jumlah elemen turunan untuk setiap simpul di pohon.

Pohon visual langsung.

Gunakan deferral. Menciutkan elemen, atau mengatur opasitasnya ke 0, tidak mencegah elemen dibuat. Menggunakan x:Load atau x:DeferLoadStrategy, Anda dapat menunda pemuatan sepotong UI dan memuatnya saat diperlukan. Ini adalah cara yang baik untuk menunda pemrosesan UI yang tidak terlihat selama startup sehingga Anda dapat memuatnya saat diperlukan atau sebagai bagian dari serangkaian logika yang tertunda. Untuk memicu pemuatan, Anda hanya perlu memanggil elemen FindName. Untuk contoh dan informasi selengkapnya, lihat atribut x:Load dan atribut x:DeferLoadStrategy.

Virtualisasi. Jika Anda memiliki konten daftar atau pengulang di UI Anda, sangat disarankan agar Anda menggunakan virtualisasi UI. Jika UI daftar tidak divirtualisasi, maka Anda membayar biaya untuk membuat semua elemen di muka, yang mana dapat memperlambat waktu mulai Anda. Lihat Pengoptimalan UI ListView dan GridView.

Performa aplikasi tidak hanya tentang performa mentah; ini juga tentang persepsi. Mengubah urutan operasi sehingga aspek visual terjadi terlebih dahulu umumnya membuat pengguna merasa seperti aplikasi lebih cepat. Pengguna menganggap aplikasi telah dimuat ketika konten muncul di layar. Aplikasi biasanya perlu melakukan beberapa hal selama startup, dan tidak semua pekerjaan tersebut diperlukan untuk memunculkan UI, sehingga potongan-potongan tersebut harus ditunda atau diprioritaskan lebih rendah dari UI.

Artikel ini membahas tentang bingkai pertama, yang berasal dari terminologi animasi dan video dan merupakan ukuran berapa lama waktu yang dibutuhkan hingga konten dilihat oleh pengguna akhir.

Memperbaiki persepsi terhadap startup

Mari kita gunakan contoh game online sederhana untuk mengidentifikasi setiap fase startup dan teknik yang berbeda untuk memberikan umpan balik pengguna sepanjang proses.

Pada fase pertama, proses diluncurkan dan aplikasi membuat jendelanya. Selama waktu ini, pengguna belum melihat konten aplikasi sendiri. Tujuan Anda adalah menampilkan jendela yang ringan di layar secepat mungkin.

Fase kedua mencakup pembuatan dan inisialisasi struktur yang penting untuk game. Jika aplikasi dapat dengan cepat membuat UI awalnya dengan data yang tersedia saat diluncurkan, maka fase ini sepele dan Anda dapat segera menampilkan UI. Jika tidak, tampilkan halaman pemuatan ringan saat aplikasi menginisialisasi.

Seperti apa halaman pemuatan terserah Anda; bisa sesederhana menampilkan bilah kemajuan atau cincin kemajuan. Poin utamanya adalah bahwa aplikasi menunjukkan bahwa aplikasi melakukan pekerjaan sebelum menjadi sepenuhnya responsif. Dalam kasus permainan, layar awal mengharuskan beberapa gambar dan suara dimuat dari disk ke dalam memori. Tugas-tugas ini membutuhkan waktu, sehingga aplikasi membuat pengguna mendapatkan informasi dengan menampilkan halaman pemuatan dengan animasi sederhana yang terkait dengan tema permainan.

Tahap ketiga dimulai setelah game memiliki sekumpulan informasi minimal untuk membuat antarmuka pengguna interaktif, yang menggantikan halaman pemuatan. Pada titik ini, satu-satunya informasi yang tersedia untuk game online mungkin adalah konten yang dimuat aplikasi dari disk. Gim ini dapat mengirim dengan konten yang cukup untuk membuat antarmuka pengguna interaktif, tetapi karena ini adalah game online yang tidak akan berfungsi penuh sampai terhubung ke internet dan mengunduh beberapa informasi tambahan. Sampai memiliki semua informasi yang dibutuhkan, pengguna dapat berinteraksi dengan UI, tetapi fitur yang membutuhkan data tambahan dari web harus memberikan umpan balik bahwa konten masih dimuat. Mungkin perlu waktu agar aplikasi berfungsi penuh, jadi penting agar fungsionalitas tersedia sesegera mungkin.

Sekarang setelah kita mengidentifikasi tiga tahap startup dalam game online, mari kita ikatkan ke kode aktual.

Fase 1 dan Fase 2

Gunakan konstruktor aplikasi hanya untuk menginisialisasi struktur data yang penting bagi aplikasi. Tetap OnLaunched fokus pada pembuatan jendela pertama dengan cepat, menetapkan konten ringan, dan mengaktifkan jendela sehingga aplikasi dapat segera menampilkan umpan balik.

public partial class App : Application
{
    public static Window MainWindow { get; private set; } = null!;

    protected override void OnLaunched(LaunchActivatedEventArgs args)
    {
        base.OnLaunched(args);

        MainWindow = new MainWindow();
        MainWindow.Content = new LoadingPage();
        MainWindow.Activate();

        _ = InitializeAsync();
    }

    private async Task InitializeAsync()
    {
        // Asynchronously restore state and load the minimum data needed
        // to create the first interactive UI.
        await LoadInitialDataAsync();

        MainWindow.Content = new GameHomePage();
    }

    private static Task LoadInitialDataAsync()
    {
        // Download data to populate the initial UI.
        return Task.CompletedTask;
    }
}

Salah satu tugas utama di OnLaunched adalah membuat UI, menetapkannya ke Window.Content, lalu memanggil Window.Activate. Jika Anda membutuhkan lebih dari satu alur aktivasi, pertahankan prinsip yang sama: tampilkan konten ringan dengan cepat dan pindahkan pekerjaan mahal dari jalur startup penting.

Aplikasi yang menampilkan halaman pemuatan selama peluncuran dapat mulai berfungsi untuk membuat UI utama di latar belakang. Setelah elemen tersebut dibuat, peristiwa FrameworkElement.Loaded terjadi. Di penanganan aktivitas, Anda dapat mengganti konten jendela, yang saat ini merupakan layar pemuatan, dengan halaman beranda yang baru dibuat.

Sangat penting bahwa aplikasi dengan periode inisialisasi yang diperpanjang menampilkan halaman pemuatan. Selain memberikan umpan balik tentang proses startup, jendela harus diaktifkan dengan cepat sehingga pengguna melihat bahwa aplikasi mengalami kemajuan.

partial class GameHomePage : Page
{
    public GameHomePage()
    {
        InitializeComponent();

        // Add a handler to be called when the home page has been loaded.
        Loaded += GameHomePageLoaded;

        // Load the minimal amount of image and sound data from disk necessary
        // to create the home page.
    }

    private void GameHomePageLoaded(object sender, RoutedEventArgs e)
    {
        // Set the content of the main window to the home page now that it's
        // ready to be displayed.
        App.MainWindow.Content = this;
    }
}

Fase 3

Hanya karena aplikasi menampilkan antarmuka pengguna tidak berarti aplikasi tersebut sepenuhnya siap digunakan. Dalam kasus permainan kami, UI ditampilkan dengan pengganti sementara untuk fitur yang memerlukan data dari internet. Pada titik ini, game mengunduh data tambahan yang diperlukan untuk membuat aplikasi berfungsi penuh dan secara progresif mengaktifkan fitur saat data diperoleh.

Terkadang banyak konten yang diperlukan untuk startup dapat dipaketkan dengan aplikasi. Demikianlah halnya dengan permainan sederhana. Ini membuat proses startup cukup sederhana. Tetapi banyak program, seperti pembaca berita dan penampil foto, harus menarik informasi dari web agar berfungsi. Data ini bisa besar dan dapat memakan waktu yang cukup lama untuk diunduh. Bagaimana aplikasi mendapatkan data ini selama startup dapat berdampak besar pada performa yang dirasakan.

Anda dapat menampilkan halaman pemuatan terlalu lama jika aplikasi mencoba mengunduh seluruh himpunan data yang diperlukan untuk fungsionalitas di fase pertama atau kedua startup. Itu membuat aplikasi terlihat menggantung. Kami menyarankan agar aplikasi mengunduh jumlah data minimal yang diperlukan untuk menampilkan UI interaktif dengan elemen tempat penampung di fase 2 dan kemudian secara progresif memuat data, yang menggantikan elemen tempat penampung, pada fase 3. Untuk informasi selengkapnya tentang menangani data, lihat Mengoptimalkan ListView dan GridView.

Bagaimana tepatnya aplikasi bereaksi terhadap setiap fase startup sepenuhnya terserah Anda, tetapi memberikan umpan balik sebanyak mungkin kepada pengguna dengan menggunakan antarmuka pengguna awal yang ringan, layar pemuatan, dan pemuatan data progresif membuat aplikasi terasa lebih cepat.

Meminimalkan rakitan terkelola di jalur mulai

Kode yang dapat digunakan kembali sering kali hadir dalam bentuk modul (DLL) yang disertakan dalam proyek. Memuat modul ini memerlukan akses disk, dan biaya dapat bertambah. Ini memiliki dampak terbesar pada startup dingin, tetapi juga dapat memengaruhi startup hangat. Di aplikasi .NET, CLR mencoba menunda biaya tersebut sebanyak mungkin dengan memuat rakitan sesuai permintaan. Artinya, CLR tidak memuat modul sampai metode yang dijalankan mereferensikannya. Jadi referensikan hanya rakitan yang diperlukan untuk peluncuran aplikasi Anda dalam kode startup sehingga CLR tidak memuat modul yang tidak perlu. Jika Anda memiliki jalur kode yang tidak digunakan di jalur startup yang memiliki referensi yang tidak perlu, pindahkan jalur kode ini ke metode lain untuk menghindari beban yang tidak perlu.

Cara lain untuk mengurangi beban modul adalah dengan menggabungkan modul aplikasi. Memuat satu rakitan besar biasanya membutuhkan waktu lebih sedikit daripada memuat dua yang kecil. Ini tidak selalu dimungkinkan, dan Anda harus menggabungkan modul hanya jika tidak membuat perbedaan material untuk produktivitas pengembang atau kegunaan kembali kode. Anda dapat menggunakan alat seperti PerfView atau Windows Performance Analyzer (WPA) untuk mengetahui modul apa yang dimuat saat startup.

Membuat permintaan web pintar

Anda dapat secara dramatis meningkatkan waktu pemuatan aplikasi dengan mengemas kontennya secara lokal, termasuk XAML, gambar, dan file lain yang penting untuk aplikasi. Operasi disk lebih cepat daripada operasi jaringan. Jika aplikasi memerlukan file tertentu saat inisialisasi, Anda dapat mengurangi waktu mulai secara keseluruhan dengan memuatnya dari disk alih-alih mengambilnya dari server jarak jauh.

Halaman jurnal dan cache yang efisien

Kontrol ini Frame menyediakan fitur navigasi. Ini menawarkan navigasi ke halaman (Navigate metode), pencatatan navigasi (BackStack dan ForwardStack properti, GoForward dan GoBack metode), penembolokan halaman (Page.NavigationCacheMode), dan dukungan serialisasi (GetNavigationState metode).

Kinerja yang harus diperhatikan dengan Frame terutama berkaitan dengan jurnal dan caching halaman.

Bingkai jurnal. Ketika Anda menavigasi ke halaman dengan Frame.Navigate, sebuah PageStackEntry untuk halaman saat ini akan ditambahkan ke kumpulan Frame.BackStack. PageStackEntry relatif kecil, tetapi tidak ada batas bawaan pada ukuran koleksi BackStack. Pengguna berpotensi dapat menavigasi dalam perulangan dan menumbuhkan koleksi ini tanpa batas.

PageStackEntry juga mencakup parameter yang diteruskan ke metode Frame.Navigate. Disarankan agar parameter menjadi jenis yang dapat diserialisasikan primitif seperti int atau string, untuk memungkinkan Frame.GetNavigationState metode berfungsi. Tetapi parameter itu berpotensi mereferensikan objek yang memanfaatkan lebih banyak bagian dari set kerja atau sumber daya lainnya, membuat setiap entri dalam BackStack menjadi jauh lebih mahal. Misalnya, Anda berpotensi menggunakan StorageFile sebagai parameter, dan akibatnya BackStack dapat menjaga jumlah file yang tidak terbatas terbuka.

Oleh karena itu disarankan untuk menjaga parameter navigasi tetap kecil dan untuk membatasi ukuran BackStack. BackStack adalah koleksi standar dalam C#, sehingga dapat dipangkas hanya dengan menghapus entri.

Caching halaman. Secara bawaan, saat Anda menavigasi ke halaman dengan metode Frame.Navigate, instans halaman baru akan dibuat. Demikian pula, jika Anda kemudian menavigasi kembali ke halaman sebelumnya dengan Frame.GoBack, instans baru halaman sebelumnya dialokasikan.

Frame juga menawarkan cache halaman opsional yang dapat menghindari instansiasi ini. Untuk memasukkan halaman ke dalam cache, gunakan properti Page.NavigationCacheMode tersebut. Mengatur mode tersebut ke Required memaksa halaman di-cache, sedangkan mengatur mode tersebut ke Enabled memungkinkannya untuk di-cache. Secara bawaan, cache memiliki ukuran 10 halaman, tetapi ini dapat diubah dengan properti Frame.CacheSize. Semua Required halaman di-cache, dan jika ada lebih sedikit dari CacheSize halaman yang diperlukan, Enabled halaman juga dapat di-cache.

Penembolokan halaman dapat membantu performa dengan menghindari instansiasi dan oleh karena itu meningkatkan performa navigasi. Cache halaman dapat merusak performa dengan penyimpanan sementara yang berlebihan dan karenanya berdampak pada set kerja.

Oleh karena itu, disarankan untuk menggunakan caching halaman yang sesuai untuk aplikasi Anda. Misalnya, Anda memiliki aplikasi yang menampilkan daftar item dalam Frame, dan saat Anda memilih item, Anda akan menavigasi bingkai ke halaman detail untuk item tersebut. Halaman daftar mungkin harus disetel ke mode cache. Jika halaman detailnya sama untuk semua item, mungkin juga harus di-cache. Tetapi jika halaman detail lebih heterogen, mungkin lebih baik mematikan penyimpanan sementara.