Bekerja melalui prinsip tanggung jawab tunggal (SRP) dengan Python saat panggilan mahal

12

Beberapa poin dasar:

  • Panggilan metode Python "mahal" karena sifatnya yang ditafsirkan . Secara teori, jika kode Anda cukup sederhana, memecah kode Python memiliki dampak negatif selain keterbacaan dan penggunaan kembali ( yang merupakan keuntungan besar bagi pengembang, tidak terlalu banyak bagi pengguna ).
  • Prinsip tanggung jawab tunggal (SRP) membuat kode dapat dibaca, lebih mudah untuk diuji dan dipelihara.
  • Proyek ini memiliki jenis latar belakang khusus di mana kami ingin kode yang dapat dibaca, tes, dan kinerja waktu.

Sebagai contoh, kode seperti ini yang memanggil beberapa metode (x4) lebih lambat dari yang berikut yang hanya satu.

from operator import add

class Vector:
    def __init__(self,list_of_3):
        self.coordinates = list_of_3

    def move(self,movement):
        self.coordinates = list( map(add, self.coordinates, movement))
        return self.coordinates

    def revert(self):
        self.coordinates = self.coordinates[::-1]
        return self.coordinates

    def get_coordinates(self):
        return self.coordinates

## Operation with one vector
vec3 = Vector([1,2,3])
vec3.move([1,1,1])
vec3.revert()
vec3.get_coordinates()

Dibandingkan dengan ini:

from operator import add

def move_and_revert_and_return(vector,movement):
    return list( map(add, vector, movement) )[::-1]

move_and_revert_and_return([1,2,3],[1,1,1])

Jika saya memparalelkan sesuatu seperti itu, cukup objektif saya kehilangan kinerja. Pikiran itu hanyalah sebuah contoh; proyek saya memiliki beberapa rutin mini dengan matematika seperti itu - Meskipun jauh lebih mudah untuk dikerjakan, profiler kami tidak menyukainya.


Bagaimana dan di mana kita merangkul SRP tanpa mengorbankan kinerja Python, karena implementasi inherennya berdampak langsung?

Apakah ada solusinya, seperti semacam pra-prosesor yang membuat segalanya sesuai untuk dirilis?

Atau apakah Python buruk dalam menangani kerusakan kode sama sekali?

lucasgcb
sumber
19
Untuk apa nilainya, dua contoh kode Anda tidak berbeda dalam jumlah tanggung jawab. SRP bukan latihan penghitungan metode.
Robert Harvey
2
@RobertHarvey Anda benar, maaf untuk contoh yang buruk dan saya akan mengedit yang lebih baik ketika saya punya waktu. Dalam kedua kasus tersebut, keterbacaan dan pemeliharaan dapat diderita dan akhirnya SRP rusak dalam basis kode saat kami mengurangi kelas dan metode mereka.
lucasgcb
4
perhatikan bahwa panggilan fungsi mahal dalam bahasa apa pun , meskipun kompiler AOT memiliki kemewahan inlining
Eevee
6
Gunakan implementasi python JITted seperti PyPy. Sebagian besar harus memperbaiki masalah ini.
Bakuriu

Jawaban:

17

Python miskin dalam menangani kerusakan kode sama sekali?

Sayangnya ya, Python lambat dan ada banyak anekdot tentang orang yang secara drastis meningkatkan kinerja dengan menyelaraskan fungsi dan membuat kode mereka jelek.

Ada pekerjaan sekitar, Cython, yang merupakan versi kompilasi dari Python dan jauh lebih cepat.

--Edit Saya hanya ingin membahas beberapa komentar dan jawaban lainnya. Meskipun daya dorong mereka mungkin bukan python spesifik. tetapi optimasi yang lebih umum.

  1. Jangan mengoptimalkan sampai Anda memiliki masalah dan kemudian mencari kemacetan

    Nasihat umumnya bagus. Tetapi asumsinya adalah bahwa kode 'normal' biasanya performant. Ini tidak selalu terjadi. Bahasa dan kerangka kerja masing-masing memiliki keunikan masing-masing. Dalam hal ini panggilan fungsi.

  2. Hanya beberapa milidetik, hal lain akan lebih lambat

    Jika Anda menjalankan kode pada komputer desktop yang kuat, Anda mungkin tidak peduli selama kode pengguna tunggal Anda dieksekusi dalam beberapa detik.

    Tetapi kode bisnis cenderung berjalan untuk banyak pengguna dan membutuhkan lebih dari satu mesin untuk mendukung beban. Jika kode Anda berjalan dua kali lebih cepat itu berarti Anda dapat memiliki dua kali jumlah pengguna atau setengah dari jumlah mesin.

    Jika Anda memiliki mesin dan pusat data, maka umumnya Anda memiliki sejumlah besar daya CPU. Jika kode Anda berjalan agak lambat, Anda dapat menyerapnya, setidaknya sampai Anda perlu membeli mesin kedua.

    Pada hari-hari ini komputasi awan di mana Anda hanya menggunakan daya komputasi yang Anda butuhkan dan tidak lebih, ada biaya langsung untuk kode non-performant.

    Meningkatkan kinerja dapat secara drastis memotong biaya utama untuk bisnis berbasis cloud dan kinerja harus menjadi yang terdepan dan utama.

Ewan
sumber
1
Sementara Jawaban Robert membantu mencakup beberapa pangkalan untuk kesalahpahaman potensial di balik melakukan optimasi semacam ini (yang sesuai dengan pertanyaan ini ), saya merasa ini menjawab situasi sedikit lebih langsung dan sejalan dengan konteks Python.
lucasgcb
2
maaf agak pendek. Saya tidak punya waktu untuk menulis lebih banyak. Tapi saya pikir Robert salah dalam hal ini. Saran terbaik dengan python tampaknya adalah profil saat Anda kode. Jangan menganggap itu akan menjadi pemain dan hanya mengoptimalkan jika Anda menemukan masalah
Ewan
2
@ Ewan: Anda tidak harus menulis seluruh program terlebih dahulu untuk mengikuti saran saya. Satu atau dua metode lebih dari cukup untuk mendapatkan profil yang memadai.
Robert Harvey
1
Anda juga dapat mencoba pypy, yang merupakan python JITted
Eevee
2
@ Ewan Jika Anda benar-benar khawatir tentang kinerja overhead panggilan fungsi, apa pun yang Anda lakukan mungkin tidak cocok untuk python. Tetapi kemudian saya benar-benar tidak dapat memikirkan banyak contoh di sana. Sebagian besar kode bisnis terbatas IO dan hal-hal berat CPU biasanya ditangani dengan memanggil ke perpustakaan asli (numpy, tensorflow, dan sebagainya).
Voo
50

Banyak masalah kinerja potensial yang sebenarnya tidak menjadi masalah dalam praktik. Masalah yang Anda ajukan mungkin salah satunya. Dalam bahasa sehari-hari, kami menyebutnya mengkhawatirkan masalah-masalah itu tanpa bukti bahwa itu adalah masalah aktual pengoptimalan dini.

Jika Anda menulis front-end untuk layanan web, kinerja Anda tidak akan terpengaruh secara signifikan oleh panggilan fungsi, karena biaya pengiriman data melalui jaringan jauh melebihi waktu yang diperlukan untuk melakukan panggilan metode.

Jika Anda menulis loop ketat yang menyegarkan layar video enam puluh kali per detik, mungkin itu masalah. Tetapi pada saat itu, saya mengklaim Anda memiliki masalah yang lebih besar jika Anda mencoba menggunakan Python untuk melakukan itu, pekerjaan yang Python mungkin tidak cocok.

Seperti biasa, cara Anda mengetahuinya adalah dengan mengukur. Jalankan profiler kinerja atau timer di atas kode Anda. Lihat apakah itu masalah nyata dalam praktik.


Prinsip Tanggung Jawab Tunggal bukan hukum atau mandat; itu adalah pedoman atau prinsip. Desain perangkat lunak selalu tentang pertukaran; tidak ada yang absolut. Ini tidak biasa untuk menukar keterbacaan dan / atau pemeliharaan untuk kecepatan, jadi Anda mungkin harus mengorbankan SRP di atas altar kinerja. Tapi jangan lakukan tradeoff itu kecuali Anda tahu Anda memiliki masalah kinerja.

Robert Harvey
sumber
3
Saya pikir ini benar, sampai kami menemukan komputasi awan. Sekarang salah satu dari dua fungsi tersebut secara efektif berharga 4 kali lipat dari yang lainnya
Ewan
2
@ Ewan 4 kali mungkin tidak masalah sampai Anda mengukurnya menjadi cukup signifikan untuk diperhatikan. Jika Foo membutuhkan 1 ms dan Bar mengambil 4 ms itu tidak baik. Sampai Anda menyadari bahwa mentransmisikan data melalui jaringan membutuhkan 200 ms. Pada saat itu, Bar menjadi lebih lambat tidak masalah. (Hanya satu contoh yang mungkin tentang menjadi X kali lebih lambat tidak membuat perbedaan yang nyata atau berdampak, tidak dimaksudkan untuk menjadi sangat realistis.)
Becuzz
8
@ Ewan Jika pengurangan tagihan menghemat $ 15 / bulan tetapi akan membutuhkan kontraktor $ 125 / jam 4 jam untuk memperbaikinya dan mengujinya, saya bisa dengan mudah membenarkan bahwa tidak sepadan dengan waktu bisnis untuk melakukan (atau setidaknya tidak melakukannya dengan benar sekarang jika waktu ke pasar sangat penting, dll.). Selalu ada pengorbanan. Dan apa yang masuk akal dalam satu keadaan mungkin tidak di yang lain.
Becuzz
3
tagihan AWS Anda memang sangat rendah
Ewan
6
@Ewan AWS berputar ke langit-langit dengan cara batch (standar adalah 100 ms). Yang berarti optimasi semacam ini hanya akan menyelamatkan Anda jika secara konsisten menghindari mendorong Anda ke bagian selanjutnya.
Delioth
2

Pertama, beberapa klarifikasi: Python adalah bahasa. Ada beberapa penerjemah berbeda yang dapat mengeksekusi kode yang ditulis dalam bahasa Python. Implementasi referensi (CPython) biasanya adalah apa yang sedang direferensikan ketika seseorang berbicara tentang "Python" seolah-olah itu adalah implementasi, tetapi penting untuk lebih tepat ketika berbicara tentang karakteristik kinerja, karena mereka dapat berbeda secara liar antara implementasi.

Bagaimana dan di mana kita merangkul SRP tanpa mengorbankan kinerja Python, karena implementasi inherennya berdampak langsung?

Kasus 1.) Jika Anda memiliki kode Python murni (<= Python Language versi 3.5, 3.6 memiliki "dukungan level beta") yang hanya bergantung pada modul Python murni, Anda dapat merangkul SRP di mana-mana dan menggunakan PyPy untuk menjalankannya. PyPy ( https://morepypy.blogspot.com/2019/03/pypy-v71-released-now-uses-utf-8.html ) adalah juru bahasa Python yang memiliki Just in Time Compiler (JIT) dan dapat menghapus fungsi panggilan overhead selama memiliki waktu yang cukup untuk "pemanasan" dengan menelusuri kode yang dieksekusi (beberapa detik IIRC). **

Jika Anda dibatasi untuk menggunakan juru bahasa CPython, Anda dapat mengekstrak fungsi yang lambat menjadi ekstensi yang ditulis dalam C, yang akan dikompilasi sebelumnya dan tidak menderita overhead juru bahasa apa pun. Anda masih dapat menggunakan SRP di mana-mana, tetapi kode Anda akan dibagi antara Python dan C. Apakah ini lebih baik atau lebih buruk untuk pemeliharaan daripada meninggalkan SRP secara selektif tetapi hanya berpegang pada kode Python tergantung pada tim Anda, tetapi jika Anda memiliki bagian penting kinerja Anda kode, itu pasti akan lebih cepat daripada bahkan kode Python murni paling dioptimalkan ditafsirkan oleh CPython. Banyak perpustakaan matematika tercepat Python menggunakan metode ini (numpy and scipy IIRC). Yang merupakan segmen yang bagus ke Kasus 2 ...

Kasus 2.) Jika Anda memiliki kode Python yang menggunakan ekstensi C (atau bergantung pada perpustakaan yang menggunakan ekstensi C), PyPy mungkin berguna atau mungkin tidak berguna tergantung pada bagaimana mereka ditulis. Lihat http://doc.pypy.org/en/latest/extending.html untuk detailnya, tetapi ringkasannya adalah bahwa CFFI memiliki overhead minimal sementara CTypes lebih lambat (menggunakannya dengan PyPy mungkin bahkan lebih lambat daripada CPython)

Cython ( https://cython.org/ ) adalah pilihan lain yang saya tidak punya banyak pengalaman dengannya. Saya menyebutkannya demi kelengkapan sehingga jawaban saya bisa "berdiri sendiri", tetapi jangan mengklaim keahlian apa pun. Dari penggunaan terbatas saya, rasanya saya harus bekerja lebih keras untuk mendapatkan peningkatan kecepatan yang sama saya bisa mendapatkan "gratis" dengan PyPy, dan jika saya membutuhkan sesuatu yang lebih baik daripada PyPy, itu sama mudahnya untuk menulis ekstensi C saya sendiri ( yang memiliki manfaat jika saya menggunakan kembali kode di tempat lain atau mengekstrak bagian dari itu ke perpustakaan, semua kode saya masih dapat berjalan di bawah Penerjemah Python dan tidak diharuskan untuk dijalankan oleh Cython).

Saya takut "terkunci ke dalam" Cython, sedangkan kode apa pun yang ditulis untuk PyPy dapat berjalan di bawah CPython juga.

** Beberapa catatan tambahan tentang PyPy dalam Produksi

Berhati-hatilah dalam membuat pilihan yang memiliki efek praktis "mengunci Anda" ke PyPy dalam basis kode besar. Karena beberapa perpustakaan pihak ketiga (sangat populer dan berguna) tidak bermain bagus karena alasan yang disebutkan sebelumnya, itu dapat menyebabkan keputusan yang sangat sulit nanti jika Anda menyadari bahwa Anda memerlukan salah satu perpustakaan tersebut. Pengalaman saya terutama dalam menggunakan PyPy untuk mempercepat beberapa (tetapi tidak semua) layanan mikro yang sensitif terhadap kinerja di lingkungan perusahaan di mana ia menambahkan kompleksitas yang dapat diabaikan pada lingkungan produksi kami (kami sudah memiliki banyak bahasa yang digunakan, beberapa dengan versi utama yang berbeda seperti 2.7 vs 3.5 menjalankan anyways).

Saya telah menemukan menggunakan PyPy dan CPython secara teratur memaksa saya untuk menulis kode yang hanya mengandalkan jaminan yang dibuat oleh spesifikasi bahasa itu sendiri, dan bukan pada detail implementasi yang dapat berubah sewaktu-waktu. Anda mungkin menganggap memikirkan perincian seperti itu sebagai beban tambahan, tetapi saya menganggapnya berharga dalam pengembangan profesional saya, dan saya pikir itu "sehat" untuk ekosistem Python secara keseluruhan.

Steven Jackson
sumber
Ya! Saya telah mempertimbangkan fokus pada ekstensi C untuk kasus ini alih-alih meninggalkan prinsip dan menulis kode liar, jawaban lain memberi saya kesan itu akan lambat terlepas kecuali saya bertukar dari penerjemah referensi - Untuk menjernihkannya, OOP masih akan menjadi pendekatan yang masuk akal menurut Anda?
lucasgcb
1
dengan kasus 1 (paragraf ke-2) apakah Anda tidak mendapatkan fungsi panggilan yang sama dengan head head , meskipun fungsinya sendiri terpenuhi?
Ewan
CPython adalah satu-satunya penerjemah yang secara umum dianggap serius. PyPy memang menarik , tetapi tentu saja tidak melihat adopsi yang meluas. Selain itu, perilakunya berbeda dari CPython, dan tidak berfungsi dengan beberapa paket penting, misalnya scipy. Beberapa pengembang waras akan merekomendasikan PyPy untuk produksi. Dengan demikian, perbedaan antara bahasa dan implementasinya tidak penting dalam praktiknya.
jpmc26
Saya pikir Anda memukul paku di kepala sekalipun. Tidak ada alasan Anda tidak bisa memiliki juru bahasa yang lebih baik, atau seorang penyusun. Ini bukan intrinsik untuk python sebagai bahasa. Anda hanya terjebak dengan kenyataan praktis
Ewan
@ jpmc26 Saya telah menggunakan PyPy dalam produksi, dan merekomendasikan untuk mempertimbangkan melakukannya kepada pengembang berpengalaman lainnya. Ini bagus untuk layanan microser menggunakan falconframework.org untuk API istirahat ringan (sebagai salah satu contoh). Perilaku berbeda karena pengembang bergantung pada detail implementasi yang BUKAN jaminan bahasa bukan alasan untuk tidak menggunakan PyPy. Itu alasan untuk menulis ulang kode Anda. Kode yang sama dapat tetap rusak jika CPython membuat perubahan pada implementasinya (yang bebas untuk melakukannya selama masih sesuai dengan spesifikasi bahasa).
Steven Jackson