Saya telah mencari di Google dan menelusuri situs web Go, tetapi sepertinya saya tidak dapat menemukan penjelasan untuk waktu pembuatan Go yang luar biasa. Apakah mereka produk dari fitur bahasa (atau ketiadaan), kompiler yang sangat optimal, atau yang lain? Saya tidak mencoba mempromosikan Go; Saya hanya penasaran.
performance
compiler-construction
build-process
go
Evan Kroske
sumber
sumber
Jawaban:
Analisis ketergantungan.
The Go FAQ digunakan untuk berisi kalimat berikut:
Meskipun frasa tersebut tidak ada dalam FAQ lagi, topik ini dijabarkan lebih lanjut dalam pembicaraan Go at Google , yang membandingkan pendekatan ketergantungan analisis C / C ++ dan Go.
Itulah alasan utama kompilasi cepat. Dan ini dengan desain.
sumber
Saya pikir itu bukan karena kompiler Go cepat , itu kompiler lain lambat .
Kompiler C dan C ++ harus mem-parsing sejumlah besar header - misalnya, mengkompilasi C ++ "hello world" membutuhkan kompilasi 18k baris kode, yang hampir setengah megabyte sumber!
Kompiler Java dan C # dijalankan dalam VM, yang berarti bahwa sebelum mereka dapat mengkompilasi apa pun, sistem operasi harus memuat seluruh VM, maka mereka harus dikompilasi dengan JIT dari bytecode ke kode asli, yang semuanya membutuhkan waktu.
Kecepatan kompilasi tergantung pada beberapa faktor.
Beberapa bahasa dirancang untuk dikompilasi dengan cepat. Sebagai contoh, Pascal dirancang untuk dikompilasi menggunakan kompiler single-pass.
Kompiler itu sendiri dapat dioptimalkan juga. Sebagai contoh, kompiler Turbo Pascal ditulis dalam assembler yang dioptimalkan dengan tangan, yang dikombinasikan dengan desain bahasa, menghasilkan kompiler yang sangat cepat yang bekerja pada perangkat keras kelas 286. Saya pikir bahkan sekarang, kompiler Pascal modern (misalnya FreePascal) lebih cepat daripada kompiler Go.
sumber
Ada beberapa alasan mengapa kompiler Go jauh lebih cepat daripada kebanyakan kompiler C / C ++:
Alasan utama : Kebanyakan kompiler C / C ++ menunjukkan desain yang sangat buruk (dari perspektif kecepatan kompilasi). Juga, dari perspektif kecepatan kompilasi, beberapa bagian dari ekosistem C / C ++ (seperti editor di mana programmer menulis kode mereka) tidak dirancang dengan kecepatan-kompilasi dalam pikiran.
Alasan utama : Kecepatan kompilasi yang cepat adalah pilihan sadar dalam kompiler Go dan juga dalam bahasa Go
Kompiler Go memiliki pengoptimal yang lebih sederhana daripada kompiler C / C ++
Tidak seperti C ++, Go tidak memiliki template dan tidak ada fungsi inline. Ini berarti bahwa Go tidak perlu melakukan template atau fungsi instantiation.
Kompilator Go menghasilkan kode rakitan tingkat rendah lebih cepat dan pengoptimal bekerja pada kode rakitan, sedangkan dalam kompiler C / C ++ tipikal, optimasi yang dilewati bekerja pada representasi internal dari kode sumber asli. Overhead tambahan dalam kompiler C / C ++ berasal dari fakta bahwa representasi internal perlu dihasilkan.
Tautan akhir (5l / 6l / 8l) dari program Go dapat lebih lambat daripada menautkan program C / C ++, karena kompilator Go sedang melalui semua kode assembly yang digunakan dan mungkin juga melakukan tindakan tambahan lainnya yaitu C / C ++ penghubung tidak melakukan
Beberapa kompiler C / C ++ (GCC) menghasilkan instruksi dalam bentuk teks (untuk diteruskan ke assembler), sedangkan kompiler Go menghasilkan instruksi dalam bentuk biner. Pekerjaan ekstra (tapi tidak banyak) perlu dilakukan untuk mengubah teks menjadi biner.
Kompiler Go hanya menargetkan sejumlah kecil arsitektur CPU, sedangkan kompiler GCC menargetkan sejumlah besar CPU
Kompiler yang dirancang dengan tujuan kecepatan kompilasi tinggi, seperti Jikes, cepat. Pada CPU 2GHz, Jikes dapat mengkompilasi 20000+ baris kode Java per detik (dan mode kompilasi tambahan bahkan lebih efisien).
sumber
Efisiensi kompilasi adalah tujuan desain utama:
FAQ bahasa cukup menarik terkait fitur bahasa tertentu yang berkaitan dengan penguraian:
sumber
aType
adalah referensi variabel, dan kemudian dalam fase analisis semantik ketika Anda mengetahui itu bukan Anda mencetak kesalahan yang berarti pada saat itu.Sementara sebagian besar di atas benar, ada satu hal yang sangat penting yang tidak benar-benar disebutkan: manajemen ketergantungan.
Go hanya perlu menyertakan paket yang Anda impor langsung (seperti yang sudah mengimpor apa yang mereka butuhkan). Ini sangat kontras dengan C / C ++, di mana setiap file tunggal mulai termasuk x header, yang mencakup header y dll. Intinya: Pengompilasi Go membutuhkan waktu linier wrt dengan jumlah paket yang diimpor, di mana C / C ++ membutuhkan waktu yang eksponensial.
sumber
Tes yang baik untuk efisiensi terjemahan kompiler adalah kompilasi sendiri: berapa lama kompiler yang diberikan untuk mengkompilasi dirinya sendiri? Untuk C ++ dibutuhkan waktu yang sangat lama (jam?). Sebagai perbandingan, kompiler Pascal / Modula-2 / Oberon akan dikompilasi sendiri dalam waktu kurang dari satu detik pada mesin modern [1].
Go telah terinspirasi oleh bahasa-bahasa ini, tetapi beberapa alasan utama untuk efisiensi ini termasuk:
Sintaks yang didefinisikan dengan jelas yang secara matematis suara, untuk pemindaian dan penguraian efisien
Tipe-aman dan dikompilasi secara statis yang menggunakan kompilasi terpisah dengan dependensi dan tipe memeriksa melintasi batas-batas modul, untuk menghindari pembacaan ulang file header yang tidak perlu dan kompilasi ulang modul lain - yang bertentangan dengan kompilasi independen seperti di C / C ++ di mana tidak ada pemeriksaan modul silang yang dilakukan oleh kompiler (karena itu perlu membaca ulang semua file header itu berulang-ulang, bahkan untuk program "hello world" sederhana satu-baris).
Implementasi compiler yang efisien (mis. Single-pass, recursive-descent top-down parsing) - yang tentu saja sangat terbantu oleh poin 1 dan 2 di atas.
Prinsip-prinsip ini telah dikenal dan diimplementasikan secara penuh pada tahun 1970-an dan 1980-an dalam bahasa seperti Mesa, Ada, Modula-2 / Oberon dan beberapa lainnya, dan baru sekarang (pada tahun 2010) menemukan jalan mereka ke bahasa modern seperti Go (Google) , Swift (Apple), C # (Microsoft) dan beberapa lainnya.
Mari kita berharap bahwa ini akan segera menjadi norma dan bukan pengecualian. Untuk sampai di sana, dua hal perlu terjadi:
Pertama, penyedia platform perangkat lunak seperti Google, Microsoft dan Apple harus memulai dengan mendorong pengembang aplikasi untuk menggunakan metodologi kompilasi yang baru, sambil memungkinkan mereka untuk menggunakan kembali basis kode yang ada. Inilah yang sekarang coba dilakukan Apple dengan bahasa pemrograman Swift, yang dapat hidup berdampingan dengan Objective-C (karena menggunakan lingkungan runtime yang sama).
Kedua, platform perangkat lunak yang mendasari sendiri akhirnya harus ditulis ulang seiring waktu menggunakan prinsip-prinsip ini, sementara secara bersamaan mendesain ulang hirarki modul dalam proses untuk membuatnya kurang monolitik. Ini tentu saja merupakan tugas yang sangat besar dan mungkin mengambil bagian yang lebih baik dari satu dekade (jika mereka cukup berani untuk benar-benar melakukannya - yang saya sama sekali tidak yakin dalam kasus Google).
Bagaimanapun, itu adalah platform yang mendorong adopsi bahasa, dan bukan sebaliknya.
Referensi:
[1] http://www.inf.ethz.ch/personal/wirth/ProjectOberon/PO.System.pdf , halaman 6: "Kompilator mengkompilasi sendiri dalam waktu sekitar 3 detik". Kutipan ini untuk papan pengembangan FPGA Xilinx Spartan-3 berbiaya rendah yang beroperasi pada frekuensi clock 25 MHz dan menampilkan 1 MByte memori utama. Dari yang satu ini dapat dengan mudah diekstrapolasi menjadi "kurang dari 1 detik" untuk prosesor modern yang berjalan pada frekuensi clock jauh di atas 1 GHz dan beberapa GBytes memori utama (yaitu beberapa pesanan yang besarnya lebih kuat daripada papan FPGA Xilinx Spartan-3), bahkan ketika memperhitungkan kecepatan I / O. Sudah kembali pada tahun 1990 ketika Oberon dijalankan pada prosesor NS32X32 25MHz dengan memori utama 2-4 MB, kompiler mengkompilasi dirinya sendiri hanya dalam beberapa detik. Gagasan untuk benar-benar menunggubagi kompiler untuk menyelesaikan siklus kompilasi sama sekali tidak dikenal oleh programmer Oberon bahkan saat itu. Untuk program tipikal, selalu diperlukan lebih banyak waktu untuk menghapus jari dari tombol mouse yang memicu perintah kompilasi daripada menunggu kompiler menyelesaikan kompilasi yang baru saja dipicu. Itu benar-benar kepuasan instan, dengan waktu tunggu hampir nol. Dan kualitas kode yang dihasilkan, meskipun tidak selalu setara dengan kompiler terbaik yang tersedia saat itu, sangat bagus untuk sebagian besar tugas dan cukup dapat diterima secara umum.
sumber
Go dirancang untuk menjadi cepat, dan itu menunjukkan.
Perhatikan bahwa GO bukan satu-satunya bahasa dengan fitur seperti itu (modul adalah norma dalam bahasa modern), tetapi mereka melakukannya dengan baik.
sumber
Mengutip dari buku " The Go Programming Language " oleh Alan Donovan dan Brian Kernighan:
sumber
Ide dasar kompilasi sebenarnya sangat sederhana. Pengurai keturunan rekursif, pada prinsipnya, dapat berjalan pada kecepatan terikat I / O. Pembuatan kode pada dasarnya adalah proses yang sangat sederhana. Tabel simbol dan sistem tipe dasar bukanlah sesuatu yang membutuhkan banyak perhitungan.
Namun, tidak sulit untuk memperlambat kompiler.
Jika ada fase preprosesor, dengan multi-level menyertakan arahan, definisi makro, dan kompilasi bersyarat, sama berharganya dengan hal-hal itu, tidak sulit untuk memuatnya. (Sebagai contoh, saya sedang memikirkan file header Windows dan MFC.) Itulah mengapa header yang dikompilasi diperlukan.
Dalam hal mengoptimalkan kode yang dihasilkan, tidak ada batasan berapa banyak pemrosesan yang dapat ditambahkan ke fase itu.
sumber
Sederhananya (dengan kata-kata saya sendiri), karena sintaksinya sangat mudah (untuk menganalisis dan mengurai)
Misalnya, tanpa pewarisan jenis berarti, bukan analisis yang bermasalah untuk mengetahui apakah tipe baru tersebut mengikuti aturan yang diberlakukan oleh tipe dasar.
Sebagai contoh dalam contoh kode ini: "antarmuka" kompiler tidak pergi dan memeriksa apakah tipe yang dimaksud mengimplementasikan antarmuka yang diberikan saat menganalisis jenis itu. Hanya sampai digunakan (dan JIKA digunakan) pemeriksaan dilakukan.
Contoh lain, kompiler memberi tahu Anda jika Anda mendeklarasikan variabel dan tidak menggunakannya (atau jika Anda seharusnya memiliki nilai balik dan Anda tidak)
Berikut ini tidak dikompilasi:
Ini semacam penegakan dan prinsip membuat kode yang dihasilkan lebih aman, dan kompiler tidak harus melakukan validasi tambahan yang dapat dilakukan oleh programmer.
Pada dasarnya semua detail ini membuat bahasa lebih mudah diurai yang menghasilkan kompilasi cepat.
Sekali lagi, dengan kata-kata saya sendiri.
sumber
Saya pikir Go dirancang secara paralel dengan kreasi kompiler, jadi mereka adalah teman terbaik sejak lahir. (IMO)
sumber
Apa lagi?
sumber