簡體   English   中英

C ++編譯器是否優化虛擬成員調用?

[英]Does C++ compiler optimize virtual member calls?

我正在考慮創建一個新的大型C ++項目。 開始很容易-只是一個簡單的窗口,也許是SDL2,也許SFML,甚至是WIN32。 好吧,我該怎么辦? 使用我想要的任何窗口會不會更好? 無需更改大量代碼,以使其他類獨立於此窗口?

說完了! 使用一個簡單的窗口界面,每個類都知道類似窗口的內容,並且我可以在不同類型之間進行選擇。 唯一的要求是將IWindow作為基類。

class IWindow {
  public:
    IWindow(std::string title, int posX, int posY, int width, int height);
    IWindow getHandle();
    void loop();
    bool toggleFullscreen();
    bool toggleFullscreen(bool fullscreen);
    int getWidth();
    int getHeight();
    int getPosX();
    int getPosY();
    //And so on ...
};

但是現在,由於必須使用虛擬方法,因此每次我的虛擬函數循環都會被游戲循環調用。 虛擬功能較慢。 我讀了大約10%。

編譯器不能看到我的窗口嗎? 它來自哪種類型? 難道看不到“ Jeah,這個程序員在此應用程序中創建了一個SDL窗口,因此只需在各處使用它的方法即可。”? 我的意思是,我在主循環中定義了窗口,它永遠不會改變。 沒什么動態的。 這是可以預見的。

那么編譯器是否能夠優化我的可預測虛擬函數調用? 這些將在每個游戲循環周期進行評估嗎? 像下面的文章一樣?

int main(int argc, char* argv[]) {
  //Creates a window derived from IWindow
  SDL::Window myWindow("Title", 0, 0, 300, 100);
  //Storing it as IWindow in a wrapper class
  Game myGame(&myWindow);
  //Game loop
  //myGame.run() calls the window's loop
  while (myGame.run()) {
    //... doing game stuff
  }
}

使用這樣的Game類:

class Game {
  protected:
    IWindow* window;
  public:
    bool run() {
      //Calls the window's virtual loop method.
      //Will it be optimized? Any way to do so?
      this->window->loop();
    }
};

聽到您的想法和經驗會很高興。

達斯·月亮

C ++編譯器是否優化虛擬成員調用?

是的,如果編譯器可以在編譯時確定具體類型,則它可能可以取消虛擬函數調用的虛擬化。

不可以,C ++編譯器將無法取消所有虛擬函數調用的虛擬化。

虛擬功能較慢。 大約10%

假設10%的差異是正確的,請考慮函數調用開銷可能在幾納秒的數量級。 幾納秒的10%並不多。 您可以在像游戲這樣的軟實時仿真的單個迭代中容納許多納秒。

編譯器不能看到我的窗口嗎?

那么編譯器是否能夠優化我的可預測虛擬函數調用?

也許。

首先, 必須在分配指針的上下文中內聯擴展run調用。 否則,它不能對指向的對象做任何假設。 為了進行內聯擴展,必須在與調用該函數的位置相同的轉換單元中對其進行定義(LTO可能能夠解除此要求)。

此外,編譯器必須能夠證明在執行過程中的任何時候都沒有修改window以指向另一個對象。 取決於循環的樣子,這種證明可能是不可能的,但是有一種簡單的方法可以簡化這一過程:聲明指針const。

至於您的編譯器是否進行了優化……我不知道。 但是您的編譯器可以,所以我建議您直接向您的編譯器提問(即要求它編譯程序並查看其作用)。

讓我們總結一下我們的評論。

虛擬調用的成本很高,但是如果處理器能夠檢測到模式,則由於現代處理器內部的預測器,虛擬調用的成本會降低。

現在,讓我們檢查您的代碼:

int main(int argc, char* argv[]) {
  //Creates a window derived from IWindow
  SDL::Window myWindow("Title", 0, 0, 300, 100);
  //Storing it as IWindow in a wrapper class
  Game myGame(&myWindow);
  //Game loop
  //myGame.run() calls the window's loop
  while (myGame.run()) {
    //... doing game stuff
  }
}

假設Game進行了虛擬run 在這種情況下,編譯器知道myGameGame類型的,可以直接將調用放入您的run函數中,而不必通過虛擬表。

現在,您在另一個文件中擁有此文件:

class Game {
  protected:
    IWindow* window;
  public:
    bool run() {
      //Calls the window's virtual loop method.
      //Will it be optimized? Any way to do so?
      this->window->loop();
    }
};

不幸的是,在這種情況下,編譯器僅通過查看此文件就無法了解任何內容,因此對SDL::Window的調用將通過IWindow進行的虛擬run

現在有了lto (鏈接時間優化),編譯器也許可以找出並消除代碼的虛擬化,但是可能不會,因為優化選項的數量會隨着文件數量以及組合數量的增加而增加。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM