Akankah kompiler mengoptimalkan argumen fungsi statis yang tidak digunakan?

Saya memiliki sekelompok fungsi yang semuanya dideklarasikan static dan fastcall. Kebanyakan dari mereka menggunakan pointer ke struct yang kurang lebih berperan sebagai this di C++. Beberapa fungsi tidak memerlukan apa pun di struct, tetapi demi keseragaman saya tetap ingin meneruskan pointernya. Akankah kompiler memperhatikan bahwa argumen tersebut tidak digunakan dan mengabaikan pengalokasian register ke argumen tersebut?


person cleong    schedule 12.01.2013    source sumber


Jawaban (3)


Saya menulis program omong kosong ini untuk mengujinya. Ada beberapa kode yang tidak masuk akal dalam fungsinya, dan memanggilnya beberapa kali karena jika tidak, kompiler hanya akan memasukkan seluruh pemanggilan fungsi sehingga membuat pengujian tidak berguna. (Saya tahu ini adalah campuran aneh antara C dan C++.... kode bodoh, tetapi masih berfungsi untuk menunjukkan masalahnya)

#include <stdio.h>
#include <vector>
#include <algorithm>

using namespace std;

struct demo
{
    int a;
    char ch;
};

static  void __fastcall func(struct demo* d)
{
    for(int i = 0; i < 100; i++) {
    std::vector<int> a;
    std::sort(begin(a), end(a));
    printf("THis is a test");
    printf("Hello world\n");
    }
}

int main()
{
  //  void*p = (void*)&func;
    struct demo d;
    func(&d);
    func(&d);
    func(&d);
    func(&d);
    func(&d);
    func(&d);
    func(&d);
    //printf((const char*)p);
}

Seperti yang tertulis di sana, fungsi memanggil kompilasi ke ini :-

    func(&d);
00F61096  call        func (0F61000h)  
    func(&d);
00F6109B  call        func (0F61000h)  
    func(&d);
00F610A0  call        func (0F61000h)  
    func(&d);
00F610A5  call        func (0F61000h)  
    func(&d);
00F610AA  call        func (0F61000h)  
    func(&d);
00F610AF  call        func (0F61000h)  
    func(&d);
00F610B4  call        func (0F61000h)  

Yang menunjukkan bahwa compiler memang akan menghilangkan parameter jika tidak digunakan. Namun jika saya menghapus komentar pada dua baris di sana untuk mengambil alamat fungsi tersebut maka segalanya berubah, malah menghasilkan ini :-

00C71099  lea         ecx,[esp]  
00C7109C  call        func (0C71000h)  
    func(&d);
00C710A1  lea         ecx,[esp]  
00C710A4  call        func (0C71000h)  
    func(&d);
00C710A9  lea         ecx,[esp]  
00C710AC  call        func (0C71000h)  
    func(&d);
00C710B1  lea         ecx,[esp]  
00C710B4  call        func (0C71000h) 

Di mana ia TIDAK mengirim penunjuk.

Asumsi saya adalah bahwa dalam kasus pertama kompiler dapat membuktikan bahwa jika ia menghasilkan konvensi pemanggilan khusus ke fungsi tersebut maka tidak mungkin ada efek yang terlihat oleh pengguna tetapi dalam kasus kedua di mana Anda mengambil pointer ke fungsi tersebut, fungsinya dapat dipanggil dari modul lain yang dikompilasi secara terpisah yang tidak memiliki cara untuk mengetahui apakah parameter tersebut diperlukan atau tidak. Meskipun dalam hal ini tidak masalah apakah registernya disetel atau tidak, secara umum kompiler harus tetap berpegang pada pola pemanggilan yang tepat jadi saya berasumsi bahwa itu menghasilkan kode yang paling umum dan tidak akan menggunakan konvensi pemanggilan khusus jika bisa. Tidak membuktikan bahwa ia dapat melihat semua kode yang dapat memanggil fungsi tersebut.

Bagaimanapun, untuk menjawab pertanyaan itu, tidak, kompiler tidak akan selalu meneruskan parameter yang tidak digunakan dalam register tetapi saya pasti tidak akan menulis kode apa pun yang bergantung pada perilaku spesifik apa pun di sini seperti mengubah kode yang tidak terkait di tempat lain (mengambil alamat fungsi), diubah perilaku itu dan tidak ada yang dijamin oleh standar apa pun yang saya lihat.

person jcoder    schedule 12.01.2013
comment
masuk akal bahwa mengambil alamat suatu fungsi tidak memungkinkan parameter yang tidak digunakan untuk dioptimalkan, karena penunjuk fungsi diketik ke tanda tangan fungsi, (bahkan ketika Anda memasukkannya ke void*) dan kompiler secara umum tidak dapat menjadi tentu saja jika kamu menggunakannya lagi dengan sihir aneh. +1 untuk menunjukkan masalah ini - person Andreas Grapentin; 12.01.2013
comment
Memang. Saya bertanya-tanya apakah dengan pembuatan kode waktu tautan, kompiler mungkin dapat menggunakan konvensi panggilan khusus karena ia dapat melihat semua kode, tetapi ternyata tidak, setidaknya dalam kasus pengujian saya. - person jcoder; 12.01.2013
comment
Kesimpulan utama yang saya ambil dari pengujian saya adalah jangan mengandalkan hal ini. Ini mungkin atau mungkin tidak meneruskan parameter dalam register. - person jcoder; 12.01.2013
comment
Pengujian Anda menunjukkan bahwa pemanggil tidak mengisi register, tetapi belum menunjukkan apakah penerima panggilan telah mengalokasikannya atau tidak (yaitu apakah argumen berikutnya cenderung tidak tumpah ke tumpukan) - person JasonD; 12.01.2013
comment
Terimakasih banyak. Saya pikir secara umum kompiler akan menahan diri untuk tidak mengubah konvensi panggilan ketika alamat suatu fungsi diambil karena penunjuk dapat melompat ke fungsi yang berbeda. - person cleong; 12.01.2013

Pertama-tama, kompiler harus memperingatkan Anda tentang argumen fungsi yang tidak digunakan, jika Anda menggunakan flag kompiler yang tepat (-Wall, misalnya). Saya sarankan untuk meninggalkan argumen yang tidak Anda gunakan di luar daftar argumen, meskipun saya' kurasa mereka akan dioptimalkan. Apa yang dapat Anda lakukan untuk memastikannya adalah mengkompilasi kode Anda dua kali, sekali dengan argumen dan sekali tanpa argumen, dan lakukan diff di antara biner dan lihat apakah keduanya cocok. Saya pikir itu harus bergantung pada kompiler yang Anda gunakan, dan tanda optimasi.

Bersulang,

andi

person Andreas Grapentin    schedule 12.01.2013
comment
setuju bahwa kompiler memang mengoptimalkannya tetapi memeriksanya menggunakan diff - seseorang tidak akan dapat memeriksa perbedaannya (karena eksekusi bergantung pada banyak hal lain yang mungkin berbeda untuk setiap proses eksekusi, terlebih lagi inisialisasi variabel tidak akan memakan banyak waktu - person exexzian; 12.01.2013
comment
@sansix jika binarinya sama, mengapa perilakunya harus berbeda? - person Andreas Grapentin; 12.01.2013
comment
Ngomong-ngomong - bedakan b/w binarai apa? (mungkin saya berpikir sebaliknya) - person exexzian; 12.01.2013
comment
@sansix maaf, saya tidak mengerti pertanyaan Anda. - person Andreas Grapentin; 12.01.2013
comment
pernyataan Anda ini do a diff between the binaries - Saya menanyakan biner b/w yang berbeda dari apa? waktu eksekusi atau ?? - person exexzian; 12.01.2013
comment
@sansix diff adalah alat unix untuk membandingkan file. Saya menyarankan membandingkan program yang dikompilasi untuk melihat apakah kode yang dihasilkan (file biner 1) dengan argumen fungsi yang tidak digunakan cocok dengan kode yang dihasilkan (file biner 2) tanpa argumen yang tidak digunakan. itu seharusnya menjadi cara yang dapat diandalkan untuk mengetahui apakah mereka sudah dioptimalkan. - person Andreas Grapentin; 12.01.2013
comment
Ohhh mengerti sekarang, ya itu memang pendekatan yang bagus. +1 - person exexzian; 12.01.2013
comment
Tidak ada jaminan bahwa perbedaan apa pun yang ditemukan dalam biner adalah karena argumennya telah dihilangkan, jadi menurut saya ini tidak dapat diandalkan sama sekali. - person JasonD; 12.01.2013
comment
@JasonD Saya setuju, bahwa perbedaan tidak akan memberi tahu Anda banyak. Namun, saya memperkirakan akan ada kesetaraan, dan hal tersebut akan menjadi hasil yang cukup meyakinkan. :) - person Andreas Grapentin; 13.01.2013

Anda dapat menghilangkan argumen dalam definisi fungsi. Ini akan memberikan petunjuk kepada kompiler bahwa argumen tertentu tidak digunakan, Ini akan memanfaatkannya dan mengoptimalkan kode

fungsi kosong(demo struktur *) { ... }

person sameer chaudhari    schedule 10.02.2017