background

As a content class applications, has been watching the news read information is the core of the headlines for the user demand, page open speed is directly related to the user use the headline at the core of the experience, in the headlines, more bearing rich enough to keep multiterminal experience under the style and logic of unity, details the content of the page we are carrying through the WebView, However, the WebView itself has poor performance compared to Native, so the Toutiao technical team has been working on optimizing the loading speed of the detail pages.

After continuous optimization, the online opening experience of the details page in Toutiao is basically invisible to the naked eye. In the following article, we will break down and introduce our ideas and practices for detail page load optimization.

Let’s take a look at the effect before and after optimization

Toutiao details page loading experience before optimization

The loading experience of toutiao details page has been optimized

Data to establish

performance

Before we start to optimize page load speeds, we need to figure out what the actual page load time is for the user. First we can look at the following formula:

Page load time = Page load complete time - Page load start timeCopy the code

When the user clicks on the card in the Feed, we can assume that the page has started loading.

The question is how do you define when a page loads? From the client’s perspective, WebView provides a loadFinsih callback for both iOS and Android, but in practice we find that the loadFinish callback does not reflect the user’s real experience.

In general, WebView rendering needs to go through the following steps

  1. Parsing HTML files
  2. Load JavaScript and CSS files
  3. Parse and execute JavaScript
  4. Building the DOM structure
  5. Load resources such as images
  6. The page is loaded

In fact, loadFinish is at the end of page loading, and the page structure has been rendered basically when DOM construction is completed. Therefore, from the perspective of user real experience, we take the time when DOM structure construction is completed (i.e., domReady) as the time when page loading is complete.

hang

In the process of browsing the details page, in addition to the page loading speed, there is another problem that particularly affects the user experience, namely, the white screen of the page, which is also a problem that users have received a lot of feedback in the early stage, but there are many scenarios that may lead to the white screen of the details page, such as network exception, WebView exception and so on. It is necessary to detect the occurrence of white screen from the perspective of user experience.

At present, the most intuitive solution I can think of is to take screenshots of WebView and traverse the color values of the pixels in the screenshots. If the color points of the non-white screen color exceed a certain threshold, it can be considered that it is not a white screen. At present, the performance problems and detection time of this solution need to be considered.

IOS provides a WebView snapshot interface to obtain the content of the current WebView rendering. The bottom layer adopts the implementation method of asynchronous callback. The API takes about 10ms and the user is basically unaware of it.

- (void)takeSnapshotWithConfiguration:(nullable WKSnapshotConfiguration *)snapshotConfiguration completionHandler:(void (^)(UIImage * _Nullable snapshotImage, NSError * _Nullable error))completionHandler API_AVAILABLE(ios(11.0));
Copy the code

The interface for obtaining view contents provided by the Android system is getDrawingCache. The API takes about 40ms and the performance loss is not very large.

In addition to the performance loss of screenshots, pixel detection is also a time-consuming scene in white screen detection. Through experiments, we shrink the WebView screenshots to 1/6 of the original one and traverse the pixel points of the detected image. When the non-white pixel points are greater than 5%, we consider the situation of non-white screen. Can be relatively efficient detection accurate details page occurred white screen.

Indicators to establish

With that in mind, we still need to figure out what metrics reflect the actual experience of a user scrolling through the headlines.

In the early days, we used the average page load time of detail pages, which was the sum of page loads per page pv, and it really was a good indicator of our load speed at the beginning.

Later, with the deepening of the details page load optimized gradually, will find that the average load time although can also reflect the details page loading speed, but because of details page pv is higher, if the average load rate of the utilization of a lot of user experience problems had been dropped on average, does not reflect the user’s actual situation, behind we adjusted the size again, For example, if the loading speed of the headline detail page is 1 second, it means that in 80% of cases, the loading of the detail page can be completed within 1 second. Of course, after our continuous optimization, The 80th quartile of detail pages has been able to load within 0.3s, meaning that 80% of the time users are able to load pages within 0.3s.

Later, we found that even the 80th percentile of the headline detail page could not reflect the real situation of many long tail users. In order to maximize the loading performance of the detail page, we finally adjusted the performance caliber of the detail page to 95th percentile. So far, thanks to our efforts, the loading speed of toutiao’s detail page has been optimized by nearly 80% in the 95th percentile. So what did we do? We’ll talk about it a little bit.

Template to optimize

Template split

As mentioned earlier, the graphic detail page is hosted through a WebView, and the simplest way to host a WebView page is to load an online page directly through the URL. So let’s start with a simple interview question: What happens when the user enters a URL from the browser into the display?

Now that we’ve covered the rendering process of a page, let’s briefly look at the following stages the user needs to go through from clicking to seeing the content of the page:

It can be seen that each time users enter the details page through online page loading, they have to go through multiple network loading, which is extremely susceptible to network fluctuations. In this case, the page loading time and success rate cannot be guaranteed, which greatly affects the user experience.

So in the headline, we split the headline and the body of the news, and extracted the common style CSS and logic JS of the headline detail page to form a separate and complete template of the detail page, so that we can directly build the template into the client.

At the same time, we will agree with the front-end JS script to inject the body content data into the page through the interface to complete the page display of the detail page. In this way, we can put the interface on the client for request.

This allows the user to enter the details page only to load the template locally, and while loading the template, the user can simultaneously request the details page data, and then inject the data into the template.

Then the user only needs to go through the following stages to see the page content:

As shown above, we can render the page with only one network load.

Could it be faster? Of course can!

In order to improve the loading speed of the page, the client preloads the news data through certain policies, so that in an ideal state, the user can directly use the cached data when he enters the page and sees the page, and the user can realize completely offline when watching the news, so as to avoid being affected by the network.

Template preheating

How much faster can it be without the network loading at all?

Of course you can! When the whole process goes offline, the bottleneck for page loading is the local template load time, so the next step is to optimize the template load time.

For the template, we did two things

  1. Template merge. Normally, the WebView needs to load the JS and CSS in the HTML after loading the main HTML, which requires multiple IO operations. So we inline the JS and CSS as well as some images into one file, so we only need one IO operation to load the template. It also greatly reduces the problem of template loading failure due to IO loading conflicts

  2. Template simplification. We pulled some non-essential scripts asynchronously, reduced unnecessary styles and JS code, and reduced the template size by more than 20%

With the above optimization, we have greatly optimized the template loading time, but can we do more? It’s still ok.

For the client, when the template with data after the separation, because every time a user clicks on load is the same template, so in fact, we don’t need the user to enter page templates to create the WebView and when loading, we only need to create a WebView in the right time in the background, and preheat load template in advance, When users click to enter the page, they can use the Loaded template WebView to directly inject the content data of the detail page into the page through JS, and the front end can render the page after receiving the data.

At this point, the user enters the details page and there is no need to reload the template. The path becomes:

As you can see, the optimization effect of template preheating and data prefetch through the local test is quite obvious. Basically, it has achieved the verification effect in the screenshot above.

Template reuse

When we after break up the template and data, data on the optimization is obvious, but we have said, in addition to the validation data, we also need to see the real experience of online user data, from 95 points on the actual data optimization is not clearly, so we observed from the data, the user 53% shooting preheating of the template, and room to improve further.

In order to make the page load as fast as possible, we want the user to be able to use a preheated WebView every time they enter the detail page. In general, we use template precreation pools to optimize the preheated WebView hit ratio when the user enters the detail page.

In many cases, creating a WebView is a performance expensive operation. If we use a pre-created pool solution, we will create the WebView frequently in the background, which will have an impact on the user’s browsing experience in the Feed scenario.

Moreover, if the user enters and exits the detail page frequently and quickly, it is very easy for the user to encounter a scenario in which the warm-up template cannot be hit.

At this time in order to optimize the user experience, as stated earlier, every time we use the same template, so after we use the current WebView, only need to empty the text data when the user exit pages, so get to the next page will be able to continue to reuse this WebView to data.

In this way, we avoided the frequent precreation of WebViews in the background, which had an impact on the user’s Feed experience, and improved the hit ratio of the preheated template when the user entered the page from 53% to 92%, optimizing the user experience.

Network optimization

After the optimization we did with the WebView template, let’s talk about the optimization we did with the content request.

The CDN to accelerate

As the headline details page request has the following features

  1. Large traffic. As mentioned before, watching news is the core scene of users in the headlines. Hundreds of millions of users use the headlines every day, and the data traffic of the details page is very large.
  2. Data attributes are basically unchanged, in the detail page request, many hot articles are repeated rendering calculation, body, title, author information, image control and some style and business logic rendering is basically unchanged, this part of the repeated calculation consumes bandwidth, server resources, is not necessary.
  3. Users are widely distributed, and the network condition is difficult to be guaranteed. The number of users of Tiaotiao is large, which covers various operators’ networks and network states, and the network quality cannot be well guaranteed. CDN can cache data to edge nodes in different places. Users can access edge nodes nearby to avoid long transmission on the public network whose network quality cannot be guaranteed, thus improving the response speed and success rate.
  4. The interface data is large. Due to the existence of body data, the data returned by the interface is often large. If the data is returned in real time every time, the pressure on the network will be large, and the bandwidth may be full and other services may be affected

Therefore, we divided the content data of the details page into static and dynamic parts, and hosted the basically unchanged content mainly consumed by users, such as the body content, title and author column, on the CDN.

The full name of the CDN is Content Delivery Network. Its purpose is to increase a layer of new network architecture in the existing Internet, the content of the website is published to the closest to the user’s network “edge”, so that users can get the required content nearby, improve the user access to the site response speed. CDN is different from mirroring because it is smarter than mirroring, or to use a metaphor: CDN= smarter mirroring + caching + traffic diversion. Therefore, CDN can significantly improve the efficiency of information flow in Internet networks. From the technical comprehensive solution due to the small network bandwidth, user traffic, unequal distribution of outlets, improve user access to the site response speed.

After hosting to CDN, users around the country can directly from the best node to obtain the details page data, also greatly save bandwidth costs.

disaster

1. Multiple domain name backup To prevent a service avalanche caused by a CDN failure, the server delivers multiple CDN links. When the current CDN fails, users can automatically switch to the next CDN.

2. Fast timeout The client traverses request CDN 1, request CDN 2, and request CDN 3. If all these CDN requests fail, the entire network request is counted as a failure.

The problem with this scenario, however, is that the timeout for requesting the CDN is assumed to be 15s. If CDN 1 fails, it needs to wait 15s to switch to CDN 2, which is unacceptable for the loading time of the detail page. If the user’s network deteriorates suddenly, it needs to wait 45s to return the error page showing failure.

Based on this, we design a fast dynamic timeout strategy for detail page requests

  • Timeout time for a single CDN request, calculated based on the value of the last successful CDN request, factor 1.5 (z value). The minimum value is 1s (x value) and the maximum value is 4s (y value). Do not cancel after this time, directly request the next CDN.
  • A CDN request has a hard timeout period of 4s (w >=y). The request is cancelled after this time. Failed to report to the user after all requests from n CDN were cancelled.

Several case:

  • The first CDN suddenly fails (assuming the time of the last successful request is a). The next request: the first CDN soon times out (a _ 1.5); Start requesting the second CDN (timeout is A _ 1.5, but the request is actually returned in b seconds). The user waits for a 1.5 + B for the next two requests: the first CDN times out (b 1.5); Start requesting the second CDN (timeout is B _ 1.5, but the request will be returned in c seconds). The waiting time of the user is B _ 1.5 + C

  • The user suddenly enters a poor network environment (assuming the time of the last successful request is a). Next request: the first CDN will soon timeout (A _ 1.5); Starting a request for a second CDN (A _ 1.5) also timed out; The third CDN (A _ 1.5) is requested. The last request will fail after a 3 + w (the value will be less than 12s).

As can be seen, with the multi-domain backup and fast timeout policy, even if the network or service is abnormal, users can recover quickly or make users aware of their network problems.

Rendering optimization

When we optimize at the template layer and network layer to the limit, we are limited by the WebView rendering speed!

Server prerender

Normally, the normal content data may be similar to JSON data. After the client obtains the data, the data is injected into the front end. The front end also needs to assemble the JSON data with the template, spell the HTML tags and other templates, and then render it to WebView rendering, resulting in a long time in the front end rendering.

In order to improve the efficiency of the user’s first screen, we’ll put all the details on the server page HTML text data, through the straight out of a service content into the page, you can directly to render the WebView. For other dynamic content (such as a relevant search), the issuance of the front-end for secondary asynchronous processing, promote efficiency of the user.

Client rendering

Generally speaking, all the content in the text is rendered through WebView. After the above optimization, the rendering efficiency of the text part of the article has been very high, but in the actual scene, many articles will contain more pictures and video scenes.

In a real scene, WebView renders non-text content with the following problems:

  1. Compared to text content, non-text content such as images and video resources render less efficiently for WebView
  2. There are also issues with the WebView rendering memory footprint and sliding experience for scenes with lots of images in the details page
  3. Finally, if the user opens the same article multiple times, the image in the same article will be loaded multiple times and cannot be cached with the client, which is also a waste of user traffic.

Therefore, in the details page, we will put non-text content such as pictures and videos on the client for rendering through native components, which can not only improve the rendering efficiency, but also reduce unnecessary traffic consumption.

Another benefit of primitive rendering is that images are increasingly becoming an important part of the post experience. For multi-image posts, we can also intelligently load the images needed for the detail page in the Feed page, increasing the user’s first-screen experience.

White during optimization

Finished performance optimization, and then to share our some of the details page white rate optimization, a lot of user feedback bad much problem may be caused by network problems such as page load time is too long, cause the user perception from the experience is bad, this part through share performance optimization methods have been able to solve the above, So here is just a brief introduction to some non-network reasons for white screen problems.

After the white screen detection and data analysis after the report, we found that the white screen problem of the details page caused by non-network reasons is generally the WebView loading problem.

In iOS, we use the system-provided WKWebView, which is a component that runs in a separate process, so when WKWebView occupies too much memory, The WebContent Process where the WKWebView is located will be killed by the system, which is reflected in the user experience as a white screen.

According to the practice of online, we can provide a callback in WKWebView webViewWebContentProcessDidTerminate function through the reload method to reload the current page, However, this situation only applies to the loadRequest through loadRequest. In the detail page, as the template-based WebView is used, rereload can only reload the template and cannot normally restore the whole detail page. The client needs to reload the template and then re-inject the data.

In addition, since we have the logic to warm up the template, the WKWebView may have crashed when entering the details page. When calling JS to inject data, it will directly return failure. When the failure occurs, we will try to reload the template. But later found a problem in practice, if direct injection method, call waiting system WebView return failed callback takes longer, so the follow-up also adjusted the data into the interface, we judge whether there is data in the injected script in advance into the interface, if not, means that template problems, directly to try again.

On Android, we use our own WebView kernel, and there are some weird holes.

  1. The WebView will read the template file while running. If another thread updates the template file at the same time, the template loading problem will occur. Therefore, you need to ensure the atomicity of the template loading
  2. Render deadening problem, the kernel is a more complex logic, internal rendering in rare cases will also appear Render deadening problem, but in the detail page of the overall user magnitude, even if only one in 100,000 possible, for the user is also a relatively big problem, at this time we will do white screen monitoring from the business to retry

Of course, regardless of iOS and Android, the WebView loading logic is quite complex, sometimes no retries will succeed, at this time we will directly downgrade to the loading line details page, priority to ensure the user experience.

conclusion

Limited by space, we have done a lot of other things, including request simplification, push article pre-pull, data injection method optimization, and so on. We have also done a lot of other directions of exploration, here do not expand, hope to have the opportunity to share with you.

Finally, let’s summarize some of our thoughts after optimizing the speed at which the details page opens

  • Data is very important. The first thing we did before optimizing the loading speed was actually to build a detailed page of data Kanban. Only through data can we really understand the current status of online users and find bottlenecks and optimization points from the real user experience.
  • User experience first, there are many optimization schemes, in addition to loading speed, but also need to start from the overall application experience, choose the best scheme for users
  • The pursuit of extreme, in fact, the initial optimization is relatively simple, but the later more difficult, need to cut a little bit of detail, to achieve the ultimate user experience

More share

Android Camera Memory problem analysis

Architecture evolution of bytedance’s self-developed online traffic diversion playback system

IOS Big decryption: the mysterious KVO

Toutiao Android ‘second’ compiler speed optimization

Toutiao technology team

The technical team of Jinri Toutiao is not only committed to the business, but also has been pursuing the ultimate user experience in technology.

If you’re looking to grow in a 100-million-dau business, and you’re looking to make leaps and bounds in technology, you’re welcome to join us.

Whether you are iOS/Android/ front end/back end, we are waiting for you in Shenzhen/Beijing/Guangzhou to do more challenging things together! Resume email: [email protected]; Email subject: Name – Years of service – Headliner Technical Team.

Welcome to the Bytedance technical team