在setter中验证值是否不同有意义吗?
Does it make sense to verify if values are different in a setter
我记得我在某个地方(可能在Github)看到过这样一个setter中的例子:
void MyClass::setValue(int newValue)
{
if (value != newValue) {
value = newValue;
}
}
对我来说,这没有多大意义,但我想知道它是否能提高性能。
对于scalar types
没有意义,但对于某些user-defined
类型可能有意义(因为type可能非常"大",或者它的赋值运算符可以做一些"困难"的工作)。
指令管道越深(至少在Intel平台上它只会变得越来越深),分支错误预测的代价就越高。
当分支错误预测时,来自错误预测的一些指令路径仍然通过管道移动。所有的工作都在这些上面进行指令被浪费了,因为它们不会被执行分支被正确预测
所以,在代码中添加if
实际上会损害性能。写操作将被L1缓存,可能会缓存很长时间。如果写操作必须是可见的,那么操作就必须是联锁的。
您可以真正判断的唯一方法是通过实际测试不同的替代方案(基准测试和/或分析代码)。不同的编译器,不同的处理器和不同的代码调用它会有很大的不同。
一般来说,对于"简单"的数据类型(int, double, char,指针等),它是没有意义的。它只会使代码对处理器来说更长更复杂[至少如果编译器按照你的要求去做-它可能会意识到"这没有任何意义,让我们删除这个检查-我不会依赖它' -编译器通常比你聪明,但让编译器的生活变得更加困难几乎不会带来更好的代码]。
编辑:另外,只有比较容易比较的东西才有意义。如果在相等的情况下难以比较数据(例如,如果长字符串相等,则需要从两个字符串中读取大量数据[或字符串开头相同,仅在最后几个字符中有所不同])。所以储蓄很少。这同样适用于具有一堆成员的类,这些成员通常几乎都是相同的,但有一两个字段不是,依此类推。另一方面,如果您有一个"客户数据"类,它有一个必须是唯一的整数客户ID,那么只比较客户ID将是"便宜的",但是复制客户姓名、地址、电话号码和客户的其他数据将是昂贵的。[当然,在这种情况下,为什么它不是(智能)指针或引用?]。编辑结束。
如果数据在不同的处理器之间被"共享"(多个线程访问相同的数据),那么它可能会有一点帮助[特别是如果这个值经常被读取,并且经常以与以前相同的值写入]。这是因为从其他处理器的缓存中"踢出"旧值是昂贵的,并且只有在实际更改某些内容时才希望这样做。
当然,只有当你知道自己的代码处于性能热点的前沿时,才有必要担心性能问题。在其他任何地方,使代码尽可能容易读、清晰和简洁总是最好的选择——这通常也会使编译器更能够确定实际发生了什么,并确保最佳的优化结果。
这种模式在Qt中很常见,其中API高度基于信号&槽。此模式有助于在循环连接的情况下避免无限循环。
在不存在信号的情况下,这段代码只会降低性能,正如@remus-rusanu和@mats-petersson所指出的那样。
- 在C++中,使用带有 std::optional 参数的函数<T>来表示可选参数是否有意义?
- API 返回智能指针的 std::optional 以明确指定指针可能为 null 是否有意义?
- 从头开始为应用程序创建 docker 映像是否有意义?
- 插入向量时,使用lambda的返回而不是函数的返回是否有意义?
- 将 final 关键字添加到没有基类(未派生)的类中的虚函数是否有意义
- 左移负整数为零是否有意义?
- 是否有一个上下文表达式`a.b :: c`有意义
- 是否有一些有意义的统计数据来证明保持有符号整数算术溢出未定义是合理的
- 将可选与reference_wrapper相结合是否有意义?
- 函数返回rvalue参考是否有意义
- 将 [[noreturn]] 添加到主函数是否有意义
- 在shared_ptr的自定义删除器中检查 nullptr 是否有意义?
- 拥有一个没有构造函数的类是否有意义
- 这是否有意义,我的计算机只能并行运行4个线程
- constexpr移动构造函数是否有意义
- 为赋值编写伪代码,并希望仔细检查它是否有意义
- 在目标平台上编译 Boost 自己是否有意义
- 多层继承在C 中是否有意义
- 从 STL 容器继承并删除"新"运算符以防止由于缺少虚拟析构函数而导致未定义的行为是否有意义?
- 一元运算符关联是否有意义