Why is const char* replaced with std::string in all method arguments of cocos2d-x 3.0?
I understand than sometimes receiving a std::string can be useful, but I suppose that for most common usages performance is going to be a lot worse because unnecessary extra memory allocations.
@Chano:
It’s not a big deal due to move semantics, perfect forwarding, copy elision and small string optimization.
Your class provides yet another layer of indirection and makes it harder for the optimizer to inline calls, you have not implemented move-friendly methods. And the memory usage is going to increase.
Your class provides yet another layer of indirection and makes it harder for the optimizer to inline calls, you have not implemented move-friendly methods.
I think the example class is too short and simple to have problems with inlining.
What I posted is a quick example to get the idea, so some move-friendly methods would be great.
Anyway, the example class already has move semantics, because move constructor and assignment operator is added automatically:
The size of and empty std::string and a const char* pointer is going to be lower than the size of a non-empty std::string, and memory fragmentation is avoided until it’s necessary.
The point is, how often cocos2d-x classes manipulates the text stored in a std::string?
I think if the answer is not much, a solution like my example class is going to perform better.
It’s not a big deal due to move semantics, perfect forwarding, copy elision and small string optimization.
Really? For example, in this common (I think) code there’s 1000 unnecessary memory allocations:
for(int index = 0; index < 1000; index++)
{
Sprite* sprite = Sprite::createWithSpriteFrameName("whatever");
}
I have a suggestion for you: write such benchmark and publish the result here. Please remember to test it on both x86 and ARM. I’m really curious about the real difference between these two solutions.
Otherwise no one is going to take your concerns seriously.
edit:
Anyway, the example class already has move semantics, because move constructor and assignment operator is added automatically
I have created two classes, StdStringObject (which stores a std::string) and TestStringObject (which stores a TestString).
I have let the objects leak to measure used memory.
Currently I don’t have a working cocos2d-x environment, so I have done the test in a Qt one (sorry).
So on a Mac Mini with a 2.3 Ghz Intel Core i5, I get the following results:
That test is very simple, it does not randomize entered string, especially the strings’ lengths are always the same. The strings are also never modified.
What compiler did you use? gcc with -O3?
And the thing that might be worth trying IMHO would be to emulate old COW (copy on write) mechanism: have separate constructors for cstring and std::string, that would copy the actual string only upon the first modification. However, I’m not sure about detecting original string’s modification from outside of this string wrapper… What do you think of this?
If the string argument is going to be modified often, it’s better to use std::string. But I don’t think that cocos2d-x needs to do that much.
String randomization is not going to change the fact that with each new std::string an extra memory allocation is going to occur.
In fact, the test is favorable to std::string since the word “whatever” has few characters. If you test the example with for example a file path or an url the performance degradation is much worse.
Performance increase using TestString is almost always 50% with any text size.
With clang, performance degradation using std::string is just too bad (600%) when the text has more than 20 chars.
With vs2012, performance degradation using std::string is just too bad (400%) when the text has more than 10 chars.
With gcc, even storing an empty std::string is terrible (storing just the char array gives a 311% performance boost).
I think that my test string should be good enough for clang and vs2012, but for gcc the better option would be to avoid std::string and use a custom string that doesn’t allocate memory if it’s not needed.
And we change all API to use std::string is because we want to make it consistent. Yep, it may bring some overhead, but is it a bottleneck of a game? Now our bottleneck is over draw, which is hard to fix because 2d games use translucent textures.
I understand that string overhead is much less important than draw overhead in a (2d) game engine, and available work resources should be spent there.
In a game that manages a lot of text, we have these options:
We store every text in a std::string and waste a lot of memory.
We create a std::string every time a cocos2d-x call contains a text argument, allocating (and deallocating) memory with each call.
We manage std::string instances in a way that memory allocations are at a minimum.
We try to avoid cocos2d-x calls which contains std::string arguments
It’s common in game engines to have string implementations that doesn’t allocate memory if it’s not needed.
For example, EASTL string implementation doesn’t allocate memory with the default constructor:
It would be great if at least the cocos2d-x string implementation could be customizable by the user with a define or a typedef or something
Thanks for the feedback. It is a hard balance between performance and consistency. More options mean more confused. And it is hard to balance between newbies and senior engineers.