避免检查可能的如果
Avoiding Checking likely if
考虑到以下内容:
class ReadWrite {
public:
int Read(size_t address);
void Write(size_t address, int val);
private:
std::map<size_t, int> db;
}
在读取函数中,当访问之前没有写入的地址时,我想抛出异常指定此类错误或允许并返回0,换句话说,我想使用std::map<size_t, int>::operator[]()
或std::map<size_t, int>::at()
,这取决于用户可以设置的一些bool值。所以我添加了以下内容:
class ReadWrite {
public:
int Read(size_t add) { if (allow) return db[add]; return db.at(add);}
void Write(size_t add, int val) { db[add] = val; }
void Allow() { allow = true; }
private:
bool allow = false;
std::map<size_t, int> db;
}
问题是:通常,程序将在程序开始时调用allow或none,然后进行多次访问。所以,性能方面,这段代码很糟糕,因为它每次执行检查if (allow)
,通常它要么总是为真,要么总是为假。那么如何解决这个问题呢?
虽然这个类的描述用例(一个或没有Allow()
)很可能是不确定的,所以我必须允许用户动态调用Allow()
。
另一个编辑:
使用函数指针的解决方案:使用不能由编译器内联的函数指针所产生的性能开销是什么?如果我们使用std::function
代替会解决问题吗?
通常情况下,程序将在对象处调用allow或none程序的开始和之后的许多访问。所以,性能方面,这段代码很糟糕,因为它每次都执行检查if (allow)在通常为true或always的地方假的。那么如何解决这个问题呢?
我不会,中央处理器会。
分支预测将计算出答案很可能在很长一段时间内是相同的,因此它将能够在硬件级别上非常优化分支。它仍然会产生一些开销,但是可以忽略不计。
如果你真的需要优化你的程序,我认为你最好使用std::unordered_map
而不是std::map
,或者转向一些更快的映射实现,比如google::dense_hash_map
。与地图查找相比,分支是微不足道的。
如果想要减少时间成本,就必须增加内存成本。接受这一点,您可以使用函数指针来实现这一点。下面是我的答案:
class ReadWrite {
public:
void Write(size_t add, int val) { db[add] = val; }
// when allowed, make the function pointer point to read2
void Allow() { Read = &ReadWrite::read2;}
//function pointer that points to read1 by default
int (ReadWrite::*Read)(size_t) = &ReadWrite::read1;
private:
int read1(size_t add){return db.at(add);}
int read2(size_t add) {return db[add];}
std::map<size_t, int> db;
};
函数指针可以作为其他成员函数调用。例如:
ReadWrite rwObject;
//some code here
//...
rwObject.Read(5); //use of function pointer
//
请注意,非静态数据成员初始化在c++11中可用,因此int (ReadWrite::*Read)(size_t) = &ReadWrite::read1;
可能无法在旧版本中编译。在这种情况下,必须显式声明一个构造函数,在该构造函数中可以完成函数指针的初始化。
可以使用指针来实现函数
class ReadWrite {
public:
void Write(size_t add, int val) { db[add] = val; }
int Read(size_t add) { (this->*Rfunc)(add); }
void Allow() { Rfunc = &ReadWrite::Read2; }
private:
std::map<size_t, int> db;
int Read1(size_t add) { return db.at(add); }
int Read2(size_t add) { return db[add]; }
int (ReadWrite::*Rfunc)(size_t) = &ReadWrite::Read1;
}
如果你想要运行时动态行为,你必须在运行时(在你想要你的逻辑动态行为的时候)为它付费。
你希望在调用Read
时根据运行时条件有不同的行为,你必须检查那个条件。无论您的overhad是函数指针调用还是分支,您都会发现在客户端代码调用Read
时,根据allow
,您的程序中会跳转或调用到不同的位置。
注意:分析和修复真正的瓶颈——而不是可疑的瓶颈。(如果你能通过证实你的怀疑或者找出为什么你对业绩的假设是错误的,你会学到更多。)
- 检查数组中是否有字符串中的值,如果没有,则添加它
- 如果 constexpr语言 - 为什么完全检查丢弃的语句?
- 检查私有成员变量是否在一定范围内,如果没有调整
- 有没有办法搜索向量的元素,<String>然后检查它是否包含特定的字符,如果它确实打印了它
- 如果是数字或字符,请检查输入
- 如果 BGL 中的add_vertex则检查顶点是否存在
- 如果检查不起作用,则 C++
- 如果字符串向量包含字符'p',如何检查 c++
- 如果结果为假,再次检查如何导致更多错误的结果?
- 如果方法不进行类型检查,为什么C++模板匹配?
- 如果元素的数量已经知道,检查数组或向量中的所有元素是否相等更好?
- 如果检查和内联条件之间是否存在编译器差异
- 如果条件检查,多个向量大小相等
- 使用C++代码检查操作系统是否可用(如果可用,是否打开/关闭)
- 使用未来wait_for时无法检查成员变量,但是如果我睡在线程中,它可以工作
- c++ 检查 Unix 中是否存在目录,如果存在,则调用 void 函数
- 检查 int 的输入,如果是字符,请再次请求输入(Turbo C++)
- 这是检查 const 函数(如果是否创建某些内容)的更好方法
- 如果 constexpr 和需要表达式进行临时概念检查
- 检查单词中的字母是否在另一个数组中,如果全部匹配,则返回true