
Hal yang romantis, seperti langit berbintang, dan hal yang hardcore, seperti mengoptimalkan konsumsi memori aplikasi iOS, mungkin berjalan bersama-sama: ada baiknya mencoba mendorong langit berbintang ini menjadi aplikasi AR, dan pertanyaan tentang konsumsi itu akan segera muncul.
Untuk meminimalkan penggunaan memori akan berguna dalam banyak kasus lainnya. Jadi teks ini pada contoh proyek kecil menunjukkan metode optimasi yang dapat berguna dalam aplikasi iOS yang sama sekali berbeda (dan tidak hanya iOS-).
Posting disiapkan berdasarkan transkrip laporan
Conrad Filer dari konferensi Pius 2018 Mobius. Kami melampirkan videonya, dan kemudian versi teks pada orang pertama:
Senang menyambut semua orang! Nama saya Conrad Filer, dan dengan nama spektakuler "A Million Stars in One iPhone" kami akan membahas bagaimana Anda dapat meminimalkan ukuran memori yang ditempati oleh aplikasi iOS Anda. Penuh warna dan dalam contoh.
Mengapa mengoptimalkan?
Apa yang secara umum mendorong kita untuk melakukan optimasi, apa sebenarnya yang ingin kita capai? Kami tidak menginginkan ini:
Kami tidak ingin pengguna menunggu. Artinya, alasan pertama adalah untuk
mengurangi waktu boot .
Alasan lain adalah
untuk meningkatkan kualitas .

Kita dapat berbicara tentang kualitas gambar, suara, dan bahkan AI. “AI yang Dioptimalkan” berarti Anda dapat mencapai lebih banyak - misalnya, menghitung gim untuk jumlah gerakan yang lebih maju.
Alasan ketiga sangat penting:
menghemat daya baterai . Optimalisasi membantu mengurangi daya baterai. Inilah perbandingan yang menarik, meskipun dari dunia Android. Di sini membandingkan Vulkan dan OpenGL ES:

Yang kedua lebih buruk dioptimalkan untuk platform seluler. Mengamati kecepatan konsumsi daya baterai, Anda dapat melihat bahwa untuk gambar yang serupa, OpenGL ES menghabiskan lebih banyak sumber daya daripada Vulkan.
Apa jenis pengoptimalan yang dapat membantu di sini? Misalnya, dalam game berbasis giliran, ketika pengguna berpikir tentang perpindahannya, Anda dapat mengurangi FPS menjadi nol. Jika Anda memiliki mesin 3D, maka sangat bijaksana untuk mematikan semuanya sementara pengguna hanya melihat layar.
Selain itu, ada kalanya tanpa pendekatan yang dioptimalkan Anda tidak akan dapat mengimplementasikan satu atau fitur canggih lainnya: itu tidak akan ditarik.
Tidak ada fanatisme
Berbicara tentang pengoptimalan, orang tidak dapat tidak mengingat tesis Donald Knuth: “Kita harus melupakan efisiensi yang rendah, misalnya, dalam 97% kasus: pengoptimalan prematur adalah akar dari semua kejahatan. Meskipun kita tidak harus menyerahkan kemampuan kita dalam 3% kritis ini. "
Dalam 97% kasus, kita seharusnya tidak memedulikan efisiensi, tetapi pertama-tama tentang bagaimana membuat kode kita dapat dimengerti, aman dan dapat diuji. Kami masih mengembangkan untuk perangkat seluler, dan bukan untuk pesawat ruang angkasa. Perusahaan tempat kami bekerja tidak boleh membayar lebih untuk dukungan kode yang kami tulis. Selain itu, waktu kerja pengembang memiliki biaya, dan jika Anda menghabiskannya untuk mengoptimalkan sesuatu yang tidak penting, maka Anda menghabiskan uang perusahaan. Fakta bahwa kode yang dioptimalkan dengan baik cenderung lebih sulit untuk dipahami, Anda dapat melihat contoh-contoh yang akan saya tunjukkan hari ini.
Secara umum, memprioritaskan dan mengoptimalkan secara bermakna sesuai kebutuhan.
Pendekatannya
Saat mengerjakan pengoptimalan, kami biasanya memantau kinerja (baca: beban prosesor) atau jumlah memori yang digunakan. Seringkali kedua opsi ini akan bertentangan, dan Anda perlu menemukan keseimbangan di antara keduanya.
Dalam hal prosesor, kami dapat mengurangi jumlah siklus prosesor yang diperlukan oleh operasi kami. Seperti yang Anda ketahui, lebih sedikit siklus prosesor memberi kami waktu muat lebih sedikit, konsumsi baterai lebih sedikit, kemampuan untuk memberikan kualitas yang lebih baik, dll.
Untuk pengembang iOS, Xcode Instruments memiliki alat Time Profiler yang praktis. Ini memungkinkan Anda untuk melacak jumlah siklus CPU yang dihabiskan oleh berbagai bagian aplikasi Anda. Laporan ini bukan tentang alat, jadi saya tidak akan memerinci sekarang, ada video bagus dari WWDC tentang ini.
Anda dapat memilih tujuan lain - pengoptimalan demi memori. Kami akan mencoba memastikan bahwa pada saat permulaan aplikasi kami cocok dengan jumlah sel RAM sekecil mungkin. Ingatlah bahwa aplikasi yang paling banyak adalah kandidat pertama untuk shutdown paksa saat pembersihan, yang OS harus lakukan. Oleh karena itu, ini mempengaruhi berapa lama aplikasi Anda tetap di latar belakang.
Penting juga bahwa sumber daya RAM untuk perangkat yang berbeda juga berbeda. Jika Anda, katakanlah, memutuskan untuk mengembangkan untuk Apple Watch, maka tidak ada cukup memori, dan ini juga membuat Anda mengoptimalkan.
Akhirnya, kadang-kadang sejumlah kecil memori juga membuat program sangat cepat. Saya akan memberi contoh. Berikut adalah struktur berbagai ukuran dalam byte:

Element8 berisi 8 byte, Element16 - 16, dan sebagainya.

Kami akan membuat array, satu untuk setiap jenis struktur kami. Dimensi semua array adalah sama - 10.000 elemen. Setiap struktur berisi jumlah bidang yang berbeda (bertambah); bidang n adalah bidang pertama dan, karenanya, ada di semua struktur.
Sekarang mari kita coba yang berikut ini: untuk setiap array kita akan menghitung jumlah semua bidangnya n. Artinya, setiap kali kita akan menjumlahkan jumlah elemen yang sama (10.000 buah). Satu-satunya perbedaan adalah bahwa untuk setiap jumlah variabel n akan diekstraksi dari struktur dengan ukuran yang berbeda. Kami tertarik pada apakah penjumlahan membutuhkan waktu yang sama.
Hasilnya adalah sebagai berikut:

Grafik menunjukkan ketergantungan waktu penjumlahan pada ukuran struktur yang digunakan dalam larik. Ternyata mendapatkan bidang n dari struktur yang lebih besar lebih lama, dan oleh karena itu operasi penjumlahan membutuhkan waktu lebih lama.
Banyak dari Anda sudah mengerti mengapa ini terjadi.
Prosesor ini memiliki cache L1, L2 (kadang-kadang bahkan L3 dan L4). Prosesor mengakses jenis memori ini secara langsung dan cepat.

Tembolok ada untuk mempercepat penggunaan kembali data. Misalkan kita bekerja dengan array. Jika array yang dibutuhkan oleh prosesor sudah ada di salah satu cache, maka sudah diperlukan oleh prosesor sebelumnya. Pada saat itu, ia meminta mereka dari memori utama, menempatkannya di cache, melakukan semua operasi yang diperlukan dengan mereka, setelah itu data ini tetap berbohong (tidak punya waktu untuk dihapus oleh orang lain).

Ukuran L1, cache L2 tidak begitu besar. Array yang dibutuhkan oleh prosesor untuk bekerja mungkin lebih besar. Untuk sepenuhnya menjalankan operasi pada array seperti itu, kita harus membongkar ke dalam cache di bagian-bagian dan beroperasi pada bagian-bagian ini satu per satu. Karena permintaan konstan ke memori utama, pemrosesan array kami akan memakan waktu lebih lama.
Saat memprogram struktur data, cobalah untuk mengingat cache. Ada kemungkinan bahwa dengan mengurangi ukuran struktur data Anda, Anda akan mencapai kapasitas cache yang sukses dan mempercepat operasi yang akan dilakukan di masa depan. Interaksi dengan memori utama selalu, sedang dan kemungkinan besar akan tetap menjadi faktor penting dalam produktivitas - bahkan ketika Anda menulis di Swift untuk perangkat berkinerja tinggi modern.
CPU vs RAM: inisialisasi malas
Meskipun dalam beberapa kasus, ketika memori yang digunakan berkurang, program mulai bekerja lebih cepat, ada beberapa kasus ketika kedua metrik ini, sebaliknya, konflik. Saya akan memberi contoh dengan konsep inisialisasi malas.
Misalkan kita memiliki metode makeHeavyObject () yang mengembalikan beberapa objek besar. Metode ini akan menginisialisasi variabel lazilyCalculated.

Pengubah malas mengatur variabel lazilyCalculated ke inisialisasi malas. Ini berarti bahwa nilai akan ditetapkan hanya ketika panggilan pertama terjadi selama eksekusi. Kemudian metode makeHeavyObject () akan bekerja dan objek yang dihasilkan akan ditugaskan ke variabel lazilyCalculated.
Apa kelebihannya di sini? Dari saat inisialisasi (meskipun nanti, tetapi akan dieksekusi) kami memiliki objek yang terletak di memori. Nilainya dihitung, siap digunakan - buat saja permintaan. Hal lain adalah bahwa objek kita besar dan dari saat inisialisasi akan menempati dalam memori bagian terbesar sel.
Anda bisa pergi ke arah lain - jangan menyimpan nilai bidang sama sekali:

Dengan setiap tautan ke bidang lazilyCalculated, metode makeHeavyObject () akan dieksekusi lagi. Nilai akan dikembalikan ke titik kueri, sementara itu tidak akan ditempatkan di memori. Seperti yang Anda lihat, menyimpan variabel adalah opsional.
Apa yang lebih mahal - untuk menyimpan objek besar dalam memori, tetapi tidak membuang waktu CPU, atau memanggil metode setiap kali kita membutuhkan bidang kita, sekaligus menghemat memori? Haruskah Anda memiliki nilai siap pakai atau menghitungnya dengan cepat? Dilema semacam ini cukup sering muncul, di mana pun Anda melakukan perhitungan - di server jauh atau di mesin lokal Anda, apa pun cache yang harus Anda gunakan. Anda harus membuat keputusan berdasarkan keterbatasan sistem dalam kasus khusus ini.
Siklus optimisasi

Apa pun yang Anda optimalkan, pekerjaan Anda, sebagai suatu peraturan, akan dibangun di atas algoritma yang sama. Pertama, Anda memeriksa kode, profil / ukuran (dalam Xcode menggunakan alat yang sesuai), mencoba mengidentifikasi kemacetannya. Intinya, atur metode dengan berapa lama waktu yang diperlukan untuk mengeksekusi. Dan kemudian lihat baris paling atas untuk menentukan apa yang harus dioptimalkan.
Memilih objek, Anda menetapkan sendiri tugas (atau, berbicara secara ilmiah, mengajukan hipotesis): dengan menerapkan ini atau metode optimasi lainnya, Anda dapat membuat bagian kode yang dipilih bekerja lebih cepat.
Selanjutnya, Anda mencoba mengoptimalkan. Setelah setiap modifikasi, Anda melihat indikator kinerja, mengevaluasi seberapa efektif modifikasi itu, seberapa banyak Anda berhasil maju.
Sama seperti dalam karya ilmiah: spekulasi, eksperimen, analisis hasil. Anda melalui siklus tindakan ini berulang-ulang. Praktek menunjukkan bahwa pekerjaan yang dibangun dengan cara ini benar-benar memungkinkan Anda untuk menghilangkan botneks satu per satu.
Tes unit

Secara singkat tentang pengujian unit: kami memiliki beberapa fungsi yang kami uji, beberapa input data input dan output data output; menerima input sebagai input, fungsi kami harus selalu mengembalikan output, dan tidak ada optimasi kami yang melanggar properti ini.
Tes unit membantu kami melacak kerusakan. Jika, sebagai respons terhadap input, fungsi kami berhenti mengembalikan output, maka, baik secara langsung maupun tidak langsung, kami mengubah arah kerja fungsi kami yang lama.
Jangan coba-coba mulai mengoptimalkan jika Anda belum menulis sebagian besar unit test untuk kode Anda. Anda harus dapat melakukan tes regresi. Jika Anda melihat GitHub my commit dalam contoh aplikasi saya, yang akan saya bahas selanjutnya, Anda dapat melihat bahwa beberapa optimasi saya membawa bug.
Dan sekarang untuk bagian yang menyenangkan, mari kita beralih ke bintang-bintang.
Juta Bintang
Ada basis data besar yang menggambarkan jutaan bintang. Selain itu, saya membuat beberapa aplikasi. Salah satunya menggunakan augmented reality, secara real time menggambar bintang di atas gambar dari kamera ponsel. Sekarang saya akan menunjukkannya dalam aksi:

Dengan tidak adanya lampu kota, seseorang dapat membedakan hingga 8.000 bintang di langit. Saya membutuhkan sekitar 1,8 MB untuk menyimpan 8.000 catatan. Secara prinsip, bisa diterima. Tetapi saya ingin menambahkan bintang-bintang yang dapat dilihat seseorang melalui teleskop - ternyata sekitar 120.000 bintang (menurut apa yang disebut katalog Hipparcos, sekarang sudah usang). Ini sudah membutuhkan 27 MB. Dan di antara katalog modern di domain publik Anda dapat menemukan satu yang akan berjumlah sekitar 2.500.000 bintang. Database seperti itu sudah akan menempati sekitar 560 MB. Seperti yang Anda lihat, banyak memori sudah diperlukan. Tapi kami tidak hanya menginginkan basis data, tetapi sebuah aplikasi berbasiskannya, di mana akan ada ARKit, SceneKit dan hal-hal lain yang juga membutuhkan memori.
Apa yang harus dilakukan
Kami akan mengoptimalkan bintang-bintang.
Alat MemoryLayout
Anda dapat mengevaluasi ukuran program secara keseluruhan. Tetapi untuk pekerjaan perhiasan seperti optimasi, Anda akan memerlukan alat untuk memperkirakan ukuran setiap struktur data individu.
Swift memungkinkan Anda melakukan ini dengan cukup sederhana - menggunakan objek MemoryLayout <>. Anda mendeklarasikan MemoryLayout <>, menentukan struktur data yang menarik bagi Anda sebagai tipe generik. Sekarang, merujuk pada properti dari objek yang diterima, Anda dapat menerima berbagai informasi berguna tentang struktur Anda.

Properti ukuran memberi kita jumlah byte yang ditempati oleh satu instance dari struktur.
Sekarang tentang properti langkahnya. Anda mungkin telah memperhatikan bahwa ukuran array, sebagai aturan, tidak sama dengan jumlah ukuran elemen penyusunnya, tetapi melebihi itu. Jelas, beberapa "udara" tertinggal di antara elemen-elemen dalam memori. Untuk memperkirakan jarak antara elemen berurutan dalam array yang berdekatan, kami menggunakan properti langkahnya. Jika Anda mengalikannya dengan jumlah elemen dalam array, Anda mendapatkan ukurannya.

StarData, struktur eksperimental kami, dalam keadaan awal yang tidak dioptimalkan:

Berikut adalah struktur data yang dirancang untuk menyimpan data tentang satu bintang. Anda tidak perlu mempelajari apa arti masing-masing elemen ini. Lebih penting sekarang untuk memperhatikan jenis: Variabel float yang menyimpan koordinat bintang (pada kenyataannya, lintang dan bujur), beberapa Int32 untuk berbagai ID, String untuk menyimpan nama dan nama berbagai klasifikasi; ada jarak, warna, dan beberapa jumlah lain yang diperlukan untuk tampilan bintang yang benar.
Kami meminta properti langkah:

Saat ini, struktur kami memiliki berat 208 byte. Sejuta struktur seperti itu akan membutuhkan 250 MB - ini, seperti yang Anda tahu, terlalu banyak. Oleh karena itu, perlu dioptimalkan.
Int yang benar
Fakta bahwa ada berbagai jenis Int diceritakan dalam pelajaran pemrograman pertama. Int yang paling akrab bagi kita di Swift disebut Int8. Ini menempati 8 bit (1 byte) dan dapat menyimpan nilai dari -128 hingga 127 inklusif. Ada juga Ints lainnya:
- Int16 dalam ukuran 2 byte, kisaran nilai adalah dari -32.768 hingga 32.767;
- Int32 dalam ukuran 4 byte, rentang nilai dari -2,147.483.648 hingga 2.147.483.647;
- Int64 (atau hanya Int) berukuran 8 byte, kisaran nilai dari -9.223.372.036.854.775.808 hingga 9.222.372.036.854.775.807.
Mungkin Anda yang terlibat dalam pengembangan web dan berurusan dengan SQL sudah memikirkan hal ini. Tapi ya, pertama-tama, pilih Int yang optimal. Dalam proyek ini, bahkan sebelum saya datang dengan optimasi di pikiran saya, saya masuk ke sedikit optimasi prematur (yang, seperti yang saya katakan sebelumnya, tidak perlu melakukannya).
Mari kita lihat, misalnya, pada bidang dengan ID. Kita tahu bahwa kita akan memiliki sekitar satu juta bintang - tidak beberapa puluh ribu, tetapi tidak satu miliar. Jadi, untuk bidang seperti itu, yang terbaik adalah memilih Int32. Kemudian saya menyadari bahwa 4 byte sudah cukup untuk Float di sini. Ganda akan menempati 8, String masing-masing 24, tambahkan semuanya - ternyata 152 byte. Jika Anda ingat, MemoryLayout sebelumnya memberi tahu kami bahwa 208. Mengapa? Kita harus menggali lebih dalam.

Pertama, mari kita lihat Opsional. Jenis opsional berbeda karena jika tidak ada nilai yang ditetapkan, mereka menyimpan nihil. Ini memastikan keamanan dalam interaksi dengan objek. Tapi seperti yang Anda tahu, ukuran seperti itu tidak dikenakan biaya gratis: dengan meminta properti ukuran dari setiap jenis opsional, Anda akan melihat bahwa jenis seperti itu selalu membutuhkan satu byte lebih. Kami membayar untuk kemampuan mendaftar untuk bidang nihil.
Kami tidak ingin menghabiskan byte tambahan pada variabel. Pada saat yang sama, kami sangat menyukai ide yang terkandung dalam opsional. Apa yang akan terjadi? Mari kita coba menerapkan struktur kita.
Mari kita memilih beberapa nilai yang bisa dianggap “tidak valid” untuk bidang yang diberikan, sementara cocok untuk jenis yang dinyatakan. Untuk getHipId (Int32) dapat berupa, misalnya, nilai "-1". Ini berarti bahwa bidang kami tidak diinisialisasi. Berikut ini adalah opsional sepeda, yang tidak tanpa byte tambahan pada nol.
Jelas, dengan trik seperti itu, kami juga memiliki potensi kerentanan. Untuk melindungi diri dari kesalahan, kami akan membuat pengambil untuk bidang, yang secara mandiri akan mengelola logika baru kami dan memeriksa nilai bidang untuk validitas.

Seorang pengambil sepenuhnya abstrak dari kami kompleksitas solusi yang ditemukan.
Beralih ke StarData kami. Ganti semua jenis opsional dengan yang biasa dan lihat apa yang terlihat:

Ternyata setelah menghilangkan opsi, kami menyimpan bukan 9 byte (satu byte untuk masing-masing dari sembilan opsi), tetapi sebanyak 48. Kejutannya menyenangkan, tapi saya ingin tahu mengapa ini terjadi. Dan itu terjadi karena penyelarasan data dalam memori.
Penyelarasan data
Ingatlah bahwa sebelum Swift kami menulis di Objective-C, dan didasarkan pada C - dan situasi ini juga kembali ke C.
Dengan menempatkan struktur apa pun dalam memori, prosesor modern menempatkan elemen-elemen mereka tidak dalam aliran kontinu (bukan "bahu-membahu"), tetapi dalam beberapa kotak yang secara tidak normal ditipiskan oleh rongga. Ini adalah penyelarasan data. Ini memungkinkan Anda untuk menyederhanakan dan mempercepat akses ke elemen data yang diperlukan dalam memori.
Aturan penyelarasan data berlaku untuk setiap variabel tergantung pada jenisnya:
- variabel tipe char dapat mulai dari 1, 2, 3, 4, dll. byte, karena hanya membutuhkan satu byte dalam dirinya sendiri;
- variabel pendek membutuhkan 2 byte, sehingga dapat dimulai dari tanggal 2, 4, 6, 8, dll. satu byte (mis., dari setiap byte genap);
- variabel tipe float membutuhkan 4 byte, yang artinya dapat dimulai dengan setiap tanggal 4, 8, 12, 16, dll. satu byte (mis., setiap byte keempat);
- variabel tipe Double dan String masing-masing menempati 8 byte, sehingga mereka dapat mulai dengan 8, 16, 24, 32, dll. byte
- dll.
Objek MemoryLayout <> memiliki properti alignment yang mengembalikan aturan alignment yang sesuai untuk tipe yang ditentukan.
Bisakah kita menerapkan pengetahuan tentang aturan pelurusan untuk mengoptimalkan kode? Mari kita lihat sebuah contoh. Ada struktur Pengguna: untuk firstName dan lastName kami menggunakan String biasa, untuk middleName - String opsional (pengguna mungkin tidak memiliki nama seperti itu). Dalam ingatan, sebuah instance dari struktur seperti itu akan ditempatkan sebagai berikut:

Seperti yang Anda lihat, karena nama tengah opsional menempati 25 byte (bukan kelipatan 8 24-byte), aturan penyelarasan mengharuskan Anda untuk melewati 7 byte berikutnya dan menghabiskan 80 byte pada seluruh struktur. Di sini, tidak peduli bagaimana Anda menukar blok dengan string, tidak mungkin untuk menghitung jumlah byte yang lebih kecil.
Dan sekarang contoh pelurusan yang gagal:
Struktur BadAligned pertama menyatakan isHidden bertipe Bool (1 byte), kemudian ukuran bertipe Double (8 byte), tidak dapat diinterpretasi dengan tipe bool (1 byte) dan akhirnya umur bertipe Int (juga 8 byte). Dinyatakan dalam urutan ini, variabel kita akan ditempatkan di memori sedemikian rupa sehingga total struktur akan menempati 32 byte.Mari kita coba mengubah urutan mendeklarasikan bidang - kita akan mengaturnya dalam urutan volume yang ditempati dan melihat bagaimana gambar dalam memori berubah.
Struktur kami tidak mengambil 32 byte, tetapi 24. Hemat 25%.Kedengarannya seperti game Tetris, bukan? Untuk hal-hal tingkat rendah seperti itu, Swift berutang bahasa C kepada leluhurnya. Dengan mendeklarasikan bidang dalam struktur data besar secara acak, Anda lebih cenderung menggunakan lebih banyak memori daripada yang Anda bisa, mengingat aturan penyelarasan. Karena itu, cobalah untuk mengingatnya dan pertimbangkan ketika menulis kode - ini tidak terlalu sulit.Mari kita beralih ke StarData kita lagi. Mari kita coba mengatur bidangnya agar volume yang ditempati bertambah.
Pertama, Float dan Int32, lalu Double dan String. Bukan Tetris yang rumit!Langkah yang kami terima adalah 152 byte. Yaitu, dengan mengoptimalkan implementasi opsi dan bekerja dengan penyelarasan, kami dapat mengurangi ukuran struktur dari 208 menjadi 152 byte.Apakah kita mendekati batas kemampuan pengoptimalan kita? Mungkin ya. Namun, ada hal lain yang belum Anda dan Anda coba - sesuatu adalah urutan besarnya lebih rumit, tetapi kadang-kadang dapat memukau Anda dengan hasilnya.Akuntansi Logika Domain
Cobalah untuk fokus pada spesifik yang melekat dalam layanan Anda. Ingat contoh saya dengan catur: ide memvariasikan indikator FPS ketika tidak ada perubahan di layar hanyalah pengoptimalan dengan memperhitungkan logika domain aplikasi.Lihatlah StarData lagi. “Bottleneck” kami yang jelas adalah bidang tipe String, mereka benar-benar memakan banyak ruang. Dan di sini spesifiknya adalah sebagai berikut: selama runtime, sebagian besar baris ini tetap kosong! Hanya 146 bintang yang memiliki nama "asli", yang ditunjukkan di bidang properName. Dan gl_id adalah ID bintang menurut katalog Gliese, yang memiliki 3801 bintang, juga jauh dari satu juta. bayer_flamstedt - sebutan Flemstead - akan ditugaskan ke bintang ke-3064. Jenis spektral jenis spektral adalah 4307 mi. Ternyata untuk sebagian besar bintang, variabel string yang dimasukkan akan kosong, sementara masing-masing menempati 24 byte.Saya datang dengan jalan keluar sebagai berikut. Mari kita dapatkan array asosiatif sebagai struktur tambahan. Sebagai kunci - pengidentifikasi numerik unik tipe Int16, sebagai nilai, tergantung pada keberadaan string karakteristik - baik nilainya atau -1.Dalam StarData kami berlawanan dengan properName, gl_id, bayer_flamstedt dan spectralType kami akan menulis indeks yang sesuai dengan kunci dalam array. Jika perlu, dapatkan satu atau beberapa string karakteristik, kami akan meminta nilai dari array melalui indeks. Tidak perlu melakukan ini secara manual - lebih baik kita menerapkan pengambil aman yang nyaman:
Getter sangat penting di sini - menyembunyikan kompleksitas implementasi kita sendiri dari kita. Array dapat didaftarkan sebagai pribadi, sekarang tidak perlu tahu tentang keberadaannya.Tentu saja, solusi ini memiliki minus. Menyimpan memori tidak bisa tidak memengaruhi beban prosesor. Dengan skema ini, kami dipaksa untuk terus mengakses susunan asosiatif kami; dan dalam kebanyakan kasus - sia-sia, karena sebagian besar baris akan tetap kosong dan permintaan akan kembali "-1".Karena itu, saya harus sedikit mengubah konsep aplikasi. Diputuskan untuk memberikan informasi kepada pengguna tentang bintang hanya ketika mereka mengklik bintang ini - hanya kemudian kueri ke array asosiatif akan dieksekusi dan data yang diterima akan ditampilkan di layar.Terlepas dari abstraksi oleh pengambil, kita harus mengakui bahwa dengan memperkenalkan array asosiatif, kita masih secara signifikan memperumit kode. Ini biasanya terjadi selama optimasi. Oleh karena itu, penting untuk melakukan pengujian unit berkualitas tinggi - untuk memastikan bahwa susunan asosiatif kami tidak mengecewakan kami pada saat yang tidak terduga.Total: melangkah sekarang memberi kita 64 byte!Hanya itu semua Tidak, sekarang kita perlu memikirkan aturan penyelarasan lagi: mengatur ulang bidang tipe Int16 lebih tinggi.
Sekarang semuanya. Seperti yang Anda lihat, menggunakan sejumlah kecil metode yang pada dasarnya sederhana, kami dapat mengurangi ukuran struktur StarData dari 208 menjadi 56 byte. Satu juta bintang sekarang menempati bukan 500 Mb, tetapi 130. Empat kali lebih sedikit!Jangan lupa tentang bahaya optimisasi prematur. Jika struktur data Pengguna Anda akan digunakan untuk sekitar 20 pengguna, Anda tidak akan menang di sana sehingga masuk akal untuk melakukannya. Lebih penting lagi, nyaman bagi pengembang berikutnya setelah Anda mempertahankan kode. Tolong jangan katakan nanti "pria ini di konferensi mengatakan bahwa perintahnya harus seperti itu"! Jangan lakukan ini hanya untuk bersenang-senang. Bagi saya, hal-hal seperti itu adalah hiburan yang baik, saya tidak tahu caranya untuk Anda.Optimalisasi Kompiler Swift
Sebagian besar pemrogram akrab dengan kepedihan dari penyusunan ulang proyek yang lama (tidak tertahankan lama). Anda baru saja membuat perubahan kecil pada kode, dan sekarang duduk dan tunggu sampai build selesai.Tetapi proses build mungkin memberi tahu Anda sesuatu tentang kode Anda. Ini adalah indikator botnekov yang sangat baik, Anda hanya perlu menyesuaikannya agar berfungsi.Secara pribadi, saya meneliti kompilasi dalam Xcode. Sebagai alat, saya menggunakan perintah berikut: Perintah
ini menginstruksikan xCode untuk melacak waktu kompilasi setiap fungsi dan menulisnya ke file culprits.txt. Isi file diurutkan sepanjang jalan.
Menggunakan instrumen sederhana saya, saya bisa mengamati hal-hal menarik. Beberapa metode dapat dikompilasi selama 2 detik, sementara hanya mengandung tiga baris kode. Apa yang bisa menjadi alasannya?Sebagai contoh, hal seperti jenis keluaran kompiler. Jika Anda tidak menentukan jenis secara eksplisit, maka Swift terpaksa mendeteksinya sendiri. Operasi ini (saya harus mengatakan, non-sepele) membutuhkan waktu prosesor, oleh karena itu, dari sudut pandang kompiler, selalu lebih baik untuk menunjukkan jenisnya. Hanya dengan menulis jenis secara eksplisit, saya pernah dapat mengurangi waktu pembuatan aplikasi dari 5 menjadi 2 (!) Menit.Tapi ada satu "tetapi": kode tanpa tipe masih lebih mudah dibaca. Dan kita sudah bicara tentang prioritas. Jangan mengoptimalkan sebelumnya: pada awalnya, pembacaan kode akan lebih mahal.Opsi server
. Swift.
,
GitHub . API-, . , ARkit. : 500 , Bluemix. , .
, , :
- . . , , , ?
- , unit-. , unit-. , . Unit- , .
- . , . , : — .
- Bekerja dengan logika domain aplikasi Anda. Alat optimasi yang paling kuat adalah pekerjaan yang terampil dengan logika domain. Ketahui fitur pekerjaan, spesifikasi aplikasi Anda - cobalah untuk memperhitungkannya, cari solusi "pribadi" Anda.
- RAM vs. CPU Cobalah yang terbaik untuk menjaga keseimbangan penggunaan memori dan prosesor. Ini selalu sangat sulit, tetapi masih mungkin untuk menemukan optimum tertentu dalam setiap kasus tertentu.
Jika Anda menyukai laporan ini dari konferensi Mobius, harap dicatat bahwa Mobius 2018 Moscow akan diadakan pada 8-9 Desember , di mana juga akan ada banyak hal menarik. Sejak 1 November, harga tiket telah meningkat, jadi masuk akal untuk mengambil keputusan sekarang!