【发布时间】:2015-02-10 19:10:04
【问题描述】:
我有一个数字运算程序,我想求解的方程由不同类的成员函数表示。当然,每个类都有几个成员变量,它们是方程的输入。成员 vars 目前是像 double 和 int 这样的原语,但为了更好地与 GUI 集成,我想用托管变量替换原语;即我想使用一个单独的类来保存变量的名称和值,并处理读取和写入它的值。我担心代码的性能和可读性。例如,我宁愿看到像 x = y + 2 这样“自然”的代码,而不是 x.set_value(y.get_value() + 2)。
我想出了四种不同的方法,并试验了每种方法需要多少时间(代码如下)。我使用调试版本使用 MSVC 2013 编译了它。我在发布模式下得到了无意义的结果,因为我认为我的循环被优化掉了。结果似乎很重要;使用原语或直接访问成员变量需要使用 getter/setter 函数或强制转换运算符重载的一半时间。
我的问题是:我是否适当地测试了这些不同的方法?有没有更好的方法来做我想做的事情?谢谢。
#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;
}
【问题讨论】:
-
我认为我的循环得到了优化 ...请参阅:Loop with a zero execution time
-
你的直觉是正确的,调用函数有开销。如果你真的很关心性能,直接访问和修改成员变量比使用函数更快。但是请注意,几乎所有编译器都对 getter 和 setter 进行了大量优化
-
@Cyber:运行时函数调用有开销,但源代码中的函数调用并不意味着运行时调用。毕竟,这就是内联的重点。 处理无意义的基准测试结果的正确方法不是禁用优化,而是修复基准测试。
-
如果这些调用存在于您实际要运行的已编译代码中的最终确定性中,则 可能 会产生开销 run (我必须假设不是 debug 代码)。非虚拟 const-getter 很容易成为编译器最常进行的内联优化之一。修复您的发布代码,使其有意义并正确运行。
-
@BenVoigt 我同意,并不是要提出其他建议(如果我这样做了!)。如果您担心优化代码的性能,您应该针对优化代码进行分析,这是毫无疑问的。
标签: c++