优雅而快速的运算符函数cpp



我目前正在开发一个程序,我的目标是使用尽可能少的存储空间,尽可能快地在(一个类的(两个对象之间进行大量操作。

class number 
{
public:
number(int x) :x{x} {};
int x;
// option 1
number operator+(number x)
{
return number(this->x + x.x);
};
// option 2
static void add(number* a, number* b, number* dest)
{
dest->x = a->x + b->x;
};
};
int main()
{
number a(2);
number b(2);
number c(0);
// 4,608e-8 sec
c = a + b;
// 2,318e-8 sec
number::add(&a,&b,&c);
}

我考虑了两种选择:

  1. 使用实际运算符
  2. 使用一个静态函数,将三个变量(包括目的地(作为参数

第一个可读性最好,但大规模使用它可能意味着需要大量空间,因为每次运行都会初始化一个新对象。

我可能已经用选项2解决了这个问题。通过获取指向目的地的指针,从而重用存储空间。选项2读起来有点笨重,如果有更多的操作相继发生,代码可能很难理解。

我已经进行了几次速度和空间测试。使用实际的操作员功能每次运行需要4,6e-8秒,存储量为920kb。空隙大小分别为2,3e-8和915kb。

我有什么选择不见了吗?如果不是,在存储空间、速度和可读性之间,哪一个是更好的折衷?

;正确的";担心性能的方法是启用编译器优化。大多数时候就是这样。

编写正确可读的代码。一旦你有了它,你就可以衡量它。如果你发现一段代码很贵,你可以看看生成的程序集,了解它为什么很贵。

将两个整数相加,最简洁的是:

int main() {
return 2+2;
}

编译器输出(带有-O3的gcc 9.2(:

main:
mov     eax, 4
ret

现在,您可能想要将整数封装到一个类中:

struct number {
int x;
};
int main() {
return number{2}.x+number{2}.x;
}

这增加了创建类实例和访问其成员的成本。编译器输出:

main:
mov     eax, 4
ret

您可以使用operator+:,而不是直接使用内置的+

struct number {
int x;
number operator+(const number& other){
return {x+ other.x};
}
};
int main() {
return (number{2}+number{2}).x;
}

这增加了呼叫运营商的成本。编译器输出:

main:
mov     eax, 4
ret

你的静态方法版本(修改最少(是这样的:

struct number {
int x;
static void add(number* a, number* b, number* dest) {
dest->x = a->x + b->x;
};
};
int main()
{
number a{2};
number b{2};
number c{0};
number::add(&a,&b,&c);
return c.x;
}

这增加了呼叫者必须"呼叫"的成本;准备";返回值,因为它是一个outparameter。它通过指针增加了间接寻址的成本。它增加了使用使类膨胀的static方法的成本。编译器输出:

main:
mov     eax, 4
ret

结论:不要过早地进行优化。在上述情况下,您所支付的所有成本都在您的代码中,对生成的程序集没有影响。编写可读且简单的代码,并将mirco优化留给编译器。如果您已经编写了正确的代码,并对其进行了测量,然后意识到存在瓶颈,那么您当然可以尝试改进该部分。不过,在做这些之前,尝试为最通用的用例改进number是徒劳的。编译器在这方面做得更好。

PS关于你的数字(4608e-8秒vs 2318e-8秒钟(,我不得不承认我忽略了它们。e-8秒太小,意义不大。同样,对于基准测试,你需要分享基准测试的确切代码,你使用的编译器的详细信息和选项,以及你在什么硬件上运行它。坦率地说,没有这些细节,数字就没有意义。

相关内容

  • 没有找到相关文章

最新更新