Skip to content

Reduce memory usage #4114

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

Closed
pekkaklarck opened this issue Oct 7, 2021 · 2 comments
Closed

Reduce memory usage #4114

pekkaklarck opened this issue Oct 7, 2021 · 2 comments

Comments

@pekkaklarck
Copy link
Member

pekkaklarck commented Oct 7, 2021

Issue #4040 indicated that memory usage related to log/report generation may have increased after RF 3.2. That wasn't certain based on the issue description, but profiling by running /usr/bin/time -v rebot output.xml with output.xml containing 1000 dummy test cases showed that maximum memory sure enough had raised from ~102MB with RF 3.2.2 to ~123MB with RF 4.1.1. Unfortunate /usr/bin/time -v doesn't show where the memory is spent, but git bisect revealed that the commit causing pretty big increase in memory usage was 21c6b46.

The aforementioned commit added direct setup and teardown attributes to TestSuite and TestCase objects and teardown to Keyword. That was a good change from the API point of view, but each TestSuite and more importantly TestCase always having setup and teardown that are represented as Keyword objects takes some memory. With Keyword objects teardown is actually only created if needed, so I figured that there wouldn't be problems with them. Reverting the API change just to make TestCase objects use a little less memory didn't feel like a good idea.

Yesterday I totally by accident encountered a Python memory profiler called Fil. I had searched for this kind of tools earlier, but hadn't found this one. It looked really promising and targeted for exactly in our kind of use case. According to Fil docs it helps us to figure out:

  • Where peak memory usage is, also known as the high-water mark.
  • What code was responsible for allocating the memory that was present at that peak moment.

Turned out Fil did what it promised and I was able to find few places where we use lot of memory and where the memory usage has increased from RF 3.2.2. The biggest problem was that Keyword.teardown which I though wouldn't matter. It's true it's only created when needed, but we accessed kw.teardown in few central places (e.g. in SuiteVisitor) and that was enough. I commit a fix to this problem and to others I encountered separately.

After profiling and tuning the memory usage has dropped considerably on my machine. Usage reported /usr/bin/time -v with the same test set I used earlier is now ~83MB. That's a huge from from ~123MB with RF 4.1.1 and also a big drop from ~103MB with RF 3.2.2. If I got my math right, with this set memory usage has dropped 33% compared to RF 4.1.1 and 20% compared to RF 3.2.2.

Although these changes are pretty small, they still require new RF 4.1.2 release candidate and may require postponing the final release few days. Considering how big enhancements are, I consider that worth it and better than waiting for RF 5.0.

@pekkaklarck pekkaklarck added this to the v4.1.2 milestone Oct 7, 2021
pekkaklarck added a commit that referenced this issue Oct 7, 2021
Accessing `Keyword.teardown` creates another `Keyword` object that
represents the teardown. Its done even if the keyword has no teardown
to make the API easier to use. It's convenient to be able to use e.g.

    kw.teardown.name = 'Example'

instead of

    if kw.teardown is not None:
        kw.teardown.name = 'Example'

The problem is that we in few cases want to do something with the
teardown if it exists and just using

    if kw.teardown:
        ...

is enough to create a `Keyword` representing the teardown.

This commit introduces new `Keyword.has_teardown` property that can be
used like

    if kw.has_teardown:
        ...

without creating a new `Keyword` object.

This saves surprisingly big amount of memory. See #4114 for more
information.
pekkaklarck added a commit that referenced this issue Oct 7, 2021
This reduces memory usage a bit per each Tags instance. Because each
TestCase and more importantly Keyword object has tags, that adds up.
This is part of memory reduction explained in #4114.
@pekkaklarck
Copy link
Member Author

In addition to commits linked above, small performance enhancements were done in 7e04431 that I forgot to link to this issue.

@pekkaklarck
Copy link
Member Author

Above profiling has been done with Rebot, but some of the enhancements (e.g. Tags.__slots__) certainly help also in execution. I profiled execution quickly as well, but it turned out to consume so little memory compared to Rebot that I decided not to investigate it further now. We can return to this again during RF 5.0 development. Changes before a patch release like 4.1.2 are always a bit problematic anyway. I consider this issue done.

pekkaklarck added a commit that referenced this issue Jan 27, 2022
Accessing a non-existing setup/teardown creates a keyword object
representing setup/teardown and that object is preserved in test/setup
to make sure future access to same setup/teardown returns the same
object. This eases usage but with big suites unnecessarily creating
setup/teardown objects has a small effect on the memory usage.

Keywords had this same problem but with them the effect on the memory
usage was considerably bigger. That problem was fixed in
6d92b06 as part of #4114.  It seems
code generating ids for model objects still unnecessarily accessed
keyword teardowns but this commit fixes that as well.

Based on my quick tests, this change has small positive effect on the
memory usage, about 1%, with 1000 dummy tests. That's nice but
probably not worth creating another issue about enhanced memory usage.

Code consistently using `has_setup/has_teardown` is a win as well.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant