Apakah templat “metaprogramming” di Jawa adalah ide yang bagus?

29

Ada file sumber dalam proyek yang agak besar dengan beberapa fungsi yang sangat sensitif terhadap kinerja (disebut jutaan kali per detik). Bahkan, pengelola sebelumnya memutuskan untuk menulis 12 salinan fungsi yang masing-masing sangat sedikit berbeda, untuk menghemat waktu yang akan dihabiskan memeriksa persyaratan dalam satu fungsi.

Sayangnya, ini berarti kode tersebut adalah PITA untuk dipelihara. Saya ingin menghapus semua kode duplikat dan menulis hanya satu templat. Namun, bahasanya, Java, tidak mendukung templat, dan saya tidak yakin obat generik cocok untuk ini.

Rencana saya saat ini adalah menulis file yang menghasilkan 12 salinan fungsi (sebuah expander template sekali pakai saja, secara praktis). Saya tentu saja akan memberikan penjelasan berlebihan mengapa file harus dibuat secara programatis.

Kekhawatiran saya adalah bahwa ini akan menyebabkan kebingungan pengelola di masa depan, dan mungkin memperkenalkan bug yang tidak menyenangkan jika mereka lupa membuat ulang file setelah memodifikasinya, atau (bahkan lebih buruk) jika mereka memodifikasi file yang dihasilkan secara program. Sayangnya, singkat menulis ulang semuanya dalam C ++, saya tidak melihat cara untuk memperbaikinya.

Apakah manfaat dari pendekatan ini lebih besar daripada kerugiannya? Haruskah saya sebaliknya:

  • Ambil pukulan kinerja dan gunakan fungsi tunggal yang dapat dipelihara.
  • Tambahkan penjelasan mengapa fungsi tersebut harus digandakan 12 kali, dan anggun ambil beban pemeliharaan.
  • Coba gunakan obat generik sebagai templat (mungkin tidak berfungsi seperti itu).
  • Berteriaklah pada pengelola lama untuk membuat kode sehingga kinerja bergantung pada satu fungsi.
  • Metode lain untuk mempertahankan kinerja dan rawatan?

PS Karena desain proyek yang buruk, membuat profil fungsi agak rumit ... namun, mantan pengelola telah meyakinkan saya bahwa kinerja yang dicapai tidak dapat diterima. Saya berasumsi dengan ini yang ia maksudkan lebih dari 5%, meskipun itu adalah tebakan lengkap dari saya.


Mungkin saya harus menguraikan sedikit. 12 salinan melakukan tugas yang sangat mirip, tetapi memiliki perbedaan kecil. Perbedaannya ada di berbagai tempat di seluruh fungsi, jadi sayangnya ada banyak, banyak, pernyataan bersyarat. Ada secara efektif 6 "mode" operasi, dan 2 "paradigma" operasi (kata-kata dibuat sendiri). Untuk menggunakan fungsi, seseorang menentukan "mode" dan "paradigma" operasi. Ini tidak pernah dinamis; setiap potongan kode menggunakan tepat satu mode dan paradigma. Semua 12 pasangan mode-paradigma digunakan di suatu tempat dalam aplikasi. Fungsi-fungsi tersebut dengan tepat dinamakan func1 to func12, dengan bilangan genap yang merepresentasikan paradigma kedua dan angka ganjil mewakili paradigma pertama.

Saya sadar bahwa ini adalah desain terburuk yang pernah ada jika perawatan adalah tujuannya. Tapi sepertinya "cukup cepat", dan kode ini tidak memerlukan perubahan untuk sementara waktu ... Juga perlu dicatat bahwa fungsi aslinya belum dihapus (meskipun kode mati sejauh yang saya tahu) , jadi refactoring akan menjadi sederhana.

Fengyang Wang
sumber
Jika hit kinerja cukup serius untuk menjamin 12 versi fungsi, maka Anda mungkin terjebak dengannya. Jika Anda refactor ke fungsi tunggal (atau menggunakan obat generik), akankah kinerja yang terjadi cukup buruk sehingga Anda akan kehilangan pelanggan dan bisnis?
FrustratedWithFormsDesigner
12
Maka saya pikir Anda perlu melakukan tes Anda sendiri (saya tahu Anda mengatakan profiling itu rumit, tetapi Anda masih bisa melakukan tes kinerja "kotak hitam" kasar dari sistem secara keseluruhan, kan?) Untuk melihat seberapa besar perbedaan di sana aku s. Dan jika itu terlihat, maka saya pikir Anda mungkin akan terjebak dengan pembuat kode.
FrustratedWithFormsDesigner
1
Ini mungkin terdengar seperti manfaat dari polimorfisme parametrik (atau bahkan mungkin obat generik), daripada memanggil setiap fungsi dengan nama yang berbeda.
Robert Harvey
2
Memberi nama fungsi func1 ... func12 tampak gila. Setidaknya beri nama mereka mode1Par2 dll ... atau mungkin myFuncM3P2.
user949300
2
"Jika mereka memodifikasi file yang dihasilkan secara program" ... buat file hanya ketika membangun dari " Makefile" (atau sistem apa pun yang Anda gunakan) dan hapus kompilasi setelah selesai selesai . Dengan cara ini mereka tidak memiliki kesempatan untuk memodifikasi file sumber yang salah.
Bakuriu

Jawaban:

23

Ini adalah situasi yang sangat buruk, Anda perlu refactor ini secepatnya - ini adalah utang teknis di itu terburuk - Anda bahkan tidak tahu betapa pentingnya kode benar-benar adalah - hanya berspekulasi bahwa itu penting.

Adapun solusi ASAP:
Sesuatu yang dapat dilakukan adalah menambahkan langkah kompilasi kustom. Jika Anda menggunakan Maven yang sebenarnya cukup sederhana untuk dilakukan, sistem build otomatis lainnya juga mungkin bisa mengatasinya. Tulis file dengan ekstensi berbeda dari .java dan tambahkan langkah kustom yang mencari sumber Anda untuk file seperti itu dan meregenerasi .java yang sebenarnya. Anda mungkin juga ingin menambahkan sangkalan besar pada file yang dibuat secara otomatis yang menjelaskan untuk tidak memodifikasinya.

Pro vs menggunakan file yang dibuat sekali saja: Pengembang Anda tidak akan mendapatkan perubahannya agar .java berfungsi. Jika mereka benar-benar menjalankan kode pada mesin mereka sebelum melakukan mereka akan menemukan bahwa perubahan mereka tidak berpengaruh (hah). Dan mungkin mereka akan membaca disclaimer. Anda benar-benar benar karena tidak mempercayai rekan setim dan masa depan Anda dengan mengingat bahwa file khusus ini harus diubah dengan cara yang berbeda. Ini juga memungkinkan pengujian otomatis, karena JUnit akan mengkompilasi program Anda sebelum menjalankan tes (dan membuat ulang file juga)

EDIT

Menilai dari komentar jawabannya muncul seolah-olah ini adalah cara untuk membuat pekerjaan ini tanpa batas dan mungkin OK untuk digunakan ke bagian penting kinerja lainnya dari proyek Anda.

Sederhananya: tidak.

Beban ekstra untuk menciptakan bahasa mini Anda sendiri, menulis generator kode untuk itu dan memeliharanya, belum lagi mengajarkannya kepada pengelola masa depan adalah neraka dalam jangka panjang. Di atas hanya memungkinkan cara yang lebih aman untuk menangani masalah saat Anda bekerja pada solusi jangka panjang . Apa yang akan dilakukan adalah di atas saya.

Biasa saja
sumber
19
Maaf, tapi saya harus tidak setuju. Anda tidak cukup tahu tentang kode OP untuk membuat keputusan ini untuknya. Bagi saya, menghasilkan kode seperti ini mengandung lebih banyak utang teknis daripada solusi aslinya.
Robert Harvey
6
Komentar Robert tidak bisa dilebih-lebihkan. Setiap kali Anda memiliki "penafian yang menjelaskan untuk tidak mengubah file" yang merupakan "utang teknis" yang setara dengan toko cek-pencairan yang dijalankan oleh bandar ilegal bernama "Shark".
corsiKa
8
File yang dibuat secara otomatis tentu saja tidak termasuk dalam repositori sumber (itu bukan sumber, setelah semua, itu kode yang dikompilasi) dan itu harus dihapus oleh ant cleanatau apa pun yang setara dengan Anda. Tidak perlu meletakkan disclaimer di file yang bahkan tidak ada!
Jörg W Mittag
2
@RobertHarvey Saya tidak membuat keputusan untuk OP. OP bertanya apakah ide yang baik untuk memiliki template seperti itu dalam cara dia dan saya mengusulkan cara (lebih baik) untuk mempertahankannya. Ini bukan solusi yang ideal dan, pada kenyataannya, seperti yang telah saya nyatakan dalam kalimat pertama, seluruh situasi buruk, dan cara yang jauh lebih baik untuk maju adalah menghilangkan masalah, bukan membuat solusi yang goyah untuk itu. Itu termasuk menilai dengan benar seberapa kritis kode ini, seberapa buruk kinerja yang terjadi dan apakah ada cara untuk membuatnya bekerja tanpa menulis banyak kode buruk.
Ordous
2
@corsiKa Saya tidak pernah mengatakan ini adalah solusi abadi. Ini akan menjadi hutang sama seperti situasi aslinya. Satu-satunya hal yang dilakukannya adalah mengurangi volatilitas hingga solusi sistematis ditemukan. Yang kemungkinan akan mencakup pindah sepenuhnya ke platform / kerangka / bahasa baru karena jika Anda memukul masalah seperti ini di Jawa - Anda melakukan sesuatu yang sangat kompleks atau sangat salah.
Biasa
16

Apakah pemeliharaannya nyata, atau hanya mengganggu Anda? Jika itu hanya mengganggu Anda, biarkan saja.

Apakah masalah kinerja itu nyata, atau apakah pengembang sebelumnya hanya berpikiran seperti itu? Masalah kinerja, lebih sering daripada tidak, tidak sesuai dengan yang diduga, bahkan ketika profiler digunakan. (Saya menggunakan teknik ini untuk menemukan mereka dengan andal.)

Jadi ada kemungkinan bahwa Anda memiliki solusi jelek untuk non-masalah, tetapi juga bisa menjadi solusi jelek untuk masalah nyata. Jika ragu, tinggalkan saja.

Mike Dunlavey
sumber
10

Benar-benar memastikan daripada di bawah nyata, produksi, kondisi (konsumsi memori realistis, yang memicu Pengumpulan Sampah secara realistis, dll), semua metode terpisah tersebut benar-benar membuat perbedaan kinerja (bukan hanya memiliki satu metode). Ini akan memakan waktu beberapa hari dari waktu Anda, tetapi dapat menghemat berminggu-minggu dengan menyederhanakan kode tempat Anda bekerja mulai sekarang.

Juga, jika Anda melakukan menemukan bahwa Anda membutuhkan semua fungsi, Anda mungkin ingin menggunakan Javassist untuk menghasilkan kode programatik. Seperti yang telah ditunjukkan oleh orang lain, Anda dapat mengotomatiskannya dengan Maven .

Shivan Dragon
sumber
2
Juga, meskipun ternyata masalah kinerja itu nyata, latihan mempelajarinya akan membantu Anda untuk lebih mengetahui apa yang mungkin terjadi dengan kode Anda.
Carl Manaster
6

Mengapa tidak menyertakan semacam templat preprocessor \ pembuatan kode untuk sistem ini? Langkah build tambahan khusus yang mengeksekusi dan memancarkan file sumber java tambahan sebelum mengkompilasi sisa kode. Ini adalah cara memasukkan klien web dari file wsdl dan xsd sering bekerja.

Anda tentu saja akan memiliki pemeliharaan generator kode preprocessor \ itu, tetapi Anda tidak perlu khawatir tentang pemeliharaan kode inti duplikat.

Karena waktu kompilasi kode Java sedang dipancarkan, tidak ada penalti kinerja yang harus dibayar untuk kode tambahan. Tetapi Anda mendapatkan penyederhanaan pemeliharaan dengan memiliki templat alih-alih semua kode yang digandakan.

Java generics tidak memberikan manfaat kinerja karena jenis penghapusan dalam bahasa, casting sederhana digunakan sebagai gantinya.

Dari pemahaman saya tentang template C ++, mereka dikompilasi menjadi beberapa fungsi per setiap permintaan template. Anda akan berakhir dengan kode waktu kompilasi tengah duplikat untuk setiap jenis yang Anda simpan di std :: vector misalnya.

Peter Smith
sumber
2

Tidak ada metode Java waras yang cukup panjang untuk memiliki 12 varian ... dan JITC membenci metode yang panjang - hanya menolak untuk mengoptimalkannya dengan benar. Saya telah melihat faktor percepatan dua dengan hanya membagi metode menjadi dua yang lebih pendek. Mungkin ini jalan yang harus ditempuh.

OTOH yang memiliki banyak salinan mungkin masuk akal bahkan jika ada yang identik. Karena masing-masing digunakan di tempat yang berbeda, mereka dioptimalkan untuk kasus yang berbeda (JITC profil mereka dan kemudian menempatkan kasus langka di jalur yang luar biasa.

Saya akan mengatakan bahwa menghasilkan kode bukanlah masalah besar, dengan asumsi ada alasan bagus. Biaya overhead agak rendah, dan file yang dinamai dengan benar akan langsung mengarah ke sumbernya. Beberapa waktu yang lalu ketika saya menghasilkan kode sumber, saya meletakkan // DO NOT EDITdi setiap baris ... Saya kira itu cukup menghemat.

maaartinus
sumber
1

Sama sekali tidak ada yang salah dengan 'masalah' yang Anda sebutkan. Dari apa yang saya tahu ini adalah jenis desain dan pendekatan yang tepat digunakan oleh server DB untuk memiliki kinerja yang baik.

Mereka memiliki banyak metode khusus untuk memastikan mereka dapat memaksimalkan kinerja untuk semua jenis operasi: bergabung, pilih, agregat, dll ... ketika kondisi tertentu berlaku.

Singkatnya, pembuatan kode seperti yang Anda pikir adalah ide yang buruk. Mungkin Anda harus melihat diagram ini untuk melihat bagaimana DB memecahkan masalah yang serupa dengan Anda:masukkan deskripsi gambar di sini

randomA
sumber
1

Anda mungkin ingin memeriksa apakah Anda masih dapat menggunakan beberapa abstraksi yang masuk akal dan menggunakan misalnya pola metode templat untuk menulis kode yang dapat dimengerti untuk fungsionalitas umum dan memindahkan perbedaan antara metode ke dalam "operasi primitif" (sesuai dengan pola descrition) di 12 subkelas. Ini akan sangat meningkatkan rawatan dan pengujian, dan itu mungkin sebenarnya memiliki kinerja yang sama dengan kode saat ini, karena JVM dapat inline metode panggilan untuk operasi primitif setelah beberapa saat. Tentu saja, Anda harus memeriksa ini dalam tes kinerja.

Hans-Peter Störr
sumber
0

Anda dapat memecahkan masalah metode khusus dengan menggunakan bahasa Scala.

Scala dapat inline metode, ini (dalam kombinasi dengan penggunaan fungsi urutan lebih tinggi mudah) memungkinkan untuk menghindari duplikasi kode gratis - ini sepertinya masalah utama yang disebutkan dalam jawaban Anda.

Tetapi juga, Scala memiliki macro sintaksis, yang memungkinkan untuk melakukan banyak hal dengan kode pada waktu kompilasi dengan cara yang aman.

Dan masalah umum dari tipe tinju primitif ketika digunakan dalam obat generik adalah mungkin untuk diselesaikan di Scala, juga: ia dapat melakukan spesialisasi generik untuk primitif untuk menghindari tinju secara otomatis dengan menggunakan @specializedanotasi - hal ini dibangun ke dalam bahasa itu sendiri. Jadi, pada dasarnya, Anda akan menulis satu metode generik di Scala, dan itu akan berjalan dengan kecepatan metode khusus. Dan jika Anda memerlukan aritmatika generik cepat juga, itu mudah dilakukan dengan menggunakan "pola" Typeclass untuk menyuntikkan operasi dan nilai-nilai untuk berbagai jenis numerik.

Juga, Scala mengagumkan dalam banyak hal. Dan Anda tidak perlu menulis ulang semua kode ke Scala, karena Scala <-> interoperabilitas Java sangat baik. Pastikan untuk menggunakan SBT (scala build tool) untuk membangun proyek.

Sersan Borsch
sumber
-1

Jika fungsi yang dipermasalahkan besar, mengubah bit "mode / paradigma" menjadi antarmuka dan kemudian mengirimkan objek yang mengimplementasikan antarmuka itu sebagai parameter agar fungsi tersebut dapat berfungsi. GoF menyebut ini pola "Strategi", iirc. Jika fungsinya kecil, peningkatan overhead mungkin signifikan ... apakah seseorang menyebutkan profil belum? ... lebih banyak orang harus menyebutkan profil.

mjfgates
sumber
ini lebih mirip komentar daripada jawaban
agas
-2

Berapa umur program? Sebagian besar perangkat keras yang lebih baru mungkin akan menghilangkan hambatan dan Anda dapat beralih ke versi yang lebih mudah dikelola. Tetapi perlu ada alasan untuk pemeliharaan, jadi kecuali tugas Anda adalah meningkatkan basis kode, biarkan seperti itu berfungsi.

pengguna138623
sumber
1
ini sepertinya hanya mengulangi poin-poin yang dibuat dan dijelaskan dalam jawaban sebelumnya yang diposting beberapa jam yang lalu
agas
1
Satu-satunya cara untuk menentukan di mana kemacetan sebenarnya adalah dengan profil itu - seperti yang disebutkan dalam jawaban lainnya. Tanpa informasi kunci itu, setiap perbaikan yang disarankan untuk kinerja adalah spekulasi murni.