简单的if语句会降低性能吗?

4
例如,我有一个类。
class Point
{
public:
    float operator[](int i) const
    {
       if (i == 0) return m_x; // simple ifs, performance reduction??
       if (i == 1) return m_y;
       return m_z;
    }

private:
    float m_x;
    float m_y;
    float m_z;
};

与访问std::array<float,3>元素相比,是否存在性能下降? 如果有的话,我该如何消除它。 我想使用字段x、y、z而不是数组。


确实,性能降低相对于什么?条件语句并不是免费的。如果if语句的参数是一个常量表达式,那么它在运行时可能是“免费”的。但这里并非如此。 - Chris Beck
2
你可以打开优化功能进行编译,并查看生成的汇编代码来回答自己的问题。 - PaulMcKenzie
3
你有没有证据表明你所编写的代码性能正在影响你实际的代码?(例如,性能分析工具是否表明这段代码是瓶颈?)如果没有,那么这就是那些明显的过早优化的情况之一。 - Ken White
1
你应该写出最能清晰反映代码意图并且易于理解和维护的代码。在这种情况下不应考虑性能。 - David Schwartz
显示剩余7条评论
3个回答

3
有性能损失吗?
我猜你的意思是“与数组查找相比”。如果是这样,答案是可能的。任何分支操作都可能导致流水线停顿(如果CPU错误地预测了哪个分支将被取),这可能会使事情变慢。现在的CPU分支预测非常好,所以在实际生活中可能不是问题 - 这将取决于调用此代码的程序的使用模式。
如果是这样,我该如何消除它。我想使用x、y、z字段而不是一个数组。
您可以通过使用三个项目的数组来删除if语句。如果您不喜欢将项目作为数组访问,您始终可以添加访问器方法,使数组再次看起来像单独的项目:
class Point 
{
public:
   [...]

   float & m_x() {return m_array[0];}
   float & m_y() {return m_array[1];}
   float & m_z() {return m_array[2];}

private:
   float m_array[3];
};

[...]

myPoint.m_x() = 5;
myPoint.m_y() = myPoint.m_x() + myPoint.m_z();
[etc]

访问 m_array[i] 比访问 m_x 更消耗 CPU 时间吗? - Tian Xiao
不,在现代CPU上不行。 - Jeremy Friesner
@JeremyFriesner:我认为从技术上讲可能是这样的。因为当您使用m_x时,指针算术可以在编译时完成,而使用m_array[i]时则不行。但这可能不会引起注意。 - Chris Beck
@ChrisBeck 但是上面的 i 就像 1 一样是一个 constexpr,所以算术运算可以在静态时完成。 - chi
@chi: 这清楚吗?我认为可能是,也可能不是。 - Chris Beck

1

可以将数据存储在数组中,而不是作为单独的成员进行存储。然后在成员的getter函数中使用数组索引。

class Point
{
public:
    float operator[](int i)
    {
       return coords[i]
    }
    float getX() {
        return coords[0];
    }
    float getY() {
        return coords[1];
    }
    float getZ() {
        return coords[2];
    }
    float setX(float val) {
        return coords[0] = val;
    }
    float setY(float val) {
        return coords[1] = val;
    }
    float setZ(float val) {
        return coords[2] = val;
    }

private:
    float coords[3];
};

你不能只使用一个普通的引用,例如 float &m_x; 并将其别名为 coords[0] 吗? - melpomene
我不喜欢那样直接访问内部表达的想法。如果你决定改变它的存储方式怎么办? - Barmar
@melpomene 注意到原始代码中成员变量被声明为私有的,因此意图是隐藏表示。 - Barmar
添加引用对象的一个缺点是它会使您的Point对象的大小增加一倍以上(如果有很多这样的对象,则可能不希望这样)。另一个缺点是它可能会使访问对象变得更加昂贵(因为它们可能需要解析一层间接性)。使用方法可以避免这些问题。 - Jeremy Friesner

-3

正如其他人所指出的那样,您的语法不正确。但是为了回答您的问题,您可以按照以下方式进行:

float operator[](int i) {
    return *(&m_x + i);
}

地址运算符加上索引,然后解除指针引用。

这比两个if语句的性能要好。当然,你必须小心,只提供有效的i值(从0到2)。

这也假设你的成员已经正确对齐,并且没有使用任何特定的编译器功能。当然,你需要了解内存的存储方式,但这绝对是最优的,因为通常认为算术操作由于处理器的设计而比条件操作更快。


@JeremyFriesner 谢谢。现在已经指定。 - Mike Weir
1
@MikeWeir 你错过了重点。此外,“这样做会更好”的说法需要引用证据。 - melpomene
@JeremyFriesner 我理解这种方法存在理论上的问题。然而,我使用过的每个C++编译器都经常采用这种方法。在什么情况下你会有一个编译器不会对其进行对齐呢?唯一的方法是强制使用编译器自定义成员填充,这不是我在这里建议的。从实际角度来看,这种解决方案是最优的,而且通常认为算术比条件更快。 - Mike Weir
1
他并没有“严重错误”的,因为如果你依赖于未定义行为,那么你就会面临问题。如果你为自己的编译器编写一个实现,并能够保证代码能够正常工作,那你可能没问题。但在其他任何情况下都没有任何保证。你的论点是无效的。这就像说:“在我去过的所有大型连锁店中,我偷了一双袜子,但从未被抓住,所以你偷袜子不会被抓。” - Pixelchemist
@Pixelchemist 成员变量在任何情况下都不会被编译器缓存,除非你无法以这种方式进行别名。对于许多优化来说,以可预测的方式使用内存非常重要。你基本上在争论C++中的内存技巧永远不应该被使用,因为你可能有一个会让你失望的编译器。实际上,这种解决方案在游戏/物理编程中广泛使用,你过于苛求了。 - Mike Weir
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接