Pertanyaan Mencegah regresi kinerja di R


Apa alur kerja yang baik untuk mendeteksi regresi kinerja dalam paket R? Idealnya, saya mencari sesuatu yang menyatu dengan R CMD check yang mengingatkan saya ketika saya telah memperkenalkan regresi kinerja yang signifikan dalam kode saya.

Apa alur kerja yang baik secara umum? Bahasa lain apa yang menyediakan alat yang bagus? Apakah itu sesuatu yang dapat dibangun di atas pengujian unit, atau yang biasanya dilakukan secara terpisah?


32
2017-12-11 15:17


asal


Jawaban:


Ini adalah pertanyaan yang sangat menantang, dan salah satu yang sering saya hadapi, karena saya menukar kode yang berbeda dalam paket untuk mempercepat pekerjaan. Kadang-kadang regresi kinerja datang bersama dengan perubahan dalam algoritma atau implementasi, tetapi mungkin juga timbul karena perubahan dalam struktur data yang digunakan.

Apa alur kerja yang baik untuk mendeteksi regresi kinerja dalam paket R?

Dalam kasus saya, saya cenderung memiliki kasus penggunaan yang sangat spesifik yang saya coba untuk mempercepat, dengan set data tetap yang berbeda. Seperti yang ditulis Spacedman, penting untuk memiliki sistem komputasi tetap, tetapi itu hampir tidak mungkin: kadang-kadang komputer bersama mungkin memiliki proses lain yang memperlambat segalanya 10-20%, bahkan ketika terlihat cukup menganggur.

Langkah saya:

  1. Standarisasi platform (misalnya satu atau beberapa mesin, mesin virtual tertentu, atau mesin virtual + infrastruktur khusus, jenis EC2 contoh Amazon).
  2. Standarisasi set data yang akan digunakan untuk pengujian kecepatan.
  3. Buat skrip dan output data antara tetap (yaitu disimpan ke file .rdat) yang melibatkan transformasi data yang sangat minim. Fokus saya adalah pada beberapa jenis pemodelan, daripada manipulasi data atau transformasi. Ini berarti saya ingin memberikan blok data yang persis sama ke fungsi pemodelan. Namun, jika transformasi data adalah tujuannya, maka pastikan bahwa data pra-transformasi / manipulasi sedekat mungkin dengan standar di seluruh pengujian versi yang berbeda dari paket tersebut. (Lihat pertanyaan ini untuk contoh memoisasi, cache, dll, yang dapat digunakan untuk menstandardisasi atau mempercepat perhitungan non-fokus. Ini referensi beberapa paket oleh OP.)
  4. Ulangi tes beberapa kali.
  5. Skalakan hasil relatif terhadap tolok ukur tetap, mis. waktu untuk melakukan regresi linier, mengurutkan matriks, dll. Ini dapat memungkinkan variasi "lokal" atau sementara dalam infrastruktur, seperti mungkin disebabkan oleh I / O, sistem memori, paket yang bergantung, dll.
  6. Periksa hasil pembuatan profil secara penuh semangat mungkin (lihat pertanyaan ini untuk beberapa wawasan, juga alat referensi dari OP).

    Idealnya, saya mencari sesuatu yang terintegrasi dengan pemeriksaan R CMD yang memberi tahu saya ketika saya memperkenalkan regresi kinerja yang signifikan dalam kode saya.

    Sayangnya, saya tidak punya jawaban untuk ini.

    Apa alur kerja yang baik secara umum?

    Bagi saya, ini sangat mirip dengan pengujian kode dinamis umum: apakah output (waktu eksekusi dalam kasus ini) dapat direproduksi, optimal, dan transparan? Transparansi berasal dari pemahaman apa yang memengaruhi keseluruhan waktu. Di sinilah saran Mike Dunlavey penting, tetapi saya lebih memilih untuk melangkah lebih jauh, dengan seorang profiler garis.

    Mengenai profiler garis, lihat pertanyaan saya sebelumnya, yang mengacu pada opsi di Python dan Matlab untuk contoh lain. Sangat penting untuk memeriksa waktu jam, tetapi juga sangat penting untuk melacak alokasi memori, berapa kali baris dijalankan, dan memanggil kedalaman tumpukan.

    Bahasa lain apa yang menyediakan alat yang bagus?

    Hampir semua bahasa lain memiliki alat yang lebih baik. :) Bahasa yang ditafsirkan seperti Python dan Matlab memiliki contoh alat yang baik & mungkin akrab yang dapat diadaptasi untuk tujuan ini. Meskipun analisis dinamis sangat penting, analisis statis dapat membantu mengidentifikasi di mana mungkin ada beberapa masalah serius. Matlab memiliki penganalisis statis yang hebat yang dapat melaporkan ketika objek (misalnya vektor, matriks) tumbuh di dalam loop, misalnya. Sangat mengerikan untuk menemukan ini hanya melalui analisis dinamis - Anda sudah membuang waktu eksekusi untuk menemukan sesuatu seperti ini, dan itu tidak selalu dapat dilihat jika konteks eksekusi Anda cukup sederhana (misalnya hanya beberapa iterasi, atau objek kecil).

    Sejauh metode agnostik bahasa, Anda dapat melihat:

    1. Valgrind & cachegrind
    2. Pemantauan disk I / O, buffer kotor, dll.
    3. Pemantauan RAM (Cachegrind sangat membantu, tetapi Anda bisa memantau alokasi RAM, dan banyak detail tentang penggunaan RAM)
    4. Penggunaan beberapa core

    Apakah itu sesuatu yang dapat dibangun di atas pengujian unit, atau yang biasanya dilakukan secara terpisah?

    Ini sulit dijawab. Untuk analisis statis, ini dapat terjadi sebelum pengujian unit. Untuk analisis dinamis, seseorang mungkin ingin menambahkan lebih banyak tes. Anggap saja sebagai desain sekuensial (yaitu dari kerangka desain eksperimental): jika biaya pelaksanaan tampak, dalam beberapa tunjangan statistik untuk variasi, sama, maka tidak diperlukan pengujian lebih lanjut. Namun, jika metode B tampaknya memiliki biaya eksekusi rata-rata lebih besar daripada metode A, maka seseorang harus melakukan tes yang lebih intensif.


Pembaruan 1: Jika saya begitu berani, ada pertanyaan lain yang saya sarankan, yaitu: "Apa beberapa gotchas dalam membandingkan waktu eksekusi dua versi paket?" Ini analog dengan mengasumsikan bahwa dua program yang mengimplementasikan algoritma yang sama harus memiliki objek perantara yang sama. Itu tidak sepenuhnya benar (lihat pertanyaan ini - bukan berarti saya mempromosikan pertanyaan saya sendiri, di sini - itu hanya kerja keras untuk membuat semuanya menjadi lebih baik dan lebih cepat ... mengarah ke beberapa pertanyaan SO tentang topik ini :)). Dengan cara yang sama, dua eksekusi kode yang sama dapat berbeda dalam waktu yang dikonsumsi karena faktor-faktor selain dari implementasi.

Jadi, beberapa getchas yang dapat terjadi, baik dalam bahasa yang sama atau lintas bahasa, dalam instance eksekusi yang sama atau di seluruh instance "identik", yang dapat memengaruhi waktu proses:

  1. Pengumpulan sampah - penerapan atau bahasa yang berbeda dapat memukul pengumpulan sampah dalam situasi yang berbeda. Ini dapat membuat dua eksekusi tampak berbeda, meskipun itu bisa sangat bergantung pada konteks, parameter, kumpulan data, dll. Eksekusi GC-obsesif akan terlihat lebih lambat.
  2. Cacheing pada level disk, motherboard (misalnya L1, L2, L3 cache), atau level lain (mis. Memoization). Seringkali, eksekusi pertama akan membayar penalti.
  3. Timbangan tegangan dinamis - Yang ini menyebalkan. Ketika ada masalah, ini mungkin salah satu yang paling sulit untuk ditemukan, karena dapat pergi dengan cepat. Sepertinya cache, tetapi sebenarnya tidak.
  4. Setiap manajer prioritas pekerjaan yang tidak Anda ketahui.
  5. Salah satu metode menggunakan beberapa core atau melakukan beberapa hal cerdas tentang bagaimana kerja dipaketkan di antara core atau CPU. Misalnya, proses yang dikunci ke inti dapat berguna dalam beberapa skenario. Satu eksekusi dari paket R mungkin lebih beruntung dalam hal ini, paket lain mungkin sangat pintar ...
  6. Variabel yang tidak digunakan, transfer data berlebihan, cache kotor, penyangga tidak mengalir, ... daftar terus berjalan.

Hasil utamanya adalah: Idealnya, bagaimana seharusnya kita menguji perbedaan dalam nilai yang diharapkan, tunduk pada keacakan yang diciptakan karena efek pesanan? Cukup sederhana: kembali ke desain eksperimental. :)

Ketika perbedaan empiris dalam waktu pelaksanaan berbeda dari perbedaan "yang diharapkan", sangat bagus untuk mengaktifkan sistem tambahan dan pemantauan pelaksanaan sehingga kita tidak harus menjalankan kembali eksperimen sampai kita biru di wajah.


17
2017-12-31 18:41



Satu-satunya cara untuk melakukan apa pun di sini adalah membuat beberapa asumsi. Jadi mari kita asumsikan mesin yang tidak berubah, atau membutuhkan 'kalibrasi ulang'.

Kemudian gunakan kerangka kerja sama uji, dan perlakukan 'harus dilakukan dalam satuan waktu X' sebagai kriteria pengujian yang harus dipenuhi. Dengan kata lain, lakukan sesuatu seperti

 stopifnot( timingOf( someExpression ) < savedValue plus fudge)

jadi kami harus mengaitkan timing sebelumnya dengan ekspresi yang diberikan. Perbandingan pengujian kesetaraan dari salah satu dari tiga paket pengujian unit yang ada dapat digunakan juga.

Tidak ada yang tidak bisa ditangani Hadley jadi saya pikir kita hampir dapat mengharapkan paket baru timr setelah istirahat akademis panjang berikutnya :). Tentu saja, ini harus menjadi opsional karena pada "tidak diketahui" mesin (berpikir: CRAN menguji paket) kami tidak memiliki titik referensi, atau faktor fudge harus "pergi ke 11" untuk secara otomatis menerima pada mesin baru .


10
2017-12-11 16:43



Perubahan baru-baru ini diumumkan pada pakan R-devel bisa memberikan ukuran kasar untuk ini.

PERUBAHAN DI UTILITAS R-devel

‘Pemeriksaan R CMD’ dapat secara opsional melaporkan waktu di berbagai bagian pemeriksaan: ini dikendalikan oleh variabel lingkungan yang didokumentasikan dalam ‘Ekstensi Tulis R’.

Lihat http://developer.r-project.org/blosxom.cgi/R-devel/2011/12/13#n2011-12-13

Waktu keseluruhan yang dihabiskan untuk menjalankan tes dapat diperiksa dan dibandingkan dengan nilai sebelumnya. Tentu saja, menambahkan tes baru akan meningkatkan waktu, tetapi regresi kinerja yang dramatis masih bisa dilihat, meskipun secara manual.

Ini tidak sebaik butir waktu sebagai dukungan waktu dalam masing-masing suite uji, tetapi juga tidak bergantung pada satu rangkaian uji spesifik.


4
2017-12-13 20:16