簡體   English   中英

訪問者是否會影響應用程序的性能?

[英]Do the accessors affect the performance of an application?

我想知道訪問器的使用是否會顯着影響應用程序的性能。 假設我們有一個Point類,有兩個私有字段。 我們可以通過調用GetX()等公共函數來訪問這些字段。

class Point
{
public:
    Point(void);
    double GetX();
    double GetY();
    void SetX(double x);
    void SetY(double y);

    ~Point(void);

private:
    double x,y;
};

但是,如果我們需要獲得字段xa的大量時間值(例如,如果我們處理圖像),這種結構是否會影響應用程序的性能? 也許將字段x和y公開會更快?

首先, 這可能是過早的優化 ,並且在一般情況下,訪問者不是應用程序級瓶頸的來源。 然而,他們並不是神奇的小精靈塵埃。 一般情況下,訪問者不會損害性能。 有幾點需要考慮:

如果實現是inline或者如果您有一個支持鏈接時優化的工具鏈,則可能會產生0影響。 下面是一個可以讓你獲得不吸編譯器絕對相同的性能的例子。

class Point {
    public: double GetX() const;
    private: double x;
};
inline double Point::GetX() const { return x; }

如果實現是脫節的,那么您將增加函數調用的成本。 如果,如你所說,函數被多次調用,那么至少代碼或多或少保證在緩存中,但開銷的相對百分比可能很高:執行函數調用的工作高於移動一個double的工作,並且有一個指針間接,因為該函數實際上使用this作為參數。

如果實現既是脫節的是可重定位庫(Linux * .so或Windows * .dll)的一部分,那么為了管理重定位,還會出現一個額外的間接。

相對於x86 32位,x86-64硬件上的后兩種成本都降低了; 這么多,你不應該擔心它。 我不能談論其他架構。

倒數第二,如果你有許多具有瑣碎的getter和setter的瑣碎對象,並且如果你沒有配置文件引導的優化或鏈接時優化,由於大量的微小函數可能會有緩存效果。 每個函數可能需要至少一個緩存行,並且這些函數不會以將常用部分組合在一起的方式自然地組織。 除非你正在編寫一個非常大規模的C ++項目或核心組件,例如KDE基礎系統,否則這個成本是你應該忽略的

最終,不要擔心。

這些方法應始終由編譯器內聯,並且其性能與將其公開相同。 您可以使用inline關鍵字來幫助編譯器,但這只是一個提示。 如果避免函數調用開銷非常重要,請閱讀生成的程序集。 如果他們被內聯你就可以了。 否則,您可能需要考慮放松其可見性。

在一個典型的情況下,不,性能沒有差別(除非你公平地告訴編譯器不要內聯任何函數)。 但是,如果允許它內聯函數,則可能會為兩者生成相同的匯編語言。

這應該不是 ,但是,被看作是通過包括這些可憎毀了你的設計的借口。 首先,一個類通常應該提供高級操作,所以(例如)你可以有一個move_relativemove_absolute ,所以不要這樣:

Point whatever;

whatever.SetX(GetX()+3);
whatever.SetY(GetY()+4);

......你會做這樣的事情:

Point whatever;

whatever.move_relative(3, 4);

然而,有時候,將數據作為數據公開確實有意義並且運作良好。 如果/當你打算這樣做時,C ++已經提供了一種封裝對數據的訪問的好方法:一個類。 它還為SetXXXGetXXX提供了一個預定義的名稱 - 它們分別是operator=operator T 正確的方法是這樣的:

template <class T>
class encapsulate {
    T value;
public:
    encapsulate(T const &t) : value(t) {}
    encapsulate &operator=(encapsulate const &t) { value = t.value; }
    operator T() { return value; }
};

使用它,您的Point類看起來像:

struct Point { 
    encapsulate<double> x, y;
};

有了這個,您想要公開的數據看起來就像是一樣。 同時,通過將encapsulate更改為執行任何操作所需的操作,您可以完全控制獲取/設置值。

Point whatever;
whatever.x = whatever.x + 3;
whatever.y = whatever.y + 4;

雖然我沒有在上面的演示模板中煩惱,但是支持普通的復合賦值運算符( +=-=*=/=等)也相當容易。 根據具體情況,消除其中許多因素通常很有用。 例如,添加/減去X / Y坐標通常是有意義的 - 但乘法和除法經常不會,所以你可以添加+=-= ,如果有人不小心輸入/=|= (僅舉幾例),他們的代碼根本無法編譯。

這還可以更好地執行您對數據所需的任何約束。 使用私有數據和訪問器/更改器,類中的其他代碼可以(並且幾乎不可避免地)以您不想要的方式修改數據。 通過強制執行正確的約束,沒有專門致力於什么的類,這個問題幾乎被消除了。 相反,類內外的代碼執行簡單的賦值(或使用值,視情況而定)並且它通過operator= / operator T自動路由 - 類中的代碼無法繞過任何需要的檢查。

既然你(顯然)關注效率,我會補充一點,這通常也不會有任何運行時成本。 事實上,作為一個模板在這方面給它一點點優勢。 正常函數中的代碼可以 (盡管只是偶然)以防止內聯擴展的方式重寫,使用模板消除了 - 如果您嘗試以不會生成內聯代碼的方式重寫它,它根本不會編譯的模板。

只要您在頭文件中定義函數,以便編譯器可以內聯它們就應該沒有任何區別。 但即使它們沒有內聯,你仍然不應該公開它們,除非分析表明它是一個重要的瓶頸並且公開變量可以改善這個問題。 公開變量會降低封裝和可維護性。 有關公共變量的更多信息,請參閱我的答案, 那么公共變量有什么用呢?

簡短的回答是肯定的,這會影響性能。 您是否會注意到差異是另一個問題,取決於您在訪問器中有多少代碼,以及其他內容。

但是,更重要的問題是,您是否需要使用訪問器獲得的收益? 如果將字段設為公共字段,則會失去對其值的控制權。 你想讓x或y成為NaN嗎? 還是+ -infinity? 將它們公之於眾將使這種情況成為可能。

如果你稍后決定你的點類不接受double(可能你需要更高的精度或者不需要精度),那么直接訪問這些字段會帶來麻煩。 雖然此更改可能還需要更改訪問器,但設置者應該可以使用重載方法。 你可能仍然可以使用雙重的公開表示,而內部表示不是雙重的(盡管對於Point類來說這不太可能,我想)。

在其他情況下,您可能希望對訪問者和設置者產生副作用,使公共領域可以避免。 也許你想為你的點改變時創建事件,但是如果字段是公共的,那么你的類將不知道值何時改變。

添加

好吧,所以我對我的“是”進行了掩飾,以便我能夠解決我覺得更重要的非性能問題,但我並不感激。

在許多情況下,“是”可能是正確的,因為它是不可察覺的。 沒錯,使用內聯和kick-ass編譯器可能最終得到相同的代碼(假設一個訪問器,如double GetX() { return x; } ),但那里有很多ifs。 編譯器只會內聯最終位於同一目標文件中的內容(通常是從單個代碼文件創建的)。 因此,您還需要一個kick-ass鏈接器來優化其他目標文件中的引用(當您到達鏈接器時,內聯提示可能仍然不會保留在代碼中)。 因此,一些(但不一定是全部)代碼可能最終會完全相同,但這只是事后才能確認並且沒有用。

如果您關注圖像處理,那么可能值得允許朋友類,以便您編寫的圖像類可以直接訪問字段,但我再也不認為即使在這種情況下訪問者也會添加你的運行時很多。

暫無
暫無

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

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