简体   繁体   English

访问者是否会影响应用程序的性能?

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

I was wondering if the use of accessors can significantly affect performance of an application. 我想知道访问器的使用是否会显着影响应用程序的性能。 Let's say we have a class Point and there are two private fields. 假设我们有一个Point类,有两个私有字段。 We can get access to these fields by calling public functions such as GetX(). 我们可以通过调用GetX()等公共函数来访问这些字段。

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

    ~Point(void);

private:
    double x,y;
};

However if we need to get the value of field xa lot of time (eg if we process images) wouldn't this construction affect the performance of application? 但是,如果我们需要获得字段xa的大量时间值(例如,如果我们处理图像),这种结构是否会影响应用程序的性能? Maybe it would be faster just to make fields x and y public? 也许将字段x和y公开会更快?

First and foremost, this is probably premature optimization , and in the general case accessors are not the source of application-level bottlenecks. 首先, 这可能是过早的优化 ,并且在一般情况下,访问者不是应用程序级瓶颈的来源。 However, they're not magic pixie dust. 然而,他们并不是神奇的小精灵尘埃。 It's generally not the case that accessors will hurt performance. 一般情况下,访问者不会损害性能。 There are a few things to consider: 有几点需要考虑:

If the implementation is inline or if you have a toolchain that supports link-time optimization, it's likely that there will be 0 impact. 如果实现是inline或者如果您有一个支持链接时优化的工具链,则可能会产生0影响。 Here's an example that lets you get absolutely the same performance on a compiler that doesn't suck. 下面是一个可以让你获得不吸编译器绝对相同的性能的例子。

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

If the implementation is out-of-line, then you have the added cost of a function call. 如果实现是脱节的,那么您将增加函数调用的成本。 If, as you say, the function is being called many times, then at least the code is more or less guaranteed to be in the cache, but the relative % of overhead may be high: the work to perform the function call is higher than the work of moving a double around, and there's a pointer indirection because the function actually uses this as a parameter. 如果,如你所说,函数被多次调用,那么至少代码或多或少保证在缓存中,但开销的相对百分比可能很高:执行函数调用的工作高于移动一个double的工作,并且有一个指针间接,因为该函数实际上使用this作为参数。

If the implementation is both out-of-line and part of a relocatable library (Linux *.so or Windows *.dll), there's an additional indirection that occurs in order to manage the relocation. 如果实现既是脱节的是可重定位库(Linux * .so或Windows * .dll)的一部分,那么为了管理重定位,还会出现一个额外的间接。

Both of the latter costs are reduced on x86-64 hardware relative to x86 32-bit; 相对于x86 32位,x86-64硬件上的后两种成本都降低了; so much so that you should just not worry about it. 这么多,你不应该担心它。 I can't speak about other architectures. 我不能谈论其他架构。

Penultimately, if you have many trivial objects with trivial getters and setters, and if you have no profile-guided optimization or link-time optimization, there may be caching effects due to large numbers of tiny functions. 倒数第二,如果你有许多具有琐碎的getter和setter的琐碎对象,并且如果你没有配置文件引导的优化或链接时优化,由于大量的微小函数可能会有缓存效果。 It's likely that each function requires a minimum of one cache line, and the functions are not going to be naturally organized in a way that groups commonly-used sections together. 每个函数可能需要至少一个缓存行,并且这些函数不会以将常用部分组合在一起的方式自然地组织。 This cost is something you should probably ignore unless you're writing a very large-scale C++ project or core component, such as the KDE base system. 除非你正在编写一个非常大规模的C ++项目或核心组件,例如KDE基础系统,否则这个成本是你应该忽略的

Ultimately, don't worry about it. 最终,不要担心。

Such methods should always be inlined by the compiler and the performance of that will be identical to making them public. 这些方法应始终由编译器内联,并且其性能与将其公开相同。 You can use the inline keyword to help the compiler along, but that's just a hint. 您可以使用inline关键字来帮助编译器,但这只是一个提示。 If it's really critical that you avoid function call overhead, read the generated assembly. 如果避免函数调用开销非常重要,请阅读生成的程序集。 If they're getting inlined you're ok. 如果他们被内联你就可以了。 Otherwise you might want to consider loosening their visibility. 否则,您可能需要考虑放松其可见性。

In a typical case, no, there will not be a difference in performance (unless you've fairly specifically told the compiler not to inline any functions). 在一个典型的情况下,不,性能没有差别(除非你公平地告诉编译器不要内联任何函数)。 If you allow it to inline functions, however, chances are that it'll generate identical assembly language for both. 但是,如果允许它内联函数,则可能会为两者生成相同的汇编语言。

That should not , however, be seen as an excuse for ruining your design by including these abominations. 这应该不是 ,但是,被看作是通过包括这些可憎毁了你的设计的借口。 First of all, a class should generally provide high level operations, so (for example) you could have a move_relative and move_absolute , so instead of something like this: 首先,一个类通常应该提供高级操作,所以(例如)你可以有一个move_relativemove_absolute ,所以不要这样:

Point whatever;

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

...you'd do something like this: ......你会做这样的事情:

Point whatever;

whatever.move_relative(3, 4);

There are times, however, that exposing something as data really does make sense and work well. 然而,有时候,将数据作为数据公开确实有意义并且运作良好。 If/when you are going to do that, C++ already provides a good way to encapsulate access to the data: a class. 如果/当你打算这样做时,C ++已经提供了一种封装对数据的访问的好方法:一个类。 It also provides a predefined name for SetXXX and GetXXX -- they're operator= and operator T respectively. 它还为SetXXXGetXXX提供了一个预定义的名称 - 它们分别是operator=operator T The right way to do this is something like this: 正确的方法是这样的:

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; }
};

Using this, your Point class looks like: 使用它,您的Point类看起来像:

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

With this, the data you want to be public looks and acts as if it is. 有了这个,您想要公开的数据看起来就像是一样。 At the same time, you retain full control over getting/setting the values by changing the encapsulate to something that does whatever you need done. 同时,通过将encapsulate更改为执行任何操作所需的操作,您可以完全控制获取/设置值。

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

Though I haven't bothered to in the demo template above, it's fairly easy to support the normal compound assignment operators ( += , -= , *= , /= , etc.) as well. 虽然我没有在上面的演示模板中烦恼,但是支持普通的复合赋值运算符( +=-=*=/=等)也相当容易。 Depending on the situation, it's often useful to eliminate many of these though. 根据具体情况,消除其中许多因素通常很有用。 Just for example, adding/subtracting to an X/Y coordinate often makes sense -- but multiplication and division frequently won't, so you can just add += and -= , and if somebody accidentally types in /= or |= (for just a couple of examples), their code simply won't compile. 例如,添加/减去X / Y坐标通常是有意义的 - 但乘法和除法经常不会,所以你可以添加+=-= ,如果有人不小心输入/=|= (仅举几例),他们的代码根本无法编译。

This also provides better enforcement of whatever constraints you need on the data. 这还可以更好地执行您对数据所需的任何约束。 With private data and an accessor/mutator, other code in the class can (and almost inevitably will) modify the data in ways you didn't want. 使用私有数据和访问器/更改器,类中的其他代码可以(并且几乎不可避免地)以您不想要的方式修改数据。 With a class dedicated to nothing by enforcing the correct constraints, that issue is virtually eliminated. 通过强制执行正确的约束,没有专门致力于什么的类,这个问题几乎被消除了。 Instead, code both inside and outside the class does a simple assignment (or uses the value, as the case may be) and it's routed through the operator= / operator T automatically -- code inside the class can't bypass whatever checking is needed. 相反,类内外的代码执行简单的赋值(或使用值,视情况而定)并且它通过operator= / operator T自动路由 - 类中的代码无法绕过任何需要的检查。

Since you're (apparently) concerned with efficiency, I'll add that this won't normally have any run-time cost either. 既然你(显然)关注效率,我会补充一点,这通常也不会有任何运行时成本。 In fact, being a template gives it a slight advantage in that regard. 事实上,作为一个模板在这方面给它一点点优势。 Where code in a normal function could (even if only by accident) be rewritten in a way that prevented inline expansion, using a template eliminates that -- if you try to rewrite it in a way that otherwise wouldn't generate inline code, with a template it won't compile at all. 正常函数中的代码可以 (尽管只是偶然)以防止内联扩展的方式重写,使用模板消除了 - 如果您尝试以不会生成内联代码的方式重写它,它根本不会编译的模板。

As long as you define the functions in the header so the compiler can inline them there should be no difference at all. 只要您在头文件中定义函数,以便编译器可以内联它们就应该没有任何区别。 But even if they aren't inlined you still shouldn't make them public unless profiling indicates that it's a significant bottleneck and that making the variables public improves the problem. 但即使它们没有内联,你仍然不应该公开它们,除非分析表明它是一个重要的瓶颈并且公开变量可以改善这个问题。 Making variables public decreases encapsulation and maintainability. 公开变量会降低封装和可维护性。 For a bit more on public variables, see my answer on What good are public variables then? 有关公共变量的更多信息,请参阅我的答案, 那么公共变量有什么用呢?

The short answer is yes, this will affect the performance. 简短的回答是肯定的,这会影响性能。 Whether you will notice the difference or not is another matter that depends on how much code you have in the accessors, among other things. 您是否会注意到差异是另一个问题,取决于您在访问器中有多少代码,以及其他内容。

The more important questions, though, is do you need what you gain from using accessors? 但是,更重要的问题是,您是否需要使用访问器获得的收益? If you make the fields public, then you lose control over their values. 如果将字段设为公共字段,则会失去对其值的控制权。 Do you want to allow x or y to be NaN? 你想让x或y成为NaN吗? or +-infinity? 还是+ -infinity? Making them public would make such cases possible. 将它们公之于众将使这种情况成为可能。

If you decide later that a double is not acceptable for your point class (maybe you need more precision or the precision isn't necessary), then accessing the fields directly would cause trouble. 如果你稍后决定你的点类不接受double(可能你需要更高的精度或者不需要精度),那么直接访问这些字段会带来麻烦。 While this change might also require changes in the accessors, the setters should be fine with overloaded methods. 虽然此更改可能还需要更改访问器,但设置者应该可以使用重载方法。 And you may still be fine with a public representation of a double whereas the internal representation is not a double (although this is not so likely with a Point class, I imagine). 你可能仍然可以使用双重的公开表示,而内部表示不是双重的(尽管对于Point类来说这不太可能,我想)。

There are other cases where you might want to have side effects on accessors and setters as well that making the fields public would circumvent. 在其他情况下,您可能希望对访问者和设置者产生副作用,使公共领域可以避免。 Maybe you want to create events for when your point changes, but if the fields are public, then your class won't know when the values change. 也许你想为你的点改变时创建事件,但是如果字段是公共的,那么你的类将不知道值何时改变。

ADDED 添加

Ok, so my glossing over with my "yes" so that I could get to the non-performance issues that I felt more important wasn't appreciated. 好吧,所以我对我的“是”进行了掩饰,以便我能够解决我觉得更重要的非性能问题,但我并不感激。

In many cases, the yes is probably as correct as it will be imperceptible. 在许多情况下,“是”可能是正确的,因为它是不可察觉的。 True, using inline and a kick-ass compiler may very well end up with the same code (assuming an accessor like double GetX() { return x; } ), but there are a lot of ifs there. 没错,使用内联和kick-ass编译器可能最终得到相同的代码(假设一个访问器,如double GetX() { return x; } ),但那里有很多ifs。 Compilers will only inline things that end up in the same object file (often created from a single code file). 编译器只会内联最终位于同一目标文件中的内容(通常是从单个代码文件创建的)。 So you also need a kick-ass linker to optimize the references in other object files (by the time you get to the linker, the inline hint may not even still remain in the code). 因此,您还需要一个kick-ass链接器来优化其他目标文件中的引用(当您到达链接器时,内联提示可能仍然不会保留在代码中)。 So some, but not necessarily all, of the code may end up being identical, but that would be something you can confirm only after the fact and isn't useful. 因此,一些(但不一定是全部)代码可能最终会完全相同,但这只是事后才能确认并且没有用。

If you're concerned about image processing then it might be worth allowing for friend classes so that an image class that you code can have access directly to the fields, but again I don't think that even in that case the accessor will be adding a lot to your runtime. 如果您关注图像处理,那么可能值得允许朋友类,以便您编写的图像类可以直接访问字段,但我再也不认为即使在这种情况下访问者也会添加你的运行时很多。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM