-
Notifications
You must be signed in to change notification settings - Fork 6k
Conversation
The first frame's time is almost 2 times earlier than before in our app after this change. |
Well, for what it's worth this does seem to speed things up in some iOS add2app stuff I have. That said, it looks like some of the comments about the order of things might need to be updated a bit. Chinmay or Jason will also be able to check the correctness of this change better than I can. |
/cc @chinmaygarde for further review |
@chinmaygarde have you had a chance to have a look at this? If so, any feedback? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I talked offline with @chinmaygarde about this. It's not thread safe internally to the engine.
This is a very interesting idea though. If we could somehow make all of the Engine
class unique_ptr's waitable/thread safe, we could probably experiment with doing this. I'd love to see this patch updated to do that, but if not Chinmay and I discussed some ideas to make it happen and one of us or someone else will get to it (a lot of people want this problem improved).
To elaborate a little bit more on this we'd want to make sure access to https://github.com/flutter/engine/blob/master/shell/common/shell.h#L91-L94 is thread safe. Right now that's being taken care of by the thread jumps and waits - if we remove that, we have to make sure it's safe not just for embedder consumers but also inside the shell itself. |
@SupSaiYaJin do you plan to followup with changes to address the thread-safety issue? |
Can you provide some specific ideas? I would try to do it. |
We'd have to make all four pointers the shell holds guarded like this, perhaps as futures, so that internal access within the class is safe and only happens when the necessary parts are set up. |
@dnfield Do you mean to use std::future? |
Yes on std::future They really all need it for internal consistency and safety. Embedders won't access the others but within the engine there's nothing stopping that and we may already have clients that do |
@dnfield I have made a huge change by using std::shared_future, could you plz check it? |
The test that's failing is one that's checking thread safety. |
shell/common/shell.cc
Outdated
Shell::CreateCallback<PlatformView> on_create_platform_view, | ||
Shell::CreateCallback<Rasterizer> on_create_rasterizer) { | ||
if (!task_runners.IsValid()) { | ||
FML_LOG(ERROR) << "Task runners to run the shell were invalid."; | ||
return nullptr; | ||
} | ||
|
||
auto shell = | ||
std::unique_ptr<Shell>(new Shell(std::move(vm), task_runners, settings)); | ||
auto shell = std::unique_ptr<Shell>(new Shell(task_runners, settings)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we need to tell the shell about how to get a VM at this point. The platform view may need it for some embedding implementations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also nit: this can just be auto shell = std::make_unique<Shell>(task_runners, settings);
shell/common/shell.cc
Outdated
} | ||
|
||
DartVM* Shell::GetDartVM() { | ||
return &vm_; | ||
fml::WeakPtr<Shell> Shell::GetShell() const { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WeakPtrs are only safe for access on the thread they were created on. It looks like there are violations of this in several places in this code.
It also is concerning that this is getting used from the destructor of the shell in post task calls - as in, it seems like there's some attempt to refernece the memory of an object whose destructor has potentially completed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do yu think of holding Shell with std::shared_ptr instead of std::unique_ptr by AndroidShellHolder and EmbedderEngine, let Shell extends std::enable_shared_from_this, then we can use std::weak_ptr.lock() on each closure to access Shell.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think for that scale of change it'd be really nice if we had a design document that outlined the rationale fo rdoing this, the impact of it, and what alternative approaches are being rejected. It would help give the engine team a chance to evaluate what's becoming a fairly large change, and allow for some more comments/refinement than we can likely achieve in a GitHub pull request format.
shell/common/shell.cc
Outdated
fml::MakeCopyable([engine = std::move(engine_), &ui_latch]() mutable { | ||
engine.reset(); | ||
fml::MakeCopyable([shell = GetShell(), &ui_latch]() mutable { | ||
shell->vm_->GetServiceProtocol()->RemoveHandler(shell.get()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems very dangerous. We're almost definitely going to see situations where the shell pointer is being used on the wrong thread and after the destruction of the object.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we use [this] directly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not really sure how that helps anything. We're still be using a pointer to the shell at a point where its destructor has finished.
I haven't thoroughly reviewed the whole patch, but the last couple comments I left seem like pretty serious design concerns to me. What do you think? |
@SupSaiYaJin have you had a chance to take a look at @dnfield 's comments? |
From offline conversations, I believe this PR is more meant as an illustration of inefficiencies that can exist during the engine startup. Since this is so central to everything downstream, it could be sensible for our engine team to pick up (the way we did with flutter/flutter#25075). @SupSaiYaJin can you open an issue with more numerical descriptions of this issue in terms of how this occurs and from testing or live data, how much this patch affects loading time? |
@gaaclarke has been looking at startup performance (however is unfortunately out of the office at the moment). |
I've poked at this a bit more. It seems like it would be really nice to move the VM start up somewhere that doesn't block the platform thread. To do that, we'd need to kill the shell constructor that takes a VM, which is currently depended on by flutter_runner, but doesn't need to be. We'd also have to kill off the shell vending a reference to the VM, or just make it pass through to the engine and block if the engine is still creating the VM. That seems like it should be safe to me, but I'm a little fuzzy on how that might impact the rest of the ivars in the shell that might want the VM to be around by initialization. We'd have to figure out a good way to test that they can tolerate the VM starting later than it currently does from their perspective. |
@eseidel You've got me for one more day! Thanks @SupSaiYaJin. I read through the diff, here is my summary of what you did: You unlatched synchronization points inside the setup of Shells to allow certain initialization tasks to happen in parallel. Then replaced all getters for the initialized instance variables with calls to Futures which will delay synchronization to the last possible moment. The problem I have with it is that it is pretty invasive. I'd have to see some hard evidence that the gains merited the risks. Also, access to the instance variables incur a cost in perpetuity when accessing them. How about this as an alternative: #10182 ? I think this will get the majority of the performance boost you achieved without having to rip up so much. We just shuffle around our synchronization points so they are closer to where they need to be instead of forcing things to happen serially. I'll have to think this though a bit more to make sure its safe. I'd like @chinmaygarde's take too. edit: I mentioned in that PR that the code would look cleaner if it was using futures, maybe that's something you'd like to refactor @SupSaiYaJin while I'm on vacation? Provided we are comfortable with this change. |
It would be nice to attach the |
Thanks for your contribution. Since we haven't heard back for a while on this, and it's currently got merge conflicts, I'm going to close this PR for now. Please don't hesitate to comment on the PR if you object to the approach @gaaclarke has proposed with #10182; we will reopen it right away! |
The main idea is release the latch between platform thread and ui thread. These threads are always very busy when startup especialy in large complex app.
Look forward to discuss with you guys.