Bagaimana mendokumentasikan dan mengajar orang lain "dioptimalkan di luar pengakuan" kode intensif komputasi?

11

Kadang-kadang ada 1% kode yang cukup intensif secara komputasi yang membutuhkan jenis optimasi tingkat rendah terberat. Contohnya adalah pemrosesan video, pemrosesan gambar, dan semua jenis pemrosesan sinyal, secara umum.

Tujuannya adalah untuk mendokumentasikan, dan mengajarkan teknik pengoptimalan, sehingga kode tidak menjadi tidak dapat dipelihara dan cenderung dihapus oleh pengembang yang lebih baru. (*)

(*) Meskipun ada kemungkinan bahwa optimasi tertentu sama sekali tidak berguna di beberapa CPU mendatang yang tidak terduga, sehingga kode tersebut tetap akan dihapus.

Mempertimbangkan bahwa penawaran perangkat lunak (komersial atau open-source) mempertahankan keunggulan kompetitif mereka dengan memiliki kode tercepat dan memanfaatkan arsitektur CPU terbaru, penulis perangkat lunak sering perlu mengubah kode mereka untuk membuatnya berjalan lebih cepat sambil mendapatkan output yang sama untuk suatu produk tertentu. tugas, daftar putih mentolerir sejumlah kecil kesalahan pembulatan.

Biasanya, seorang penulis perangkat lunak dapat menyimpan banyak versi fungsi sebagai dokumentasi dari setiap optimasi / algoritma penulisan ulang yang terjadi. Bagaimana cara membuat versi ini tersedia bagi orang lain untuk mempelajari teknik optimasi mereka?

Terkait:

rwong
sumber
1
Anda bisa menyimpan versi yang berbeda dalam kode, berkomentar, dengan banyak komentar memberi tahu pembaca apa yang sedang terjadi.
Mike Dunlavey
1
Dan jangan hanya memberi tahu mereka apa yang dilakukan kode, tetapi mengapa lebih cepat seperti itu. Sertakan tautan ke algoritme jika diperlukan, baik milik Anda sendiri, seperti wiki, dokumen, atau sumber daya yang tersedia di internet (cukup perhatikan tautan-busuk dalam hal ini, mungkin bijaksana untuk menyalinnya ke sistem dokumen Anda sendiri dengan tautan ke sumber asli .)
Marjan Venema
1
@MikeDunlavey: Aduh, tolong jangan berkomentar. Hanya memiliki beberapa implementasi dari fungsi yang sama, dan panggil yang paling cepat. Dengan begitu Anda dapat dengan mudah beralih ke versi kode yang berbeda, dan membandingkan semuanya.
sleske
2
@sleske Terkadang, memiliki lebih banyak kode biner dapat memperlambatnya.
quant_dev
@quant_dev: Ya, itu bisa terjadi. Saya hanya berpikir sangat penting bahwa kode ini dibangun & dijalankan (idealnya) secara teratur, agar selalu terbarui. Mungkin membangunnya dalam mode debug saja.
sleske

Jawaban:

10

Jawaban singkat

Buat optimisasi tetap lokal, jelaskan, dokumentasikan dengan baik, dan buat mudah untuk membandingkan versi yang dioptimalkan satu sama lain dan dengan versi yang tidak dioptimalkan, baik dalam hal kode sumber dan kinerja run-time.

Jawaban penuh

Jika optimisasi seperti itu benar - benar penting bagi produk Anda, maka Anda perlu tahu tidak hanya mengapa optimisasi bermanfaat sebelumnya, tetapi juga memberikan informasi yang cukup untuk membantu pengembang mengetahui apakah mereka akan berguna di masa depan.

Idealnya, Anda perlu mengabadikan pengujian kinerja ke dalam proses pembuatan Anda, sehingga Anda mengetahui kapan teknologi baru membatalkan optimisasi lama.

Ingat:

Aturan Pertama Optimalisasi Program: Jangan lakukan itu.

Aturan Kedua tentang Pengoptimalan Program (hanya untuk para ahli!): Jangan lakukan itu dulu. "

- Michael A. Jackson

Untuk mengetahui apakah sekarang saatnya membutuhkan benchmarking dan pengujian.

Seperti yang Anda sebutkan, masalah terbesar dengan kode yang sangat optimal adalah sulitnya mempertahankannya, sejauh mungkin, Anda perlu menjaga bagian yang dioptimalkan terpisah dari bagian yang tidak dioptimalkan. Apakah Anda melakukan ini melalui menghubungkan waktu kompilasi, panggilan fungsi virtual runtime atau sesuatu di antaranya tidak masalah. Yang penting adalah bahwa ketika Anda menjalankan tes Anda, Anda ingin dapat menguji terhadap semua versi yang saat ini Anda minati.

Saya akan cenderung untuk membangun suatu sistem sedemikian rupa sehingga versi dasar kode produksi yang tidak dioptimalkan dapat selalu digunakan untuk memahami maksud dari kode tersebut, kemudian membangun berbagai modul yang dioptimalkan bersama dengan ini berisi versi atau versi yang dioptimalkan, secara eksplisit mendokumentasikan di mana pun versi yang dioptimalkan berbeda dari garis dasar. Ketika Anda menjalankan tes (unit dan integrasi), Anda menjalankannya pada versi yang tidak dioptimalkan dan pada semua modul yang dioptimalkan saat ini.

Contoh

Misalnya, katakanlah Anda memiliki fungsi Fast Fourier Transform . Mungkin Anda memiliki dasar, implementasi algoritmik, fft.cdan pengujian fft_tests.c.

Kemudian datanglah Pentium dan Anda memutuskan untuk mengimplementasikan versi titik tetap dalam fft_mmx.cmenggunakan instruksi MMX . Kemudian pentium 3 datang dan Anda memutuskan untuk menambahkan versi yang menggunakan Streaming SIMD Extensions di fft_sse.c.

Sekarang Anda ingin menambahkan CUDA , jadi Anda menambahkan fft_cuda.c, tetapi temukan bahwa dengan dataset uji yang telah Anda gunakan selama bertahun-tahun, versi CUDA lebih lambat daripada versi SSE! Anda melakukan beberapa analisis dan akhirnya menambahkan dataset yang 100 kali lebih besar dan Anda mendapatkan kecepatan yang Anda harapkan, tetapi sekarang Anda tahu bahwa waktu pengaturan untuk menggunakan versi CUDA adalah signifikan dan dengan dataset kecil Anda harus menggunakan algoritma tanpa biaya pengaturan itu.

Dalam setiap kasus Anda menerapkan algoritma yang sama, semua harus berperilaku dengan cara yang sama, tetapi akan berjalan dengan efisiensi dan kecepatan yang berbeda pada arsitektur yang berbeda (jika mereka akan berjalan sama sekali). Dari sudut pandang kode, Anda dapat membandingkan setiap pasangan file sumber untuk mencari tahu mengapa antarmuka yang sama diimplementasikan dalam cara yang berbeda dan biasanya, cara termudah adalah merujuk kembali ke versi asli yang tidak dioptimalkan.

Semua hal yang sama berlaku untuk implementasi OOP di mana kelas dasar yang mengimplementasikan algoritma yang tidak dioptimalkan, dan kelas turunan menerapkan optimisasi yang berbeda.

Yang penting adalah menjaga hal-hal yang sama yang sama , sehingga perbedaannya jelas .

Mark Booth
sumber
7

Khususnya karena Anda telah mengambil contoh pemrosesan Video dan Gambar seseorang dapat menyimpan kode sebagai bagian dari versi yang sama tetapi aktif atau tidak aktif tergantung pada konteksnya.

Meskipun Anda belum menyebutkan, saya berasumsi di Csini.

Cara paling sederhana dalam Ckode, kita melakukan optimasi (dan juga berlaku ketika mencoba membuat hal-hal portabel) adalah untuk tetap

 
#ifdef OPTIMIZATION_XYZ_ENABLE 
   // your optimzied code here... 
#else  
   // your basic code here...

Ketika Anda mengaktifkan #define OPTIMIZATION_XYZ_ENABLEselama kompilasi di Makefile, semuanya berjalan sesuai.

Biasanya, memotong beberapa baris kode di tengah fungsi bisa menjadi berantakan ketika ada terlalu banyak fungsi yang dioptimalkan. Oleh karena itu, dalam hal ini seseorang mendefinisikan pointer fungsi yang berbeda untuk melakukan fungsi tertentu.

kode utama selalu dijalankan melalui fungsi pointer seperti


   codec->computed_idct(blocks); 

Tetapi pointer fungsi ditentukan tergantung pada jenis contoh (misalnya di sini fungsi idct dioptimalkan untuk arsitektur CPU yang berbeda.



if(OPTIMIZE_X86) {
  codec->computed_idct = compute_idct_x86; 
}
else if(OPTIMZE_ARM) {
  codec->computed_idct = compute_idct_ARM;
}
else {
  codec->computed_idct = compute_idct_C; 
}

Anda akan melihat kode libjpeg dan kode libmpeg2 dan mungkin ffmpeg untuk teknik seperti itu.

Dipan Mehta
sumber
6

Sebagai seorang peneliti saya akhirnya menulis sedikit kode "bottleneck". Namun, begitu dimasukkan ke dalam produksi, tanggung jawab untuk mengintegrasikannya ke dalam produk dan memberikan dukungan berikutnya jatuh ke pengembang. Seperti yang dapat Anda bayangkan, mengomunikasikan dengan jelas apa dan bagaimana program seharusnya beroperasi adalah yang paling penting.

Saya telah menemukan bahwa ada tiga bahan penting dalam menyelesaikan langkah ini dengan sukses

  1. Algoritma yang digunakan harus benar-benar jelas.
  2. Tujuan setiap lini implementasi harus jelas.
  3. Penyimpangan dari hasil yang diharapkan harus diidentifikasi sesegera mungkin.

Untuk langkah pertama, saya selalu menulis whitepaper pendek yang mendokumentasikan algoritma. Tujuannya di sini adalah untuk benar-benar menulisnya sehingga orang lain dapat menerapkannya dari awal hanya dengan menggunakan kertas putih. Jika itu adalah algoritma yang terkenal dan dipublikasikan, itu cukup untuk memberikan referensi dan mengulangi persamaan kunci. Jika ini adalah karya asli, Anda harus sedikit lebih eksplisit. Ini akan memberi tahu Anda apa yang seharusnya dilakukan kode .

Implementasi aktual yang diserahkan ke pengembangan harus didokumentasikan sedemikian rupa sehingga semua seluk-beluk diberikan secara eksplisit. Jika Anda mendapatkan kunci dalam urutan tertentu untuk menghindari kebuntuan, tambahkan komentar. Jika Anda beralih pada kolom alih-alih pada baris matriks karena masalah koherensi cache, tambahkan komentar. Jika Anda melakukan sesuatu yang sedikit pintar, komentari itu. Jika Anda dapat menjamin whitepaper dan kode tidak akan pernah dipisahkan (melalui VCS atau sistem serupa), Anda dapat merujuk kembali ke whitepaper. Hasilnya dengan mudah dapat lebih dari 50% komentar. Itu benar. Ini akan memberi tahu Anda mengapa kode melakukan apa yang dilakukannya.

Akhirnya, Anda harus bisa menjamin kebenaran dalam menghadapi perubahan. Untungnya kami alat yang berguna dalam pengujian otomatis dan platform integrasi berkelanjutan . Ini akan memberi tahu Anda apa yang sebenarnya dilakukan kode .

Rekomendasi saya yang paling tulus adalah jangan berhemat pada salah satu langkah. Anda akan membutuhkannya nanti;)

drxzcl
sumber
Terima kasih atas jawaban komprehensif Anda. Saya setuju dengan semua poin Anda. Dalam hal pengujian otomatis, saya menemukan bahwa cukup mencakup rentang numerik aritmatika titik tetap dan kode SIMD sulit, sesuatu yang telah saya bakar dua kali. Prasyarat yang dinyatakan dalam komentar saja (tanpa kode untuk memperkuat) tidak selalu dipenuhi.
rwong
Alasan saya belum menerima jawaban Anda adalah karena saya perlu panduan lebih lanjut tentang apa arti "sebuah whitepaper", dan upaya apa yang harus dilakukan untuk memproduksinya. Untuk beberapa industri, ini adalah bagian dari lini bisnis utama, tetapi di industri lain biayanya harus dipertimbangkan dan jalan pintas yang tersedia secara hukum seharusnya diambil.
rwong
Pertama-tama, saya merasakan sakit Anda mengenai pengujian otomatis, aritmatika floating point dan kode paralel. Saya khawatir tidak ada solusi yang valid untuk semua kasus. Biasanya saya bekerja dengan toleransi yang cukup liberal, tetapi dalam industri Anda itu mungkin tidak mungkin.
drxzcl
2
Dalam prakteknya whitepaper sering terlihat seperti draft pertama dari sebuah makalah ilmiah, tanpa bagian "bulu" (tidak ada pengantar yang berarti, tidak ada abstrak, kesimpulan / diskusi minimal dan hanya referensi yang diperlukan untuk memahaminya). Saya melihat penulisan makalah sebagai laporan, dan bagian integral dari, pengembangan algoritma dan / atau pemilihan algoritma. Anda memilih untuk mengimplementasikan algoritma ini (katakanlah spektral FFT). Apa itu sebenarnya? Mengapa Anda memilih yang ini dari yang lain? Apa karakteristik parallelisasinya? Upaya tersebut harus proporsional dengan pekerjaan seleksi / pengembangan.
drxzcl
5

Saya percaya ini akan diselesaikan dengan baik melalui komentar komprehensif kode, ke titik di mana setiap blok kode signifikan memiliki komentar penjelas sebelumnya.

Komentar harus mencakup kutipan untuk spesifikasi atau bahan referensi perangkat keras.

Gunakan istilah-istilah industri dan algoritma luas yang sesuai - misalnya 'arsitektur X menghasilkan jebakan CPU untuk pembacaan yang tidak selaras, sehingga Perangkat Duff ini memenuhi batas penyelarasan berikutnya'.

Saya akan menggunakan penamaan variabel in-your-face untuk memastikan tidak ada kesalahpahaman tentang apa yang terjadi. Bukan Hongaria, tetapi hal-hal seperti 'langkah' untuk menggambarkan jarak dalam byte antara dua piksel vertikal.

Saya juga akan melengkapi ini dengan dokumen pendek yang dapat dibaca secara manusiawi yang memiliki diagram dan desain blok tingkat tinggi.

JBRWilkinson
sumber
1
Menggunakan satu terminologi yang konsisten untuk satu hal (misalnya menggunakan "langkah" di atas makna yang serupa misalnya "langkah", "perataan") dalam proyek yang sama akan membantu. Ini agak sulit ketika mengintegrasikan beberapa basis kode proyek ke dalam satu proyek.
rwong