Mengapa Raku berkinerja sangat buruk dengan array multidimensi?

10

Saya ingin tahu mengapa Raku melakukan manipulasi array multidimensi yang sangat buruk. Saya telah membuat tes cepat menginisialisasi matriks 2 dimensi dalam Python, C # dan Raku dan waktu yang berlalu sangat tinggi untuk nanti.

Untuk Raku

my @grid[4000;4000] = [[0 xx 4000] xx 4000];
# Elapsed time 42 seconds !!

Untuk Python

table= [ [ 0 for i in range(4000) ] for j in range(4000) ]
# Elapsed time 0.51 seconds

C #

int [,]matrix = new int[4000,4000];
//Just for mimic same behaviour
for(int i=0;i<4000;i++)
   for(int j=0;j<4000;j++)
       matrix[i,j] = 0;
# Elapsed time 0.096 seconds

Apakah saya salah? Sepertinya terlalu banyak perbedaan.

Javi
sumber
5
Ini hanya lambat untuk array multidimensi berbentuk (EG di mana Anda mendefinisikannya @grid[4000;4000]) kode python tidak menggunakan array berbentuk dan dari Anda mencoba yang sama di Raku Anda mendapatkan waktu yang jauh lebih baik kembali: my @grid = [[0 xx 4000] xx 4000]; Itu berarti Anda harus mengakses dengan @grid[0][0]tidak @grid[0;0]. Saya pikir ini sebagian besar karena array berbentuk masih dalam proses.
Scimon Proctor
1
Di komputer saya @grid[1000;1000] = [[0 xx 1000]xx1000]butuh 12 detik. @grid = [[0 xx 1000]xx1000]mengambil 0,6 jadi ... ya. Saya akan menghindari array berbentuk.
Scimon Proctor
5
@Simon Anda sebenarnya masih dapat menggunakan accessor [;] untuk array yang tidak berbentuk. my @grid = [[$++ xx 100] xx 100]; say @grid[0;1]; say @grid[1;1]mengembalikan masing
pengguna0721090601
Luar biasa! Itu membuat segalanya lebih mudah.
Scimon Proctor
2
Array multi-dimensi berbentuk belum menerima kebaikan optimasi belum banyak daerah Rakudo telah menerima.
Elizabeth Mattijsen

Jawaban:

13

Perbandingan langsung awal

Saya akan mulai dengan kode yang jauh lebih selaras dengan kode Python Anda daripada terjemahan Anda sendiri. Saya pikir kode Raku yang paling langsung setara dengan Python Anda adalah:

my \table = [ [ 0 for ^4000 ] for ^4000 ];
say table[3999;3999]; # 0

Kode ini menyatakan pengidentifikasi sigil-free 1 . Itu:

  • Tetes "membentuk" ( [4000;4000]dalam my @table[4000;4000]). Saya menjatuhkannya karena kode Python Anda tidak melakukannya. Membentuk memberi keuntungan tetapi memiliki implikasi kinerja. 2

  • Menggunakan ikatan bukan penugasan . Saya beralih ke pengikatan karena kode Python Anda melakukan pengikatan, bukan penugasan. (Python tidak membedakan antara keduanya.) Sementara pendekatan penugasan Raku membawa keuntungan mendasar yang layak dimiliki untuk kode umum, ia memiliki implikasi kinerja. 3


Kode ini saya mulai dengan jawaban saya masih lambat.

Pertama, kode Raku, dijalankan melalui kompilator Rakudo dari Desember 2018, sekitar 5 kali lebih lambat dari kode Python Anda, menggunakan juru bahasa Python dari Juni 2019, pada perangkat keras yang sama. 3

Kedua, baik kode Raku dan kode Python lambat, misalnya dibandingkan dengan kode C # Anda. Kita bisa melakukan yang lebih baik ...

Alternatif idiomatik yang seribu kali lebih cepat

Kode berikut layak dipertimbangkan:

my \table = [ [ 0 xx Inf ] xx Inf ];
say table[ 100_000; 100_000 ]; # 0

Meskipun kode ini sesuai dengan 10 miliar elemen array nosional daripada hanya 16 juta elemen satu dalam kode Python dan C # Anda, waktu wallclock untuk menjalankannya kurang dari setengah dari kode Python, dan hanya 5 kali lebih lambat daripada C # kode. Itu menunjukkan Rakudo menjalankan kode Raku lebih dari seribu kali lebih cepat dari kode Python yang setara, dan seratus kali lebih cepat dari kode C #.

Kode Raku tampaknya jauh lebih cepat karena tabel sedang diinisialisasi malas dengan menggunakan xx Inf. 4 Satu-satunya pekerjaan signifikan yang dilakukan untuk menjalankan saysaluran. Hal ini menyebabkan pembuatan 100.000 array dimensi pertama, dan kemudian mengisi hanya array dimensi kedua 100.000 dengan 100.000 elemen, sehingga saydapat menampilkan yang 0ditahan di elemen terakhir array itu.

Ada Lebih Dari Satu Cara Untuk Melakukannya

Satu masalah yang mendasari pertanyaan Anda adalah selalu ada lebih dari satu cara untuk melakukannya. 5 Jika Anda menemukan kinerja yang buruk untuk kode yang kecepatannya kritis, mengkodekannya secara berbeda, seperti yang telah saya lakukan, dapat membuat perbedaan dramatis. 6

(Pilihan lain yang sangat bagus adalah mengajukan pertanyaan SO ...)

Masa depan

Raku hati-hati dirancang untuk menjadi sangat optimiz mampu , yaitu mampu untuk satu hari run lebih cepat diberikan pekerjaan compiler yang cukup atas tahun-tahun mendatang , dari, katakanlah, Perl 5 atau Python 3 kaleng, secara teori, pernah menjalankan, kecuali mereka pergi melalui tanah- sebuah desain ulang dan tahun kerja kompiler yang sesuai.

Analogi yang agak baik adalah apa yang terjadi dengan kinerja Java selama 25 tahun terakhir. Rakudo / NQP / MoarVM sekitar setengah jalan melalui proses jatuh tempo yang telah ditumpuk oleh Java stack.

Catatan kaki

1 Saya bisa menulis my $table := .... Tetapi deklarasi formulir my \foo ...menghilangkan pertimbangan sigil dan memungkinkan penggunaan =daripada :=yang diperlukan dengan pengidentifikasi sigil. (Sebagai bonus, "memotong sigil" menghasilkan pengenal sigil gratis, yang biasa bagi para pembuat kode dalam banyak bahasa yang tidak menggunakan sigil yang tentu saja termasuk Python dan C #.)

2 Membentuk suatu hari dapat menghasilkan operasi array yang lebih cepat untuk beberapa kode. Sementara itu, seperti yang disebutkan dalam komentar pada pertanyaan Anda, saat ini jelas sebaliknya, secara signifikan memperlambatnya. Saya membayangkan itu sebagian besar karena setiap akses array secara naif diperiksa secara dinamis pada saat ini, perlahan-lahan semuanya turun, dan juga tidak ada upaya untuk menggunakan ukuran tetap untuk membantu mempercepat segalanya. Selain itu, ketika saya mencoba menemukan solusi cepat untuk kode Anda, saya gagal menemukan satu menggunakan array ukuran tetap karena banyak operasi pada array ukuran tetap sedang tidak diterapkan. Sekali lagi, ini diharapkan akan dilaksanakan suatu hari tetapi mungkin belum menjadi titik rasa sakit yang cukup bagi siapa pun untuk bekerja menerapkannya sampai saat ini.

3 Pada saat penulisan ini, TIO menggunakan Python 3.7.4, dari Juni 2019, dan Rakudo v2018.12, dari Desember 2018. Kinerja Rakudo saat ini membaik secara signifikan lebih cepat dari pada waktu yang ditentukan oleh penerjemah resmi Python 3, jadi saya ingin mengharapkan celah antara Rakudo terbaru dan Python terbaru, ketika Rakudo lebih lambat, secara signifikan lebih sempit daripada yang dinyatakan dalam jawaban ini. Secara khusus, pekerjaan saat ini secara signifikan meningkatkan kinerja penugasan.

4 xx standar untuk pemrosesan malas tetapi beberapa ekspresi memaksa evaluasi bersemangat karena baik semantik bahasa atau keterbatasan kompilator saat ini. Pada tahun v2018.12 Rakudo, untuk ekspresi bentuk [ [ foo xx bar ] xx baz ]tetap malas dan tidak dipaksa untuk mengevaluasi dengan bersemangat, keduanya bar dan bazharus Inf. Sebaliknya, my \table = [0 xx 100_000 for ^100_000]malas tanpa menggunakan Inf. (Kode terakhir sebenarnya menyimpan 100.000 Seqdetik di dimensi pertama daripada 100.000 Arraydetik - say WHAT table[0]menampilkan Seqdaripada Array- tetapi sebagian besar kode tidak akan dapat menemukan perbedaannya - say table[99_999;99_999]masih akan ditampilkan 0.)

5 Beberapa orang berpikir bahwa itu adalah kelemahan untuk menerima bahwa ada lebih dari satu cara untuk memikirkan dan memberi kode solusi untuk masalah yang diberikan. Pada kenyataannya itu adalah kekuatan setidaknya dalam tiga hal. Pertama, secara umum, setiap masalah non-sepele yang diberikan dapat diselesaikan oleh banyak algoritma berbeda dengan perbedaan dramatis dalam profil kinerja. Jawaban ini mencakup pendekatan yang sudah tersedia dengan Rakudo berusia satu tahun yang akan lebih dari seribu kali lebih cepat daripada Python dalam praktiknya dalam beberapa skenario. Kedua, bahasa multi-paradigma yang sengaja fleksibel dan seperti Raku memungkinkan seorang pembuat kode (atau tim pembuat kode) untuk mengekspresikan solusi yang mereka anggap elegan dan dapat dipertahankan, atau yang baru saja menyelesaikan pekerjaan, berdasarkan apa yang merekaberpikir adalah yang terbaik, bukan apa yang dipaksakan oleh bahasa Ketiga, kinerja Rakudo sebagai kompiler pengoptimal saat ini sangat bervariasi. Untungnya ia memiliki profiler 6 yang hebat , sehingga orang dapat melihat di mana bottleneck berada, dan fleksibilitas yang besar, sehingga orang dapat mencoba kode alternatif dan ini dapat menghasilkan kode yang jauh lebih cepat.

6 Ketika kinerja penting, atau jika Anda menyelidiki masalah kinerja, lihat halaman Raku doc ​​tentang kinerja ; halaman ini mencakup berbagai opsi termasuk penggunaan profiler Rakudo.

raiph
sumber