C++ 访问成员变量时的性能注意事项

C++ 访问成员变量时的性能注意事项,c++,C++,我有一个数字处理程序,其中我要解的方程由不同类的成员函数表示。当然,每个类都有几个成员变量,这些变量是方程的输入。成员变量目前是double和int之类的原语,但是为了更好地与GUI集成,我想用托管变量替换原语;i、 我想使用一个单独的类来保存变量的名称和值,并处理读取和写入它的值。我关心代码的性能和可读性。例如,我宁愿看到像x=y+2这样看起来“自然”的代码,而不是x.set\u value(y.get\u value()+2) 我想出了四种不同的方法,并试验了每种方法所需的时间(代码如下)。

我有一个数字处理程序,其中我要解的方程由不同类的成员函数表示。当然,每个类都有几个成员变量,这些变量是方程的输入。成员变量目前是double和int之类的原语,但是为了更好地与GUI集成,我想用托管变量替换原语;i、 我想使用一个单独的类来保存变量的名称和值,并处理读取和写入它的值。我关心代码的性能和可读性。例如,我宁愿看到像
x=y+2
这样看起来“自然”的代码,而不是
x.set\u value(y.get\u value()+2)

我想出了四种不同的方法,并试验了每种方法所需的时间(代码如下)。我用MSVC 2013编译了这个,使用的是调试版本。我在发布模式下得到了无意义的结果,因为我认为我的循环得到了优化。结果似乎意义重大;使用原语或直接访问成员变量需要使用getter/setter函数或cast运算符重载的一半时间

我的问题是:我是否适当地测试了这些不同的方法?有没有更好的方法来做我想做的事?谢谢

#include <iostream>
#include <chrono>

using namespace std;
using namespace std::chrono;

//Class to manage input parameters
struct Parameter {
    double _value = 0.0;
    double Get_value() const {return _value;}
    void Set_value(double value) {_value = value;}
    operator double(){return _value;}
    void operator=(const double& rhs) {_value = rhs;}
};

int main() {
    const size_t NUM_TESTS = 100;       //Number of tests to run
    const size_t MAX_ITER = 1000000;    //Number of iterations to run in each test
    const double x = 2.71828;           //Variable to read from
    double y = 0;                       //Variable to write to
    Parameter test_parameter;       //managed variable to read/write from/to
    double test_primitive = 0.0;    //primitive variable to read/write from/to
    size_t t_primitive = 0;         //Total time spent on primitive variable (microseconds)
    size_t t_managed_cast = 0;      //Time spent on managed variable using cast and assignment operators
    size_t t_managed_getset = 0;    //Time spent on managed variable using getter/setter functions;
    size_t t_managed_direct = 0;    //Time spent on managed variable using direct access of member var.

    for (size_t n = 0; n < NUM_TESTS; ++n) {
        //Test using a primitive variable.
        auto t0 = high_resolution_clock::now();
        for (size_t i = 0; i < MAX_ITER; ++i) {
            test_primitive = x;
            y = test_primitive;
        }
        auto t1 = high_resolution_clock::now();
        t_primitive += duration_cast<microseconds>(t1-t0).count();

        //Test using a managed variable, using cast operator and assignment operator
        t0 = high_resolution_clock::now();
        for (size_t i = 0; i < MAX_ITER; ++i) {
            test_parameter = x;
            y = test_parameter;
        }
        t1 = high_resolution_clock::now();
        t_managed_cast += duration_cast<microseconds>(t1-t0).count();

        //Test using managed variable, using getter/setter member functions
        t0 = high_resolution_clock::now();
        for (size_t i = 0; i < MAX_ITER; ++i) {
            test_parameter.Set_value(x);
            y = test_parameter.Get_value();
        }
        t1 = high_resolution_clock::now();
        t_managed_getset += duration_cast<microseconds>(t1-t0).count();

        //Test using managed variable, using direct public access
        t0 = high_resolution_clock::now();
        for (size_t i = 0; i < MAX_ITER; ++i) {
            test_parameter._value = x;
            y = test_parameter._value;
        }
        t1 = high_resolution_clock::now();
        t_managed_direct += duration_cast<microseconds>(t1-t0).count();
    }

    cout << "Average time for primitive (microseconds): " << t_primitive / NUM_TESTS << endl;
    cout << "Average time for managed with cast (microseconds): " << t_managed_cast / NUM_TESTS << endl;
    cout << "Average time for managed with get/set (microseconds): " << t_managed_getset / NUM_TESTS << endl;
    cout << "Average time for managed with direct access (microseconds): " << t_managed_direct / NUM_TESTS << endl;

    return 0;
}
#包括
#包括
使用名称空间std;
使用名称空间std::chrono;
//类来管理输入参数
结构参数{
双_值=0.0;
双Get_value()常量{return_value;}
无效设置值(双值){u值=值;}
运算符double(){return_value;}
void运算符=(constdouble&rhs){u值=rhs;}
};
int main(){
const size\u t NUM\u TESTS=100;//要运行的测试数
const size\u t MAX\u ITER=1000000;//每次测试中要运行的迭代次数
const double x=2.71828;//要从中读取的变量
双y=0;//要写入的变量
参数test_Parameter;//要从/写入的托管变量
double test_primitive=0.0;//要从/写入的原语变量
size\u t\u primitive=0;//花费在primitive变量上的总时间(微秒)
size\u t\u managed\u cast=0;//使用cast和赋值运算符在托管变量上花费的时间
size\u t\u managed\u getset=0;//使用getter/setter函数在托管变量上花费的时间;
size\u t\u managed\u direct=0;//使用成员变量的直接访问在托管变量上花费的时间。
对于(大小n=0;n
现在再次启用优化,只需担心启用优化后的时间安排

在这里,我修正了你的基准


现在再次启用优化,只需担心启用优化后的计时问题。

调试版本不会内联这些访问器方法,因此需要更长的时间。发布版本不会出现此问题


尝试使用发布版本,但将变量设置为volatile,我相信这将禁用循环优化。

调试版本不会内联这些访问器方法,因此需要更长的时间。发布版本不会有此问题


尝试使用发布版本,但将变量设置为volatile,我相信这会禁用循环优化。

我认为我的循环会得到优化…请看:您的直觉是正确的,调用函数会有开销。如果您真的关心性能,直接访问和修改成员变量会很快但是请注意,getter和setter,尤其是,几乎所有的compilers@Cyber:运行时函数调用有开销,但源代码中的函数调用并不意味着运行时调用。这毕竟是内联点。处理无意义基准测试结果的正确方法s不是禁用优化,而是修复基准测试。如果这些调用在您实际要运行的编译代码中最终存在(我必须假设这些调用不是调试代码),则可能会产生开销.Non-virtual const getter很容易成为编译器进行的最频繁的内联优化之一。请修复您发布的代码,使其有意义并正确地工作。@BenVoigt我同意,并且无意提出其他建议(如果我这么做了!)。如果你关心优化代码的性能,你应该对照优化代码进行评测,这是毫无疑问的。我认为我的循环得到了优化…请看:你的直觉是正确的,调用fun会有开销
volatile const double x = 2.71828;
volatile double y = 0;
// ^^ VERY IMPORTANT