Untuk waktu yang lama saya akan menulis artikel tentang numba dan membandingkan kecepatannya dengan si. Artikel Haskell “ Lebih cepat dari C ++; lebih lambat dari PHP ”didorong untuk bertindak. Dalam komentar pada artikel ini, mereka menyebutkan perpustakaan numba dan secara ajaib dapat memperkirakan kecepatan eksekusi kode dalam python ke kecepatan dalam s. Dalam artikel ini, setelah ulasan singkat tentang numba (bagian 1), analisis situasi ini sedikit lebih rinci ( bagian 2 ).

Kerugian utama dari python dianggap kecepatannya. Python overclocking dengan berbagai keberhasilan dimulai hampir dari hari-hari pertama keberadaannya: kulit domba , psyco , dangkal unladen , parkit , theano , nuitka , pythran , cython , pypy , numba .

Sampai saat ini, tiga terakhir adalah yang paling laris. Cython
(jangan dikacaukan dengan cpython) - sangat berbeda secara semantik dari python biasa. Bahkan, ini adalah bahasa yang terpisah - hibrida dari C dan python. Adapun pypy
(implementasi alternatif dari penerjemah python menggunakan kompilasi jit) dan numba
(perpustakaan untuk kompilasi kode dalam llvm), mereka pergi dengan cara yang berbeda. pypy
awalnya menyatakan dukungan untuk semua konstruksi python. Di numba, mereka melanjutkan dari fakta bahwa itu paling sering memerlukan cpu bound - perhitungan matematis, masing-masing, mereka mengidentifikasi bagian dari bahasa yang terkait dengan perhitungan dan mulai meng-overclock, secara bertahap meningkatkan "cakupan" (misalnya, hingga saat ini tidak ada dukungan garis) , sekarang dia telah muncul). Dengan demikian, tidak seluruh program di-overclock di numba
, tetapi fungsi-fungsi terpisah , ini memungkinkan Anda untuk menggabungkan kecepatan tinggi dan kompatibilitas dengan perpustakaan yang numba
tidak (belum) mendukung. Numpy didukung (dengan batasan kecil) di pypy
dan numba
.
Kenalan saya dengan Numba dimulai pada 2015 dengan pertanyaan tentang stackoverflow tentang kecepatan multiplikasi matriks dalam python: Produk luar yang efisien dalam python
Sejak itu, banyak peristiwa telah terjadi di masing-masing perpustakaan, tetapi gambar sehubungan dengan numba
/ cython
/ pypy
tidak berubah numba
: numba
menyalip cython
melalui penggunaan instruksi prosesor asli ( cython
tidak bisa jit), dan pypy
- karena pelaksanaan byvecode byte yang lebih efisien .
Numba sangat berguna bagi saya di tempat kerja (memproses gambar hiperspektral) dan dalam pengajaran (integrasi numerik, menyelesaikan persamaan diferensial).
cara mengatur
Beberapa tahun yang lalu ada masalah dengan instalasi, sekarang semuanya diselesaikan: itu menginstal sama baiknya baik melalui pip install numba
dan melalui conda install numba
. llvm diperketat dan diinstal secara otomatis.
cara mempercepat
Untuk mempercepat suatu fungsi, Anda harus memasukkan dekorator njit sebelum mendefinisikannya:
from numba import njit @njit def f(n): s = 0. for i in range(n): s += sqrt(i) return s
Akselerasi 40 kali.
Akar diperlukan, karena jika tidak, numba akan mengenali jumlah dari perkembangan aritmatika (!) Dan menghitungnya dalam waktu yang konstan.
jit vs njit
Sebelumnya, hanya mode @jit
(bukan @njit
) yang relevan. Intinya adalah bahwa dalam mode ini Anda dapat menggunakan operasi yang tidak didukung oleh numba: numba dengan kecepatan tinggi mencapai operasi seperti pertama, kemudian melambat, dan sampai akhir pelaksanaan fungsi berlanjut pada kecepatan Python biasa, bahkan jika tidak ada lagi "terlarang" yang ditemui dalam fungsi ( yang disebut mode objek), yang jelas tidak rasional. Sekarang @jit
bertahap meninggalkan @jit
, selalu disarankan untuk menggunakan @njit (atau dalam bentuk penuh @jit(nopython=True)
): dalam mode ini, numba bersumpah dengan pengecualian di tempat-tempat seperti itu - lebih baik menulis ulang mereka agar tidak kehilangan kecepatan.
apa yang bisa mempercepat
Dalam fungsi yang di-overclock, hanya sebagian dari fungsi python dan numba yang dapat digunakan. Semua operator, fungsi dan kelas dibagi menjadi dua bagian sehubungan dengan numba: yang numba "pahami" dan yang "tidak pahami".
Ada dua daftar seperti itu dalam dokumentasi numba (dengan contoh):
Dari yang patut diperhatikan dalam daftar ini:
- numba "memahami" daftar Python dengan penambahan cepat (diamortisasi O (1)) sampai akhir yang numpy "tidak mengerti" (meskipun hanya yang homogen dari unsur-unsur dari jenis yang sama),
- array numpy yang tidak di python dasar. Mengerti juga
- tuple: mereka bisa, seperti python biasa, mengandung elemen dari tipe yang berbeda.
- kamus: numba memiliki implementasi sendiri dari kamus yang diketik. Semua kunci harus dari jenis yang sama, persis seperti nilainya. Dict python tidak dapat dilewatkan ke numba, tetapi numba
numba.typed.Dict
dapat dibuat dengan python dan ditransfer ke / dari numba (sementara di python bekerja sedikit lebih lambat daripada python). - namun baru-baru ini str dan byte, hanya karena parameter input tidak dapat dibuat (belum?).
Dia sama sekali tidak mengerti perpustakaan lain (khususnya, scipy dan panda).
Tetapi bahkan bagian bahasa yang dia mengerti sudah cukup untuk meng-overclock sebagian besar kode untuk aplikasi ilmiah yang menjadi fokus utama numba.
penting!
Dari fungsi overclock, hanya fungsi overclock, bukan overclock, yang dapat dipanggil.
(meskipun fungsi overclock dapat dipanggil dari overclocked dan tidak overclocked).
global
Dalam fungsi yang di-overclock, variabel global menjadi konstanta: nilainya tetap pada saat fungsi dikompilasi ( contoh ). => Jangan gunakan variabel global dalam fungsi overclock (kecuali untuk konstanta).
tanda tangan
Dalam nomor fungsi masing-masing, satu atau beberapa jenis argumen input dan output dipetakan, yaitu tanda tangan. Ketika fungsi pertama kali dipanggil, tanda tangan dihasilkan dan kode fungsi biner yang sesuai dikompilasi secara otomatis. Ketika diluncurkan dengan jenis argumen lain, tanda tangan baru dan binari baru akan dibuat (yang lama akan dipertahankan). Dengan demikian, "keluar ke mode" dalam hal kecepatan eksekusi untuk setiap tanda tangan terjadi, mulai dari yang kedua dengan jenis argumen ini. Begitu juga
- “Warm up the cache” dengan meluncurkan dengan array input ukuran kecil, atau
- tentukan argumen
@jit(cache=True)
untuk menyimpan kode yang dikompilasi ke disk dengan pemuatan otomatisnya selama peluncuran program berikutnya (meskipun dalam praktiknya hari ini peluncuran pertama masih sedikit lebih lambat daripada yang berikutnya, tetapi lebih cepat daripada tanpa cache=True
) .
Ada cara ketiga. Tanda tangan dapat diatur secara manual:
from numba import int16, int32 @njit(int32(int16, int16)) def f(x, y): return x + y >>> f.signatures [(int16, int16)]
Ketika Anda menjalankan fungsi dengan tanda tangan yang ditentukan dalam dekorator, jalankan pertama akan cepat: kompilasi akan terjadi pada saat python melihat definisi fungsi, dan bukan pada awal pertama. Mungkin ada beberapa tanda tangan, urutan urutannya penting.
Peringatan: metode terakhir ini tidak aman di masa mendatang. Para penulis numba memperingatkan bahwa sintaks untuk menentukan jenis dapat berubah di masa depan, @jit
/ @njit
tanpa tanda tangan adalah opsi yang lebih aman dalam hal ini.
tanda tangan mulai menunjukkan tanda tangan hanya ketika python mengetahui tentang mereka, yaitu, setelah panggilan fungsi pertama, atau jika mereka diatur secara manual.
Selain tanda tangan f.signatures
tanda tangan dapat dilihat melalui f.inspect_types()
- selain jenis parameter input, fungsi ini akan menunjukkan jenis parameter output serta jenis semua variabel lokal.
Selain jenis parameter input dan output, dimungkinkan untuk menentukan jenis variabel lokal secara manual:
from numba import int16, int32 @njit(int32(int16, int16), locals={'z': int32}) def f(x, y): z = y + 10 return x + z
int
Di numba, bilangan bulat tidak memiliki aritmatika panjang seperti pada python "sederhana", tetapi ada tipe standar dari berbagai lebar dari int8
ke int64
( ketik tabel dalam dokumentasi). Ada juga tipe int_
(dan juga float_
), yang Anda gunakan memberi numba kesempatan untuk memilih lebar bidang yang optimal (dari sudut pandangnya).
kelas
Pada umumnya ada dukungan untuk kelas (@ jitclass), tetapi sejauh ini masih eksperimental, jadi lebih baik untuk menghindari menggunakannya untuk saat ini (saat ini, dalam pengalaman saya, jauh lebih lambat dengan mereka daripada tanpa mereka).
dtypes khusus
Numba mendukung alternatif tertentu untuk kelas dari numpy - structured array, atau, dengan kata lain, dtype khusus. Mereka bekerja pada kecepatan yang sama dengan array numpy biasa, mereka sedikit lebih nyaman untuk diindeks (misalnya, a['y2']
lebih mudah dibaca daripada a[3]
). Menariknya, di numba, tidak seperti numpy, a.y2
lebih ringkas diizinkan bersama dengan sintaksis yang biasa a['y2']
. Tetapi secara umum, dukungan mereka pada numba menyisakan banyak yang harus diinginkan, dan beberapa operasi, bahkan jelas pada numpy, dengan mereka di numba dicatat cukup non-sepele.
GPU
Ia mampu mengeksekusi kode overclock pada GPU, dan berbeda dengan yang sama, misalnya, pycuda atau pytorch, tidak hanya pada nvidia, tetapi juga pada kartu amd'shnyh. Dengan ini, sejauh ini saya hanya memiliki sedikit pengetahuan. Berikut ini adalah artikel tentang Habre 2016 Perbandingan kinerja perhitungan GPU dalam Python dan C. Di sana, kecepatan yang sebanding dengan C diperoleh.
kompilasi sebelumnya
Ada mode kompilasi ( dokumentasi ) normal (yaitu, bukan jit) di numba, tetapi mode ini bukan yang utama, saya tidak memahaminya.
paralelisasi otomatis
Beberapa tugas (misalnya, mengalikan matriks dengan angka) diparalelkan secara alami. Tetapi ada tugas yang implementasinya tidak bisa diparalelkan. Dengan dekorator @njit(parallel=True)
numba menganalisis kode fungsi yang di-overclock, menemukan bagian-bagian tersebut, yang masing-masing tidak dapat diparalelkan dengan sendirinya, dan menjalankannya secara bersamaan pada inti CPU yang berbeda ( dokumentasi ). Sebelumnya, Anda hanya bisa memparalelkan fungsi secara manual menggunakan @vectorize
( dokumentasi ), yang memerlukan perubahan kode.
Dalam praktiknya, tampilannya seperti ini: tambah parallel=True
, ukur kecepatannya, jika kita beruntung dan ternyata lebih cepat - kita tinggalkan, lebih lambat - kita lepaskan. (** Pembaruan Seperti tercantum dalam komentar ke bagian kedua artikel, bendera ini memiliki banyak bug terbuka)
Rilis GIL
Fungsi yang didekorasi dengan @jit(nogil=True)
dan berjalan di utas yang berbeda dapat dijalankan secara paralel. Untuk menghindari kondisi balapan, Anda harus menggunakan sinkronisasi utas.
dokumentasi
Numbe masih kekurangan dokumentasi yang masuk akal. Memang, tapi tidak semuanya ada di dalam dirinya.
optimasi
Ada beberapa ketidakpastian ketika mengoptimalkan kode secara manual: kode unpythonic sering berjalan lebih cepat daripada pythonic.
Bagi mereka yang tertarik dengan topik ini, saya dapat merekomendasikan video kelas master numba dari konferensi 2017 yang cerdik (ada kode sumber di github). Ini benar-benar panjang dan sebagian ketinggalan jaman (misalnya, garis sudah didukung), tetapi membantu untuk mendapatkan ide umum: ada, khususnya, tentang pythonic / unpythonic, jit (parallel = True), dll.
Pada bagian kedua , kami akan mempertimbangkan penggunaan numba menggunakan kode dari artikel yang disebutkan di awal artikel.