Closed gaearon closed 1 year ago
All modified and coverable lines are covered by tests :white_check_mark:
Comparison is base (
6151e7e
) 83.09% compared to head (eaf9dd1
) 83.10%.
:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.
@robhogan has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.
@robhogan merged this pull request in facebook/metro@2f62858ae187bbb2f49c1e90e4bdca51659d80cb.
This pull request has been reverted by 92340b350a5c4b948b989f6c6e444825d5e0aaa6.
Urgh - this caused native crashes across multiple apps in debug builds post-land, still connecting the dots but there are runtime mem allocation failures coming from Hermes - first few stack frames, FYI:
SIGABRT:libxplat_hermes_API_HermesAPIAndroid.so:./xplat/hermes/external/llvh/include/llvh/Support/MemAlloc.h:llvh::SmallVectorBase::grow_pod(void*, unsigned int, unsigned int)
hermes::irgen::ESTreeIRGen::serializeScope(hermes::ScopeDesc*, bool)
hermes::irgen::ESTreeIRGen::setupLazyScope(hermes::ESTree::FunctionLikeNode*, hermes::Function*, hermes::ESTree::BlockStatementNode*)
hermes::irgen::ESTreeIRGen::genES5Function(hermes::Identifier, hermes::Variable*, hermes::ESTree::FunctionLikeNode*, bool)
Is there any chance that some (relative) paths are still incredibly long, or that Hermes has a limit on function names that isn't present in other JS engines? I was thinking of adding .slice(0, 50)
or something.
Is there any chance that some (relative) paths are still incredibly long, or that Hermes has a limit on function names that isn't present in other JS engines? I was thinking of adding
.slice(0, 50)
or something.
String length is a factor, but probably not the determining one. We saw total RAM on Facebook Android (dev builds) go from 1.6GB to 3.8GB with this change, so way more than just the sum of those strings, even though the paths are pretty long.
@tmikov has been working on it and identified a quadratic growth situation where scopes were repeatedly re-serialised during lazy evaluation, but the fix for that didn't seem to be enough on its own.
Worst case, we could enable this behind a serializer
config flag in Metro and document the memory use, but the Hermes folks were actively looking into it last I heard so they may want a bit more time.
There is no limit, Hermes is just not optimized for running from source - this problem doesn't exist when running from bytecode. Running from source with lazy compilation is, in a sense, contrary to Hermes's mission of being an optimizing AOT compiler. We basically bolted lazy compilation on top of the AOT compiler and had to use a lot of hacks, because it simply wasn't designed for that. It is a miracle it has worked as well as it has...
We will support this properly in Static Hermes (at tremendous complexity cost), but for now we are trying to figure out exactly what is consuming the memory in "regular" Hermes. There is something quadratic. (We already fixed one quadratic case, but sadly it made no difference.)
This adds path-based function names to the generated module factories in development.
They're only emitted when
minify
isfalse
anddev
istrue
.Motivation
Profiling module initialization is a common need. The current set of tooling React Native provides for it is arguably abysmal.
The primary tool suggested by React Native documentation is Systrace which literally does not exist anymore. "Profiling with Hermes" suggests using sampling profiler which seems to barely work β I wasn't able to capture any information with it.
If you try to use Hermes Chrome DevTools integration and press "Record" in the Performance tab, it hangs.
I almost gave up at that point, but @brentvatne kindly suggested that there is a workaround β I need to Shift+Cmd+P to get a different "JS Profiler" Chrome DevTools tab to show up, and that one actually works.
Even that approach is a little bit broken but it's the closest to what I want.
Unfortunately, even that tool does not let me see which modules I'm spending time initializing:
As you can see, module factories have blank function names.
This is not the case with web-based tooling. For example, webpack emits module factories as a dictionary like
{ "./src/some/module.js": function() {}, ... }
so the stacks are useful. Since we don't want to change the format entirely, and configuringfn.name
dynamically at runtime does not make it show up in the trace, I opted for a minimal possible change β to actually emit the module names in development.As a result, I can see where I'm spending time during module initialization: