gnzlbg / jemallocator

Rust allocator using jemalloc as a backend
Apache License 2.0
403 stars 112 forks source link

Support for more page sizes #68

Closed michaeleisel closed 6 years ago

michaeleisel commented 6 years ago

When running on the iPhone 8, which has a page size of 16KB, I get the error message <jemalloc>: Unsupported system page size, and then my program segfaults when trying to access the allocated memory. One solution to this would be to use the --with-lg-page=<lg-page> flag, perhaps as a feature flag like with https://github.com/alexcrichton/jemallocator#feature-flags?

gnzlbg commented 6 years ago

Is this only the iPhone 8 or iOS in general ?

I think that, if possible, we should do this in the build.rs depending only on the target - that is, the page size will need to be part of the target specification somehow and we'd just use a 16kB page size for aarch64-apple-ios when configuring jemalloc.

Passing values via feature flags, can be done, but I only know of very messy ways of doing so. At that point it might be worth it to just have a custom_config feature that makes the build.rs read configuration options from a jemallocator.toml file or similar and uses those while building jemalloc.

michaeleisel commented 6 years ago

Yeah that sounds like a good idea. IIRC, it's 16KB for newer iPhones and 4KB for older ones. It may even split cleanly across targets, where 64-bit phones are 16KB and 32-bit are 4KB, I'm not sure. However, it can change even across OS updates, so the more I think about it, the worse of an idea it seems to ship anything for the iPhone that's tied to a specific page size. Is it possible to make jemalloc flexible enough to handle any page size?

gnzlbg commented 6 years ago

However, it can change even across OS updates, so the more I think about it, the worse of an idea it seems to ship anything for the iPhone that's tied to a specific page size.

Isn't jemalloc also the system allocator in iOS ? If so, you can just use the system allocator instead of compiling your own jemalloc version. Dynamically linking against it should pretty much always do the right thing, but I don't know how easy/hard is that to set up.

Is it possible to make jemalloc flexible enough to handle any page size?

I think jemalloc's configured page size must at least be as big as the system one, but it can be any power of two that is larger than that. So using 16kB should work on both systems using 16kB and 4kB, but it won't work on systems that have, e.g., a 64kB page size.

michaeleisel commented 6 years ago

jemalloc is not the system allocator in iOS. Also, since the page size could go up in the future, it makes me feel uneasy having a limit to it.

gnzlbg commented 6 years ago

The easiest solution here is to read this from an environment variable e.g. JEMALLOCATOR_LG_PAGE_SIZE=... in the build.rs script. PRs welcome.

jemalloc is not the system allocator in iOS. Also, since the page size could go up in the future, it makes me feel uneasy having a limit to it.

jemalloc does not support detecting / selecting this dynamically yet - there has been some work in this direction, but it is far from done. Using the system allocator on iOS is probably the best thing you can do right now to overcome this limitation.

michaeleisel commented 6 years ago

@gnzlbg what are the downsides of simply picking a really large page size that iOS versions in the next few years would never exceed? Jemalloc, from all the benchmarks I've run, is substantially faster (2x or more)

gnzlbg commented 6 years ago

@gnzlbg what are the downsides of simply picking a really large page size that iOS versions in the next few years would never exceed?

Probably wasting memory. I would really prefer to just choose the smallest size necessary for jemalloc to actually work "out-of-the-box" (e.g. 16kB on ios) and if a newer iOS version appears that requires a larger size, just bump this default value and release a new minor version.

This together with a knob (e.g. an environment variable at build time) that lets you do whatever you want should be good enough for the time being.

If you think you should bump the page size to 32kB or 64kB to try to create binaries that are compatible with "future" iOS versions you can try to do that, but I am not sure that will work (not necessarily due to jemalloc, but maybe you need to recompile anyways due to other issues).

michaeleisel commented 6 years ago

@gnzlbg my concern with using 16kb is that people can update their OS version without updating the app. The iOS app store submission process is slow (one-week review times are common) and updates are not mandatory for the user to download. It's not that uncommon for users to have a version of an app that's 6 months old or more. And even if OS updates and app updates could be coupled, we wouldn't want to force all apps with Rust in them to be updated every time the page size changes. Admittedly, I believe the page size has only changed once in the history of iOS from 4kb to 16kb, but I don't personally have the ability to predict when/if it would happen again.

gnzlbg commented 6 years ago

As mentioned, if you need to support such an environment, jemalloc does not have the required features yet, and you can always use the system allocator.

Admittedly, I believe the page size has only changed once in the history of iOS from 4kb to 16kb, but I don't personally have the ability to predict when/if it would happen again.

Nobody has, and independently of how high we choose it, they can always change it to some value higher, such that everything would still break. If you really want to use jemalloc here, the best you can do is send PRs to jemalloc upstream to finish the implementation of this feature, but that might be a significant amount of work, and jemalloc's PR review process is very time consuming and slow.

michaeleisel commented 6 years ago

Sounds good

michaeleisel commented 6 years ago

@gnzlbg thank you! i had a thought about the page size issue: could we test the page size at runtime, and only use jemalloc's if it's what we expect it to be? the fallback would be the system allocator

gnzlbg commented 6 years ago

could we test the page size at runtime, and only use jemalloc's if it's what we expect it to be? the fallback would be the system allocator

I don't think this can be easily done.

There is a certain degree of life before main in which the Rust run-time is initialized. @alexcrichton might correct me here, but this run-time can technically allocate memory AFAIK. So changing the global allocator while the program is running sounds very dangerous, since something might have allocated memory already before the change happens, which might mean that one then attempts to free that memory, or "realloc" it, with a different global allocator that does not know anything about it.

As mentioned, your best bet here is to just always dynamically link against the system allocator. That should do the right thing on all iphone models - IIRC jemalloc just wraps the system allocator on MacOS X anyways, so it might also do nothing on iOS as well?

The alternative is to just use a slightly larger lg-page-size in jemalloc, but as we have discussed earlier that won't be future-proof (e.g. if future iphones increase the page size your binaries might stop working).

michaeleisel commented 6 years ago

another option would be to use wrappers that do a quick check on each allocation for which allocator we should use

gnzlbg commented 6 years ago

The global allocator is accessed through global symbols like malloc, so once you switch to which library those symbols refer to dynamically, you can't use the old allocator anymore (no symbol points to it) unless you switch it globally again.

michaeleisel commented 6 years ago

can't you store the old pointer though for later use? for example, with dyld interposing, you can still access the old function within the one that's replacing it, e.g. https://opensource.apple.com/source/dyld/dyld-97.1/include/mach-o/dyld-interposing.h