Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize memory allocations in VariantParser::get_token #10844

Merged
merged 2 commits into from
Sep 5, 2017

Conversation

MednauN
Copy link
Contributor

@MednauN MednauN commented Sep 1, 2017

This is optimization I made after profiling loading time of a game we're making. The game has lots of heavy scenes with meshes, animations and stuff, so parsing them became a bottleneck.
Apparently, there are a lot of redundant memory allocations inside VariantParser::get_token. I rewrote the code so it will use static buffer when possible.

Just a couple of screenshots - this is profiling results of loading phase of our game:
before
And this is the same loading after my optimization:
after

And I'm aware that it's only actual for development builds (exported projects are unlikely to have this issue at all). Still, I think it won't hurt to merge.

@hpvb
Copy link
Member

hpvb commented Sep 1, 2017

I like the idea for the optimization but it seems to me it would be better to decouple a String's length() from the underlaying array.size(). That way everything benefits from a small performance win due to less allocation.

It seems that keeping the string's length separately from it's size wouldn't be terribly hard to do. Once that's done you could just resize num to start off with a reasonable number of bytes.

I don't think this should be merged in this form personally.

@MednauN
Copy link
Contributor Author

MednauN commented Sep 1, 2017

@hpvb you certainly have a point here, but separating string length from array size will cause String::length and String::size return completely different values, and I'm afraid that may break something in existing code.
And the static buffer is also important here, because it saves us from any memory allocations in most of cases. (actually, it's similar how std::string works, and I really wanted to add this optimization to String itself, but since it's inherited from Vector<CharType>, I can't do this)
Hm... or maybe, "static buffer" thing may be discarded here without much of performance loss. Gonna check this with profiler.

@MednauN
Copy link
Contributor Author

MednauN commented Sep 1, 2017

This is what happened when I left only long_num_buffer (initial size 64 bytes, growth strategy 1.5):
no_static_buffer
It was tested on a different scene, so total count of samples is different, sorry about that.
But performance relative to get_token should be pretty accurate, so I assume that performance drops more than twice because of Vector::resize and Vector::~Vector.
So, as I said, static buffer is important.

@hpvb
Copy link
Member

hpvb commented Sep 1, 2017

My concern is that there are tons of places in godot where this type of parsing happens. Maybe add a new helper container that implements this algorithm and replace all of these types of rapid-fire appending cases in parsers with that.

I just really don't like this mini-string implementation in the middle of a parser.

@MednauN
Copy link
Contributor Author

MednauN commented Sep 1, 2017

@hpvb oh, yeah, a helper container sounds like a good idea. Thanks for the hint. I think I'll make something like StringBuilder from Java if it's okay.

@hpvb
Copy link
Member

hpvb commented Sep 1, 2017

@MednauN That sounds about perfect! 👍

@@ -296,7 +296,10 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri
if (cchar == '-' || (cchar >= '0' && cchar <= '9')) {
//a number

String num;
const int num_buffer_size = 64;
CharType num_buffer[num_buffer_size];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

num_buffer_size needs to be a static const int in order to comply to the C++ standard. The way it is now is probably accepted by most compilers because they can detect it's a constant expression, but by the standard this is not a valid declaration.

With -std=gnu++03 this might work, but it's again not C++ standard conform.

I mean it will most likely work on all compilers currently used, just pointing it out here. 😄

@karroffel
Copy link
Contributor

@MednauN hey, would you mind if I work on that StringBuilder class? A PR should be ready in like an hour.

@MednauN
Copy link
Contributor Author

MednauN commented Sep 1, 2017

@karroffel okay, suit yourself)

@karroffel
Copy link
Contributor

@MednauN #10860 here you go!

@MednauN
Copy link
Contributor Author

MednauN commented Sep 1, 2017

@karroffel well, that's one way to make it, but I'm afraid, in context of this PR the StringBuffer will be lacking in performance. As I wrote before, using static buffer (i.e. fixed-size array of chars) is one of important points of the whole optimization thing here. Thus, if I want to re-write PR using the StringBuffer, I would need to improve it so it'll use a static buffer before allocating memory.
Well, sure I can add these changes myself, but since you're already writing the StringBuffer implementation, maybe you should consider to add this optimization as well.

@karroffel
Copy link
Contributor

@MednauN I wanted to implement that in the past, so to be quite honest, I didn't make it for your PR, I just took the chance because this time reduz understood what I actually meant with a StringBuilder 😄

If you could comment that on the PR that would be cool. I could implement a static-sized member array that's used before any other dynamic allocation, that would be doable, but I fear that reduz will have something against it. But I would appreciate it if you could make a list of demands in the PR.

@MednauN
Copy link
Contributor Author

MednauN commented Sep 4, 2017

I made StringBuffer that does all my optimizations. Personally I think it should be part of StringBuilder from #10860, but since karroffel says that it would be better to separate them, so be it.

@reduz
Copy link
Member

reduz commented Sep 4, 2017 via email

@reduz
Copy link
Member

reduz commented Sep 4, 2017 via email

@akien-mga
Copy link
Member

There are small code style issues that need to be fixed: https://travis-ci.org/godotengine/godot/jobs/271743463

While at it, I think it might make sense to split your changes in two commits for clarity: first add StringBuffer, then use it in the second commit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants