鉴于以下内容:
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[]()
或{{ 1}},取决于用户可以设置的一些bool值。所以我添加以下内容:
std::map<size_t, int>::at()
问题是:
通常,程序将在程序开始时调用allow或none,然后进行多次访问。因此,性能方面,这段代码很糟糕,因为它每次执行检查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;
}
,通常它总是为真或总是为假。
那你怎么解决这个问题呢?
修改
虽然这个类的描述用例(一个或没有if (allow)
)很可能是不确定的,所以我必须允许用户动态调用Allow()
。
另一个编辑:
使用函数指针的解决方案:使用无法由编译器内联的函数指针会导致性能开销怎么办?如果我们使用Allow()
而不是解决问题?
答案 0 :(得分:1)
通常情况下,程序会有一次允许或者一次调用 程序的开始,然后很多访问。所以, 性能方面,这段代码很糟糕,因为它每次都会执行 检查是否(允许)通常它总是正确或总是 假。那你怎么解决这个问题呢?
我不会,CPU会 Branch Prediction会发现答案很可能在很长一段时间内都是相同的,所以它能够非常优化硬件级别的分支。它仍然会产生一些开销,但是可以忽略不计。
如果您确实需要优化您的计划,我认为您最好使用std::unordered_map
代替std::map
,或者转移到更快的地图实施,例如google::dense_hash_map
。与地图查找相比,该分支无关紧要。
答案 1 :(得分:0)
如果您想减少时间成本,则必须增加内存成本。接受这一点,您可以使用函数指针执行此操作。以下是我的回答:
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;
可能无法使用旧版本进行编译。在这种情况下,您必须显式声明一个构造函数,其中可以完成函数指针的初始化。
答案 2 :(得分:0)
您可以使用指针进行操作。
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;
}
答案 3 :(得分:0)
如果您想要运行时动态行为,您必须在运行时(在您希望逻辑动态运行的那一点)为它付费。
根据运行时条件,您需要在调用Read
的位置执行不同的行为,并且您必须检查该条件。
无论你的overhad是函数指针调用还是分支,你都会发现跳转或调用程序中的不同位置,具体取决于客户端调用allow
点Read
代码。
注意:描述和修复真正的瓶颈 - 不是可疑的瓶颈。 (如果您通过确认您的怀疑或找出您对性能的假设错误的原因,您将了解更多信息。)