1

Given the following:

class ReadWrite {
public:
    int Read(size_t address);
    void Write(size_t address, int val);
private:
    std::map<size_t, int> db;
}

In read function when accessing an address which no previous write was made to I want to either throw exception designating such error or allow that and return 0, in other words I would like to either use std::map<size_t, int>::operator[]() or std::map<size_t, int>::at(), depending on some bool value which user can set. So I add the following:

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

The problem with that is: Usually, the program will have one call of allow or none at the beginning of the program and then afterwards many accesses. So, performance wise, this code is bad because it every-time performs the check if (allow) where usually it's either always true or always false. So how would you solve such problem?

Edit:

While the described use case (one or none Allow() at first) of this class is very likely it's not definite and so I must allow user call Allow() dynamically.

Another Edit:

Solutions which use function pointer: What about the performance overhead incurred by using function pointer which is not able to make inline by the compiler? If we use std::function instead will that solve the issue?

Hanna Khalil
  • 975
  • 1
  • 10
  • 28
  • 3
    Did you measure if that's really your performance bottleneck? – πάντα ῥεῖ Oct 22 '16 at 21:13
  • no I didn't but it's still unnecessary check to make and it's bothering me – Hanna Khalil Oct 22 '16 at 21:15
  • 1
    1) It is not unnecessary if you want the functionality. 2) you have no idea what optimizations will be done by the compiler and the cpu 3) even if it is not necessary and there are no optimizations done by compiler and cpu, this question is still [premature optimization](https://xkcd.com/1691/) (which [is the root of all evil, btw](http://stackoverflow.com/questions/385506/when-is-optimisation-premature)) – zvone Oct 22 '16 at 21:21
  • 1
    `template class ReadWrite { ... };` This way, the caller can decide at compile time which flavor of `Read` they want. Since `allow` is now a compile-time constant, the compiler would not generate a run-time check but will simply eliminate the unreachable branch (or, if you want to be extra sure, write a specialization of `Read`). – Igor Tandetnik Oct 22 '16 at 21:24
  • Please see the edit – Hanna Khalil Oct 22 '16 at 21:26
  • 2
    Well, you can't have it both ways - support changing the behavior midway at run-time, but somehow magically avoid the run-time check of which behavior to use. it is incorrect to characterize it as "unnecessary check" - it's necessary to fulfill your requirements. If you don't like the check, change the requirements. – Igor Tandetnik Oct 22 '16 at 21:28

4 Answers4

1

Usually, the program will have one call of allow or none at the beginning of the program and then afterwards many accesses. So, performance wise, this code is bad because it every-time performs the check if (allow) where usually it's either always true or always false. So how would you solve such problem?

I won't, The CPU will.
the Branch Prediction will figure out that the answer is most likely to be same for some long time so it will able to optimize the branch in the hardware level very much. it will still incur some overhead, but very negligible.

If you really need to optimize your program, I think your better use std::unordered_map instead of std::map, or move to some faster map implementation, like google::dense_hash_map. the branch is insignificant compared to map-lookup.

Community
  • 1
  • 1
David Haim
  • 25,446
  • 3
  • 44
  • 78
0

If you want to decrease the time-cost, you have to increase the memory-cost. Accepting that, you can do this with a function pointer. Below is my answer:

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

The function pointer can be called as the other member functions. As an example:

ReadWrite rwObject;
//some code here
//...
rwObject.Read(5); //use of function pointer
//

Note that non-static data member initialization is available with c++11, so the int (ReadWrite::*Read)(size_t) = &ReadWrite::read1; may not compile with older versions. In that case, you have to explicitly declare one constructor, where the initialization of the function pointer can be done.

Ugur Yilmaz
  • 156
  • 1
  • 7
  • yes I was looking for something like this. But why do you expose all of that? Aren't these supposed to be private with only one Read function which calls function pointer? – Hanna Khalil Oct 22 '16 at 22:16
0

You can use a pointer to function.

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;
}
cpatricio
  • 457
  • 1
  • 5
  • 11
0

If you want runtime dynamic behaviour you'll have to pay for it at runtime (at the point you want your logic to behave dynamically).

You want different behaviour at the point where you call Read depending on a runtime condition and you'll have to check that condition. No matter whether your overhad is a function pointer call or a branch, you'll find a jump or call to different places in your program depending on allow at the point Read is called by the client code.

Note: Profile and fix real bottlenecks - not suspected ones. (You'll learn more if you profile by either having your suspicion confirmed or by finding out why your assumption about the performance was wrong.)

Pixelchemist
  • 24,090
  • 7
  • 47
  • 71
  • I agree that there is run-time overhead which I can't avoid completely. The question is whether this overhead is less significant if std::function is used instead of function pointer? – Hanna Khalil Oct 22 '16 at 23:58
  • @HannaKhalil: Profile. I don't think that `std::function` can do more than a function pointer in this case. As I pointed out you'll end up with reading a target adress and jump to that address anyway. – Pixelchemist Oct 23 '16 at 01:13