Kemudahan dan prestasi biasanya berkorelasi songsang. Jika kod itu mudah digunakan, ia kurang dioptimumkan. Jika ia dioptimumkan, ia kurang mudah. Kod yang cekap perlu mendekati butiran terperinci tentang perkara yang sebenarnya sedang dijalankan, bagaimana.
Saya menemui contoh dalam kerja berterusan kami untuk menjalankan & mengoptimumkan segmentasi selular DeepCell untuk penyelidikan kanser. Model DeepCell AI meramalkan piksel mana yang berkemungkinan besar berada dalam sel. Dari situ, kami "membanjiri" daripada piksel yang paling berkemungkinan, sehingga mencapai sempadan sel (di bawah beberapa ambang).
Sebahagian daripada proses ini melibatkan melicinkan celah kecil di dalam sel yang diramalkan, yang boleh berlaku atas pelbagai sebab tetapi tidak mungkin secara biologi. (Fikirkan lubang donat, bukan membran berliang sel.)
Algoritma pengisian lubang adalah seperti ini:
Berikut ialah contoh nombor Euler daripada artikel Wikipedia; bulatan (hanya bahagian garisan) mempunyai ciri Euler sifar manakala cakera (bulatan "diisi") mempunyai nilai 1.
Kami di sini bukan untuk bercakap tentang mentakrifkan atau mengira nombor Euler. Kita akan bercakap tentang cara laluan mudah perpustakaan untuk mengira nombor Euler agak tidak cekap.
Perkara pertama dahulu. Kami melihat masalah itu dengan melihat profil ini menggunakan Speedscope:
Ia menunjukkan ~32ms (~15%) dibelanjakan dalam regionprops. Paparan ini berat sebelah kiri, jika kita pergi ke paparan garis masa dan zum masuk, kita mendapat ini:
(Perhatikan bahawa kami melakukan ini dua kali, oleh itu ~16ms di sini dan ~16ms di tempat lain, tidak ditunjukkan.)
Ini segera disyaki: bahagian "menarik" untuk mencari objek dengan find_objects ialah sekerat pertama, 0.5ms. Ia mengembalikan senarai tupel, bukan penjana, jadi apabila ia selesai ia selesai. Jadi apa jadi dengan semua perkara lain? Kami sedang membina objek RegionProperties. Mari zum masuk pada salah satu daripadanya.
Sekerat kecil (yang kami tidak akan zum masuk) ialah panggilan __setattr__ tersuai: objek RegionProperties menyokong pengaliansi, contohnya jika anda menetapkan atribut ConvexArea ia mengubah hala ke area_convex atribut standard. Walaupun kami tidak menggunakannya, kami masih melalui penukar atribut.
Tambahan pula: kami tidak menggunakan kebanyakan sifat yang dikira dalam sifat wilayah. Kami hanya mementingkan nombor Euler:
props = regionprops(np.squeeze(label_img.astype('int')), cache=False) for prop in props: if prop.euler_number < 1:
sebaliknya, ia hanya menggunakan aspek paling asas bagi sifat rantau: kawasan imej yang dikesan oleh find_objects (kepingan imej asal).
Jadi, kami menukar kod kepada kod fill_holes untuk memintas sahaja fungsi tujuan am regionprops. Sebaliknya, kami memanggil find_objects dan menghantar subkawasan imej yang terhasil kepada fungsi euler_number (bukan kaedah pada objek RegionProperties).
Berikut ialah permintaan tarik: deepcell-imaging#358 Langkau pembinaan regionprops
Dengan melangkau objek perantaraan, kami mendapat peningkatan prestasi yang baik untuk operasi fill_holes:
Image size | Before | After | Speedup |
---|---|---|---|
260k pixels | 48ms | 40ms | 8ms (17%) |
140M pixels | 15.6s | 11.7s | 3.9s (25%) |
Untuk imej yang lebih besar, 4s ialah ~3% daripada keseluruhan masa jalan– bukan sebahagian besarnya, tetapi juga tidak terlalu lusuh.
Atas ialah kandungan terperinci Perangkap prestasi: perpustakaan umum & objek pembantu. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!