Tampaknya bagi saya itu akan bekerja dengan sangat baik untuk melakukan optimasi rekursi ekor di C dan C ++, namun saat debugging saya sepertinya tidak pernah melihat frame stack yang menunjukkan optimasi ini. Itu agak bagus, karena tumpukan memberi tahu saya seberapa dalam rekursi itu. Namun, pengoptimalan juga akan menyenangkan.
Apakah ada kompiler C ++ yang melakukan optimasi ini? Mengapa? Kenapa tidak?
Bagaimana cara saya memberitahu kompiler untuk melakukannya?
- Untuk MSVC:
/O2
atau/Ox
- Untuk GCC:
-O2
atau-O3
Bagaimana kalau memeriksa apakah kompiler telah melakukan ini dalam kasus tertentu?
- Untuk MSVC, aktifkan output PDB untuk dapat melacak kode, lalu periksa kodenya
- Untuk GCC ..?
Saya masih akan mengambil saran untuk bagaimana menentukan apakah fungsi tertentu dioptimalkan seperti ini oleh kompiler (meskipun saya merasa meyakinkan bahwa Konrad menyuruh saya untuk menganggapnya)
Selalu mungkin untuk memeriksa apakah kompiler melakukan ini sama sekali dengan membuat rekursi tak terbatas dan memeriksa apakah itu menghasilkan infinite loop atau stack overflow (saya melakukan ini dengan GCC dan menemukan itu -O2
sudah cukup), tapi saya ingin menjadi mampu memeriksa fungsi tertentu yang saya tahu akan berakhir pula. Saya ingin memiliki cara mudah untuk memeriksa ini :)
Setelah beberapa pengujian, saya menemukan bahwa destruktor merusak kemungkinan melakukan optimasi ini. Kadang-kadang layak untuk mengubah cakupan variabel dan temporal tertentu untuk memastikan mereka keluar dari ruang lingkup sebelum pernyataan pengembalian dimulai.
Jika ada destructor yang perlu dijalankan setelah tail-call, optimasi tail-call tidak dapat dilakukan.
sumber
gcc
memiliki opsi yang lebih sempit-foptimize-sibling-calls
untuk "mengoptimalkan panggilan saudara dan panggilan rekursif". Pilihan ini (menurutgcc(1)
halaman manual untuk versi 4.4, 4.7 dan 4.8 menargetkan berbagai platform) diaktifkan pada tingkat-O2
,-O3
,-Os
.gcc 4.3.2 sepenuhnya menguraikan fungsi ini (
atoi()
implementasi jelek / sepele ) kemain()
. Level optimisasi adalah-O1
. Saya perhatikan jika saya bermain-main dengannya (bahkan mengubahnya daristatic
menjadiextern
, rekursi ekor hilang cukup cepat, jadi saya tidak akan bergantung padanya untuk kebenaran program.sumber
extern
metode pun dapat diuraikan.-O1
tidak ada inlining dan tidak ada optimasi rekursi ekor . Anda harus menggunakannya-O2
untuk itu (well, dalam 4.2.x, yang sudah agak kuno sekarang, masih tidak akan diuraikan). BTW Perlu juga ditambahkan bahwa gcc dapat mengoptimalkan rekursi bahkan ketika itu tidak sepenuhnya ekor (seperti faktorial tanpa akumulator).Seperti halnya yang jelas (kompiler tidak melakukan optimasi semacam ini kecuali Anda memintanya), ada kerumitan tentang optimasi panggilan ekor di C ++: destructors.
Diberikan sesuatu seperti:
Compiler tidak dapat (secara umum) mengoptimalkan panggilan karena ini perlu memanggil destruktor
cls
setelah panggilan rekursif kembali.Terkadang kompilator dapat melihat bahwa destruktor tidak memiliki efek samping yang terlihat secara eksternal (sehingga dapat dilakukan lebih awal), tetapi seringkali tidak.
Bentuk yang sangat umum dari ini adalah di mana
Funky
sebenarnya astd::vector
atau serupa.sumber
Sebagian besar kompiler tidak melakukan optimasi dalam bentuk debug.
Jika menggunakan VC, coba versi rilis dengan dinyalakan info PDB - ini akan membuat Anda menelusuri aplikasi yang dioptimalkan dan semoga Anda akan melihat apa yang Anda inginkan. Perhatikan, bagaimanapun, bahwa debugging dan penelusuran build yang dioptimalkan akan membuat Anda berputar-putar di mana-mana, dan sering kali Anda tidak dapat memeriksa variabel secara langsung karena mereka hanya berakhir di register atau dioptimalkan sepenuhnya sepenuhnya. Ini pengalaman yang "menarik" ...
sumber
Seperti yang disebutkan Greg, kompiler tidak akan melakukannya dalam mode debug. Tidak apa-apa untuk debug build menjadi lebih lambat daripada build prod, tetapi mereka seharusnya tidak crash lebih sering: dan jika Anda bergantung pada optimisasi panggilan ekor, mereka mungkin melakukan hal itu. Karena itu, seringkali yang terbaik adalah menulis ulang panggilan ekor sebagai loop normal. :-(
sumber