Run a performance-check to ensure WebView Support Library prototypes are not considerably slower than android.webkit. |
||||
Issue descriptionTo ensure our WebView Support Library approach is feasible we should run a performance check (ensuring that we don't add too much overhead for each WebView call) for a WebView Support Library prototype. What we could do here is call a single WebView-method lots of times, both for the support library, and for the frameworks WebView. We should probably run such a test for the KitKat/old WebView path of the support library if we end up supporting such a path.
,
Oct 9 2017
Note: the WebView used in comment #1 is a debug-build.
,
Oct 9 2017
dupeMethod looks to be the most expensive part, can that just happen once and have the result saved?
,
Oct 9 2017
Good question, we would have to store the 'dupeMethod' for each method we want to call across the support library <-> glue boundary, so we would probably have to store this in some kind of map (or have every location where we create an InvocationHandler store its locally used dupeMethod in a static variable). I'm gonna see if I can find a profiler showing stack traces to inspect what it is that's actually taking time.
,
Oct 10 2017
dupeMethod takes up 17% of the InvocationHandler.invoke call, the rest is the actual method invocation. The real culprit here seems to be Proxy.newProxyInstance() - it takes up about half of the entire execution time. If we use Proxy and InvocationHandler I don't think there's much we can do about that - whenever we need to cast something to an interface (which we have to do in each method call where we pass some support library object, e.g. setWebViewClient, or postVisualStateCallback), using Proxy, we will have to use Proxy.newProxyInstance(). I'll finish looking into how to call between the support library and the glue layer so we can say for sure whether Proxy and InvocationHandler are strictly necessary.
,
Oct 11 2017
Actually, I'm not sure why the dupeMethod was part of that trace at all (unless the visual state callback was triggered), dupeMethod should only be called when AwContents triggers the callback - never when we just pass a callback to the glue from the support library. I think I'll have to measure performance from both sides - i.e. both when calling from one side into the other, and when triggering a callback passed from one side to the other.
,
Oct 12 2017
Haah, this solution is so confusing in terms of what is called when (which is not great in terms of maintainability), anyway - the reason dupeMethod is called inside AugmentedWebView.postVisualStateCallback is because AugmentedWebView is calling mProvider.insertVisualStateCallback - and mProvider, which is of type GlueWebViewChromiumProvider, is also an object called through an InvocationHandler. So the dupeMethod method I'm referring to here is the one defined in the glue, rather than the one defined in the support library (yay confusion! :D).
Call stack:
AugmentedWebView.postVisualStateCallback ->
Proxy<WebViewProvider>.invoke() ->
InvocationHandler<WebViewProvider>.invoke() ->
GlueFactoryProviderFetcher.dupeMethod(insertVisualStateCallbackMethod).invoke(GlueWebViewChromiumProvider) ->
GlueWebViewChromiumProvider.insertVisualStateCallback() ->
Proxy.newProxyInstance(callbackInvocationHandler)
AwContents.insertVisualStateCallback()
as noted in comment #5,
GlueFactoryProviderFetcher.dupeMethod takes up ~15-20% of the execution time, while
Proxy.newProxyInstance takes around half of the execution time.
,
Oct 12 2017
After removing WebViewChromium.checkNeedsPost() from WebViewChromium.insertVisualStateCallback (that was a decent time-consumer), and pre-warming WebView before perf measurments (calling WebView.getCertificate() to trigger WebViewChromiumFactoryProvider.startYourEngines), I got the following results: including AwContents.insertVisualStateCallback implementation: 1116 ms, android.webkit postVisualStateCallback 4687 ms, support library postVisualStateCallback InvocationHandler 1207 ms, support library postVisualStateCallback Object+reflection Without AwContents.insertVisualStateCallback implementation (i.e. just purely calling from support library into glue): 679 ms, android.webkit postVisualStateCallback 4145 ms, support library postVisualStateCallback InvocationHandler 726 ms, support library postVisualStateCallback Object+reflection all of the above numbers are 10k iterations of postVisualStateCallback calls. So the overhead of calling into WebView APK code is essentially the same between an android.webkit call and a reflection call, but in the InvocationHandler approach this overhead is about 6x.
,
Oct 24 2017
Given that most WebView APIs are fairly heavy-weight, I don't think adding an overhead like this for each method call will make a huge difference for WebView performance, but I also don't know the entire WebView API surface. Do we have any methods which might be called very frequently? Is there some way we can check whether apps use any of our APIs in loops, e.g. through Marmot?
,
Oct 24 2017
probably most frequent are calls are the ones that happen once per draw, which is 60/s, so not high enough to worry too much about I can't think of any API that requires apps to call in tight loops for a long period of time
,
Oct 25 2017
Assuming (given comment #8) that the InvocationHandler approach adds around half a millisecond of execution time per call (~4k ms per 10k calls), we're looking at 60 * 0.5ms = 30ms extra execution time per second (for calls happening once per draw). This sounds OK as long as there aren't many of those calls, Bo do you know if we perform several of these, or just one, per draw? Btw, given that we are just adding new APIs since L, it would surprise me if many of those calls are being made once per draw, but you never know I guess :)
,
Oct 26 2017
,
Oct 26 2017
I think there are maybe 3-4 calls that goes down to every frame. There might be a few calls up as well. But none of these are new since L, so I not really applicable here. 0.5ms per call is pretty slow for things that happen every frame. But doesn't look like we have any of those
,
Oct 26 2017
I think general rule of thumb should be use InvocationHandler as default. And only switch to reflection for that call if we see a need for perf reasons.
,
Nov 2 2017
Yeah I think #14 sounds right. |
||||
►
Sign in to add a comment |
||||
Comment 1 by gsennton@chromium.org
, Oct 9 2017