Attempting to create a pool allocator

Started by
15 comments, last by dpadam450 11 years, 1 month ago

The bad side is its just useless clutter.

Everyone knows globals are "bad" right, so there are nearly none? Function arguments and local variables you instantly see right? Functions should also be kept short and often dont even have local variables? So the only kind of variable left are member variables, that means you can just assume if its not local its a member variable, even if you avoid all distracting hungarian namemangling.

Advertisement
Using m_ is not a bad habit. Some people don't like it, but it's certainly useful.

Parameter names should be uncluttered and free to be called wherever they need to be because readability of the interface is the most important. Member names should not be allowed to clash with parameter names and the easiest way to ensure that is to use a naming convention.

I see you have mem and memory above. That's not really better.

Using prefixed member variables (e.g. m_) is useful for writing optimized code.

You may or may not know it, but all member variables alias other pointer variables (e.g. function arguments and other members) because of the implicit this-pointer. This affects code optimization and often leads to less optimal code because of redundant loads. Therefore I like all member variables to clearly stand out by using a prefix, so they aren't used in e.g. tight inner loops, but moved to a local variable first. Aliasing basically happens all over the place.

Google for "pointer aliasing", "strict aliasing rule", "type punning" and "restricted pointers" if you're interested.

T

Using prefixed member variables (e.g. m_) is useful for writing optimized code.

You may or may not know it, but all member variables alias other pointer variables (e.g. function arguments and other members) because of the implicit this-pointer. This affects code optimization and often leads to less optimal code because of redundant loads. Therefore I like all member variables to clearly stand out by using a prefix, so they aren't used in e.g. tight inner loops, but moved to a local variable first. Aliasing basically happens all over the place.

Google for "pointer aliasing", "strict aliasing rule", "type punning" and "restricted pointers" if you're interested.

That's something that the compiler should be able to optimize on its own. Accessing a member of a struct can trivially be proven not to alias with another member of the struct. Also, member variables will be moved to registers if they are used in tight inner loops, so there wouldn't be redundant loads.

Thanks for all the responses !

A lot of useful information : )

T

Using prefixed member variables (e.g. m_) is useful for writing optimized code.

You may or may not know it, but all member variables alias other pointer variables (e.g. function arguments and other members) because of the implicit this-pointer. This affects code optimization and often leads to less optimal code because of redundant loads. Therefore I like all member variables to clearly stand out by using a prefix, so they aren't used in e.g. tight inner loops, but moved to a local variable first. Aliasing basically happens all over the place.

Google for "pointer aliasing", "strict aliasing rule", "type punning" and "restricted pointers" if you're interested.

That's something that the compiler should be able to optimize on its own. Accessing a member of a struct can trivially be proven not to alias with another member of the struct. Also, member variables will be moved to registers if they are used in tight inner loops, so there wouldn't be redundant loads.

In the case of aliasing, you couldn't be more wrong.

You should not assume what the compiler can and should do, but rather check the facts about what it is allowed to do.

First and foremost, the compiler needs to generate correct code. Only then can it create optimized code.

In the case of aliasing, the compiler must be very conservative in order to generate code that behaves correctly in all situations.

It cannot be trivially proven that two members don't alias each other. Consider a class with two int* members - if they both point to the same address, they alias each other. And because they are both of type "pointer to int", they are allowed to do so, per the standard. So-called compatible types are allowed to alias, and that also includes signed/unsigned, and cv-qualified types, e.g. a int* is allowed to alias an unsigned int*. In addition, there is a special case for char*, which is allowed to alias everything. You need that to correctly implement certain functionality in case your compiler adheres to the strict aliasing rule (the PS3 compiler does, trust me).

You also stated that "member variables will be moved to registers if they are used in tight inner loops, so there wouldn't be redundant loads", and again, this is wrong in case of aliasing.

Consider the following simple example:


void XorCypher::Encrypt(char* buffer, size_t size)
{
  for (size_t i=0; i<size; ++i)
  {
    buffer ^= m_key;
  }
}

Assume this is a member function of a class XorCypher that has a member named char m_key. In this case, char* buffer aliases char XorCypher::m_key because of the implicit this-pointer (m_key actually becomes a pointer to char). Therefore, the compiler has to generate code that reloads m_key on each and every loop iteration because it might have changed in a store-operation to buffer!

Aliasing happens for member variables, function arguments, pointer types, and also reference types. That's why I said "aliasing basically happens all over the place", and you have to be aware of that.

What can you do against it? Use local variables, or restricted pointers (using the C99 restrict keyword, supported by all major C++ compilers).

After all, there is a reason why free functions can be faster than member functions, and why the above can be a performance penalty on Xbox360/PS3 platforms.

Recommended reading material if you want to know more details:


http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html

http://cellperformance.beyond3d.com/articles/2006/05/demystifying-the-restrict-keyword.html

http://assemblyrequired.crashworks.org/2008/07/08/load-hit-stores-and-the-__restrict-keyword/

The bad side is its just useless clutter.

Everyone knows globals are "bad" right, so there are nearly none? Function arguments and local variables you instantly see right? Functions should also be kept short and often dont even have local variables? So the only kind of variable left are member variables, that means you can just assume if its not local its a member variable, even if you avoid all distracting hungarian namemangling.



Not useless clutter, the reason I posted this in the first place is I was like "where is 'at' coming from" and for each variable I have to go find it. Remember in the real world files can get 1-2K lines long, so the idea you want to go back to a header to see if the variable is local or not is horrible. You could have 5 func parameters + 10 local variables in a given function that you didn't write.

The two companies (game) I have worked at use mVariableName. At home I use this-> because I like reading code and saying "this objects speed is 20" instead of "mSpeed is 20".

Oké, any other reasons I should use void* instead of char *? I saw both when I was googling around so I'm not really sure.


Convention. You took a piece of raw memory and casted it to a char and then to a GameObject (example below). So you are denoting the memory you are returning is going to be a char, but really it's not really going to be a char, just a pointer to raw memory that the user of the class will cast to something they want. I just searched the EA base, and they are all void* as well.
GameObject* gameObject = allocate();

NBA2K, Madden, Maneater, Killing Floor, Sims http://www.pawlowskipinball.com/pinballeternal

This topic is closed to new replies.

Advertisement