Haruskah saya memikirkan kode mesin yang dikompilasi ketika saya menulis kode saya?

20

Misalnya saya punya kode berikut:

auto z = [](int x) -> int {
    if (x > 0) {
        switch (x) {
            case 2: return 5;
            case 3: return 6;
            default: return 1;
            }
        }
    return 0;
    };

Dan kemudian saya menyebutnya beberapa kali. Dalam kode asm saya melihat panggilan eksternal dengan lambda .... sesuatu ... Ini menjadi tidak mudah dibaca dan saya pikir itu juga dapat menyebabkan kinerja. Jadi mungkin saya menang dalam meta-pemrograman tetapi apakah saya kalah dalam asm debugging dan kinerja? Haruskah saya menghindari fitur bahasa modern, makro dan aspek pemrograman meta lainnya untuk memastikan kinerja dan kesederhanaan debugging?

cnd
sumber
1
Bergantung pada versi kompiler dan pustaka standar yang dibundelnya, lambda mungkin memang diimplementasikan secara tidak efisien. Lihat pertanyaan ini di Stackoverflow. Namun, tanggung jawab untuk perbaikan harus berada pada vendor kompiler.
rwong
15
Anda tidak perlu men-debug kode rakitan, kecuali jika Anda berada di jalur kinerja kritis. Juga, "kode bersih"! = "Penampilan bagus".
BЈовић
Perbaiki lekukan Anda. Saya mencoba melakukannya, tetapi sepertinya Anda tidak dapat mengedit spasi kosong saja.
Christoffer Hammarström
3
@Heather: Anda tampaknya menggunakan gaya Ratliff , yang belum pernah saya lihat sebelumnya dan sulit untuk dibaca. Ini tentu saja salah satu yang kurang dikenal. Kesan saya adalah bahwa Anda tidak membuat indentasi dengan benar. Akan tetapi jika Anda menemukannya dapat dibaca, saya hanya tidak setuju.
Christoffer Hammarström
1
The ifbenar-benar berlebihan dalam kode contoh, dan sementara compiler mungkin akan menangkap bahwa tidak ada alasan untuk menggoda prediksi cabang buruk.
dmckee

Jawaban:

59

Haruskah saya memikirkan kode mesin yang dikompilasi ketika saya menulis kode saya?

Tidak , tidak ketika Anda menulis kode Anda pertama kali dan tidak menderita masalah kinerja nyata yang dapat diukur. Untuk sebagian besar tugas, ini adalah kasus standar. Berpikir terlalu dini tentang pengoptimalan disebut "pengoptimalan prematur", dan ada alasan bagus mengapa D. Knuth menyebut itu "akar dari semua kejahatan" .

Ya , ketika Anda mengukur hambatan kinerja nyata yang dapat dibuktikan, dan Anda mengidentifikasi bahwa konstruksi lambda spesifik sebagai penyebab utama. Dalam hal ini, mungkin ide yang baik untuk mengingat "hukum abstraksi bocor" karya Joel Spolsky dan memikirkan apa yang mungkin terjadi pada level asm. Namun berhati-hatilah, Anda mungkin akan heran betapa kecilnya peningkatan kinerja ketika Anda mengganti sebuah konstruksi lambda dengan sebuah konstruksi bahasa yang "tidak begitu modern" (setidaknya, ketika menggunakan kompiler C ++ yang layak).

Doc Brown
sumber
2
+1 Ringkas, akurat, dan mudah diikuti per Doc biasa, senang kami memiliki Anda di sini.
Jimmy Hoffa
Setuju, jawaban yang sangat jelas.
cnd
8

Pilihan antara lambda dan kelas functor adalah pertukaran.

Keuntungan dari lambda sebagian besar sintaksis, dengan meminimalkan jumlah boilerplate dan memungkinkan kode terkait secara konseptual ditulis inline, di dalam fungsi yang akan menggunakannya (segera atau lambat).

Dari segi kinerja, ini tidak lebih buruk dari kelas functor , yang merupakan C ++ struct atau kelas yang berisi "metode" tunggal. Bahkan, kompiler memperlakukan lambda tidak berbeda dengan kelas functor yang dihasilkan kompiler di belakang layar.

// define the functor method somewhere
struct some_computer_generated_gibberish_0123456789
{
    int operator() (int x) const
    {
        if (x == 2) return 5;
        if (x == 3) return 6;
        return 0;
    }
};

// make a call
some_computer_generated_gibberish_0123456789 an_instance_of_0123456789;
int outputValue = an_instance_of_0123456789(inputValue);

Dalam contoh kode Anda, kinerja-bijaksana itu tidak berbeda dari panggilan fungsi, karena kelas functor kebetulan tidak memiliki keadaan (karena memiliki klausa tangkap kosong), sehingga tidak memerlukan alokasi, konstruktor atau penghancuran.

int some_computer_generated_gibberish_0123456789_method_more_gibberish(int x)
{
    if (...) return ...;
    return ...;
}

Mendebug kode C ++ non-sepele menggunakan disassembler selalu menjadi tugas yang sulit. Ini benar dengan atau tanpa menggunakan lambda. Ini disebabkan oleh optimisasi kode canggih oleh kompiler C ++ yang menghasilkan penataan ulang, interleaving, dan penghilangan kode mati.

Aspek penyusunan nama agak tidak menyenangkan, dan dukungan debugger untuk lambda masih dalam masa pertumbuhan . Hanya dapat diharapkan bahwa dukungan debugger akan meningkat seiring waktu.

Saat ini, cara terbaik untuk men-debug kode lambda adalah dengan menggunakan debugger yang mendukung pengaturan breakpoint pada tingkat kode sumber, yaitu dengan menentukan nama file sumber dan nomor baris.

rwong
sumber
3

Untuk menambah jawaban dengan @DocBrown, ingat bahwa hari ini CPU murah tetapi tenaga kerja mahal.

Dalam keseluruhan biaya suatu program, perangkat keras biasanya sepele dibandingkan dengan biaya pemeliharaan, yang sejauh ini merupakan bagian paling mahal dari proyek tipikal (bahkan lebih dari pengembangannya).

Oleh karena itu, kode Anda perlu mengoptimalkan pemeliharaan di atas segalanya, kecuali ketika kinerjanya sangat penting (dan itupun pemeliharaan perlu dipertimbangkan).

Paddy Landau
sumber
Hanya sebagian yang benar. Jika kode Anda menjalankan O (n ^ 2) (kuadrat) dan Anda dapat membuatnya menjadi lebih baik, katakanlah O (log (n)) (logaritmik), maka perangkat keras tidak akan pernah mengalami peningkatan kinerja sebanyak mengubah kode. Dalam hal yang ditentukan oleh poster asli, ini sangat tidak mungkin.
gnash117
@ gnash117 - ya, Anda benar jika kode ini akan dijalankan berkali-kali; terima kasih telah menunjukkan ini. Dalam kasus seperti itu, mendokumentasikan kode dengan jelas akan membuatnya dapat dipertahankan sambil memungkinkan peningkatan kinerja.
Paddy Landau
"tenaga kerja itu mahal" - Benar. Waktu pelanggan Anda sangat penting dan seringkali mahal.
Cerad