2016-10-22 86 views
1

考虑以下几点:避免检查可能发生,如果

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(),取决于用户可以设置的一些布尔值。所以,我添加以下内容:

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()动态。

另一个编辑:

的解决方案,使用函数指针:有关使用函数指针是不能够由编译器进行在线支付的性能开销是什么?如果我们使用std::function而不是解决这个问题?

+3

如果这确实是您的性能瓶颈? –

+0

不,我没有,但它仍然是不必要的检查,它是困扰我 –

+1

1)如果你想要的功能没有必要。 2)你不知道编译器和cpu会做什么优化3)即使没有必要,也没有编译器和cpu完成的优化,这个问题仍然是[过早优化](https:// xkcd (这是所有邪恶的根源,顺便说一句)(http://stackoverflow.com/questions/385506/when-is-optimisation-premature)) – zvone

回答

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;可以不与旧版本的编译。在这种情况下,你必须显式声明一个构造函数,可以完成函数指针的初始化。

+0

是的,我正在寻找这样的事情。但你为什么要公开所有这些?这些应该不是私有的,只有一个调用函数指针的Read函数? –

+0

是的,你是对的。编辑! –

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; 
} 
1

通常情况下,该方案将允许有一个呼叫或没有在 开始计划的,然后再把许多的访问。所以, 表现明智,这个代码是坏的,因为它每次执行 检查,如果(允许)通常在哪里总是为真或总是 错误。那么你将如何解决这个问题?

我不会,CPU会的。
Branch Prediction会发现答案在很长一段时间内最有可能相同,所以它能够非常好地在硬件级别优化分支。它仍然会产生一些开销,但是可以忽略不计。

如果您确实需要优化您的程序,我认为您最好使用std::unordered_map而不是std::map,或者转到更快的地图实施,如google::dense_hash_map。与地图查找相比,该分支不重要。

0

如果你想运行时动态行为,你必须在运行时支付它(在你希望你的逻辑动态行为的地方)。

根据运行时间条件,您需要根据不同的行为调用Read,并且您必须检查该条件。 无论您的overhad是函数指针调用还是分支,您都可以在程序中的不同位置找到跳转或调用,具体取决于allow,其中Read由客户端代码调用。

注意:配置文件并修复真正的瓶颈 - 不是可疑的瓶颈。 (如果您通过确认您的怀疑或找出您对性能假设的错误原因,您将了解到更多信息。

+0

我同意存在运行时开销,我无法完全避免。问题是如果使用std :: function而不是函数指针,这个开销是否不那么重要? –

+0

@HannaKhalil:个人资料。在这种情况下,我不认为'std :: function'可以做的不仅仅是函数指针。正如我指出的那样,最终你会读到一个目标地址,然后跳到那个地址。 – Pixelchemist