C++联合的性能影响

4
Agner Fog的《C++中的软件优化》中指出,union强制变量存储在内存中,即使在其他情况下它本来可以存储在寄存器中,这可能会影响性能。(例如第148页)
我经常看到这样的代码:
struct Vector {
    union {
        struct {
            float x, y, z, w;
        };
        float v[4];
    }
};

这很方便,但现在我想知道是否可能会有性能损失。我编写了一个小基准测试,比较了使用和不使用联合的矢量实现,在某些情况下,没有使用联合的矢量明显表现更好,尽管我不知道我的基准测试有多可信。(我比较了三种实现:联合;x、y、z、w;v[4]。例如,当按值传递时,v[4] 似乎更慢,尽管结构体的大小都相同。)
我的问题是,当编写实际生产代码时,人们是否会考虑这一点?您是否知道有哪些情况是出于这个原因而决定不使用联合的?

1
除非我遇到了严重的性能问题,对我的代码进行了分析并纠正了其他所有问题,最后通过检查编译器生成的汇编代码来确定这个非常具体的点,否则我不会考虑它。 - YSC
4
除编译器优化不好以外,union 通常可以存储在寄存器中,但在这种琐碎的情况下很少发生。此外,您展示的 union 在没有产生未定义行为的情况下是无用的。我建议找一本不同的书 - nwp
5
cppreferene.com“从未被最近写入的联合成员读取是未定义行为。许多编译器提供了一种非标准语言扩展,可以读取联合中未激活的成员。” - François Andrieux
在Agner Fog的《C++软件优化》一书中,提到的内容不在第148页,而是在第153页。也许他更新了这本书? - jg6
2个回答

1
我的问题是,写实际生产代码时,人们是否会考虑这个问题?
不会。这是过早的优化(union 结构本身也是)。一旦代码以相对清晰和可靠的方式编写,就可以进行性能分析并解决真正的瓶颈。没有必要花费5分钟来推测 union 是否会在将来影响性能。它要么会影响,要么不会影响,只有性能分析才能说明。

1
为什么您认为联合结构是过早优化?我一直认为x、y、z访问是一种方便的事情。 - B_old
1
@B_old 取决于你的看法。如果 x、y、z 方便,那为什么不只保留它们呢?但有时候使用数组更方便?但是你可以有一个本地临时引用/指针数组...但这会很慢(如果它编译成真正的临时数组并且一切都是间接的,实际上编译器可能会解决它),所以你将数组直接放入联合定义中=优化(对我个人而言,v[0] 比 x、y、z 更方便,因为我习惯了从石器时代开始没有名称的内存,只有偏移量)。 - Ped7g

1
似乎目标是为向量类型的元素提供友好名称,而使用union并不是最好的方法。评论已经指出了未定义行为,即使它起作用,它也是一种限制优化机会的别名形式。
相反,避免整个混乱,只需添加访问器来命名元素。
struct quaternion
{
    float vec[4];
    float &x() { return vec[0]; }
    float &y() { return vec[1]; }
    float &z() { return vec[2]; }
    float &w() { return vec[3]; }
    const float &x() const { return vec[0]; }
    const float &y() const { return vec[1]; }
    const float &z() const { return vec[2]; }
    const float &w() const { return vec[3]; }
}

事实上,就像Eigen为其四元数实现所做的那样: https://eigen.tuxfamily.org/dox/Quaternion_8h_source.html

它真的限制了优化机会,还是只是像nwp所说的那样,混淆了“糟糕”的编译器问题? - B_old
那些 void 让我困惑了一段时间。 - nwp
2
那些 void 不应该存在。这是 C 的方式来表示没有参数的函数(与任意数量的参数相对)。在 C++ 中,这种语法是有效的,但是冗余和不寻常的。一个空参数列表的函数声明意味着确实没有参数。另外,我强烈建议添加 const 修饰符来进行重载。因为现在的便利访问器对于 const quaternion 是无用的。 - besc
根据评论建议,修复旧的 C 语法并添加 const 重载。 - Peter

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