Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Decommit instance memory after a runtime call on Linux #8998

Merged
12 commits merged into from
Jun 14, 2021

Conversation

pepyakin
Copy link
Contributor

@pepyakin pepyakin commented Jun 2, 2021

Right now we have a wasm runtime cache. This cache keeps a number of instantiated runtimes around. That allows the executor to serve the requests with as little latency as possible. This improves latency on the critical paths significantly.

However, the instances retain their memory between executions. This was made as an optimization. This means that if a runtime wrote into some page some physical memory will stay attached to it.

In the light of increasing the heap pages (#8892), this problem becomes more severe. With the defaults, that can lead to up 2 GiB consumed by those dirty mappings.

This PR solves this problem on Linux. The solution is to tell the kernel that we are no longer interested in the contents of the specified address range. This will lead to demouting these pages and freeing up the corresponding areas of physical memory. This will not lead to decreasing of virtual memory usage by the instances.

As a side effect on Linux this brings us back in line with #3011 after we dropped it. There is no intention to bring this back as a guarantee as of yet. In other words, wasm runtimes must not access uninitialized memory.

@pepyakin pepyakin added A0-please_review Pull request needs code review. B0-silent Changes should not be mentioned in any release notes B5-clientnoteworthy C1-low PR touches the given topic and has a low impact on builders. labels Jun 2, 2021
@pepyakin pepyakin removed the B0-silent Changes should not be mentioned in any release notes label Jun 3, 2021
pepyakin added 6 commits June 11, 2021 15:38
Instead of tracking RSS for the whole process we just look at the particular mapping that is associated with the linear memory of the runtime instance
Copy link
Member

@athei athei left a comment

Choose a reason for hiding this comment

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

I believe the test is incorrect.

client/executor/src/integration_tests/linux.rs Outdated Show resolved Hide resolved
client/executor/src/integration_tests/linux.rs Outdated Show resolved Hide resolved
@pepyakin
Copy link
Contributor Author

Thanks for the review, that was a great catch! It should be fine now.

Comment on lines +71 to +81
fn get_map(&self, addr: usize) -> &BTreeMap<String, usize> {
&self.0
.iter()
.find(|(range, _)| addr >= range.start && addr < range.end)
.unwrap()
.1
}

pub fn get_rss(&self, addr: usize) -> Option<usize> {
self.get_map(addr).get("Rss").cloned()
}
Copy link
Member

Choose a reason for hiding this comment

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

How sure are you that the whole instance memory is kept in one mapping? I think they can also be split up in multiple consecutive mappings (at least on macOS that is the case).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is Linux only code so we don't care about macOS here. Yes, potentially they can be split up on several mappings. The opposite is also possible AFAIU, when several instances are packed into a single VMA. Note though that the solution with madvise should work in these cases.

We generally cannot be 100% sure what wasmtime does under the hood, so this is the best effort. But hey it's not the end of the world if this test fails. I see no reason it to be not a single mapping though, at least on Linux. It is simpler to implement, most efficient, etc

return;
}

cfg_if::cfg_if! {
Copy link
Member

Choose a reason for hiding this comment

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

We don't need cfg_if here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You mean that madvise is POSIX-compatible? Sure, but it's complicated. In Linux this is not merely an advice it has a very certain and widely relied semantics of eagerly freeing and zeroing the pages. macOS is, on the other hand, is very lazy about those. madvise(WONTNEED) won't have the same effect there.

There are different options there to make it work on macOS, but those are out of scope for this PR. When the time comes though it will be clear where to integrate them : )

Copy link
Member

Choose a reason for hiding this comment

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

I mean we don't need the cfg_if.

Could be just cfg(unix)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

(Assuming you meant cfg(target_os = "linux"))

Yeah, true, not necessary. I figured I use it because:

  • it's not a new dependency. We already use it and it is a very common dep that it occurs in our dep tree many times
  • as I mentioned it's likely we want other branches for macOS and/or cfg(unix)

It's not something I am married to, so can change to a plain cfg as well. Just let me know

Copy link
Member

Choose a reason for hiding this comment

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

Yeah fine

@bkchr
Copy link
Member

bkchr commented Jun 14, 2021

bot merge

@ghost
Copy link

ghost commented Jun 14, 2021

Trying merge.

This pull request was closed.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
A0-please_review Pull request needs code review. C1-low PR touches the given topic and has a low impact on builders.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants