Adblockers Performance Study

Feb 15th, 2019

In this study, we show

About the adblocker performance study

Here, we present a detailed analysis of the performance of some of the most popular adblockers and content-blocker engines: uBlock Origin, Adblock Plus, Brave, DuckDuckGo and Cliqz/Ghostery's advanced adblocker (shipped since Ghostery 8), which we will refer to as Ghostery for the rest of the article.

Why was the study conducted?

This study was motivated by the recent Manifest V3 controversy. One of the proposed changes involves crippling the WebRequest APIs to limit their blocking abilities.

Two justifications were put forth: one related to performance and another related to privacy. The privacy argument deserves its own separate analysis and will not be covered here.

What were the findings?

In this study, we show that the performance argument does not hold. Our comparison demonstrates that the most popular content-blockers and adblockers are already very efficient (having a sub-millisecond median decision time per request) and should not result in any overhead noticeable by users.

We showed in another study The Tracker Tax that blocking ads and trackers actually reduces the loading time of websites by up to a factor of 2. Besides, efficiency is continuously improved and technologies such as WebAssembly will enable further optimizations.

What did the study compare?

This comparison does not involve full extensions, but instead focuses on network request-blocking engines. This is the most CPU-intensive task performed by content-blockers (in particular, this does not account for cosmetics engines or subscription management).

Here are the home pages for all content-blockers compared:

We did not include native blockers from Chromium and Safari projects as this would require some significant effort to package them in a way that allows benchmarking against the other libraries. We leave this for future work.

How were the adblockers compared?

Every adblocker, except uBlock Origin, are available as JavaScript libraries which can be loaded in Node.js. To allow a comparison of uBlock Origin, we had to extract the static network filtering engine out of the extension. The version of uBlock Origin running in this benchmark does not make use of the Webassembly version of domain matching.

All benchmarks were run on an X1 Carbon 2016 (i7 U6600 + 16 GB) in Node.js 11.9.0. Memory measurements were performed in Google Chrome version 72.0.3626.96 using the memory snapshot tool.


Before presenting the detailed analysis of the results, let us highlight our findings in a nutshell:

0. About the Dataset

To measure the performance of each adblocker, we replayed requests from popular domains and tracked the time it took to decide if they should be blocked or not.

We then analyzed the results in three different ways: all requests, blocked only and not blocked (taken from the same run).

How the dataset was created

The dataset was created using a pool of Chrome headless browsers (driven by the puppeteer library) to visit home pages of the top 500 domains (as reported by Cliqz Search). Up to 3 pages of each domain (picked randomly from the home page) and all the network requests seen (URL, frame URL and type) were also collected.

The dataset was shuffled in such a way that the different pages were visited in a random order, but requests seen on each page were replayed as they were recorded initially.

In summary:

1. Composition of Requests

For the purpose of this comparison, we consider that each network request can be either blocked or allowed by the content-blocker; we call the process of deciding whether a request should be blocked or not: matching.

We observed that from our dataset, only ~19.2% are blocked (average across all content-blockers).

Composition of requests

Key takeaway: how to make an adblocker more effective

This observation suggests that content-blockers will perform better on average if they can efficiently decide which requests to not block.

The filters used to determine whether or not a request is to be blocked are the ones from Easylist, where we removed all the cosmetic rules before running the benchmarks. The final list contains 38978 network filters and is available here: easylist.txt.

It should be noted at this point that a larger proportion of requests would be blocked by enabling extra filters lists such as EasyPrivacy.

2. Time To Match All Requests

We first look at all of the requests (whether they will eventually be blocked or not).

We use a log scale for the x-axis (time in milliseconds) to facilitate the comparison of the cumulative distribution of the time it takes for adblockers to decide whether or not a request should be blocked.

Here is a break-down of the 99th percentile and median times for each content-blocker:

Ghostery 0.050ms 0.007ms
uBlock Origin 0.124ms (2.5x slower) 0.017ms (2.7x slower)
Adblock Plus 0.103ms (2.1x slower) 0.019ms (2.9x slower)
Brave 1.288ms (25.9x slower) 0.041ms (6.3x slower)
DuckDuckGo 12.085ms (242.5x slower) 8.270ms (1258.4x slower)

Below you can find the cumulative distribution plots of these timings:

Time To Match All Requests

3. Time To Match Requests Which Are Not Blocked

The following table details 99th percentile and median timings for requests not blocked:

Ghostery 0.049ms 0.006ms
uBlock Origin 0.112ms (2.3x slower) 0.018ms (2.8x slower)
Adblock Plus 0.105ms (2.2x slower) 0.020ms (3.1x slower)
Brave 1.270ms (26.2x slower) 0.038ms (5.9x slower)
DuckDuckGo 11.190ms (230.5x slower) 6.781ms (1060.5x slower)

Time to match requests which are not blocked

4. Time To Match Requests Which Are Blocked

The following table details 99th percentile and median timings for requests blocked:

Ghostery 0.052ms 0.007ms
uBlock Origin 0.165ms (3.1x slower) 0.016ms (2.2x slower)
Adblock Plus 0.099ms (1.9x slower) 0.014ms (1.9x slower)
Brave 1.468ms (28.0x slower) 0.062ms (8.5x slower)
DuckDuckGo 13.025ms (248.5x slower) 8.31ms (1130.6x slower)

Time to match requests which are blocked

Summary of findings: How the adblockers performed

On these graphs, we observe a plateau for Adblock Plus, Brave and Duckduckgo.

This can be explained by the fact that these engines implement some form of caching internally, thus having a very fast response time for some requests (redundancy in requests comes from both common third parties seen on multiple websites as well as the fact that we load several pages for each domain).

This caching can be implemented on top of any content-blocker and does not tell much about the efficiency of each. We can see this as a means to trade memory against CPU usage.

Ghostery’s adblocker performance

From the previous measurements, we see that Ghostery outperforms other libraries in terms of matching speed. Without going into too many details, here are some of the optimizations which can explain these results:

5. Serialization And Deserialization

In this section, we have a look at the performance of content-blockers when it comes to serializing their internal representation for faster subsequent loading.

Only DuckDuckGo's engine does not provide this feature. uBlock Origin, Ghostery, Adblock Plus and Brave all allow to serialize or cache (uBlock Origin's terminology is: selfies) the entire blocking engine to either a string or a buffer, which can then be used to speed-up subsequent loads.

The impact of load time on user experience

As this is a one-time operation, having a higher loading time does not impact desktop users significantly. On the other hand, the ability to quickly initialize the content-blocker is critical on mobile.

Comparison of serialization and deserialization times

Another use-case allowed by such capability is to perform the parsing of the lists on the backend and ship the serialized form of the content-blocker to clients directly. This removes the cost of initialization completely.

We performed 100 serializations for each content-blocker and display the results below:

Serialization timings

This bar plot contains the median time taken to serialize the engine for each content-blocker:

Serialization timings

Similarly, we measure the time it takes to restore the content-blocker from its serialized form:

Deserialization timings

And here is the median time:

Deserialization timings

Last but not least, we measured the size of the serialized buffer for each content-blocker:

Cache size

From these measurements, we see that Ghostery offers both significantly faster serialization and deserialization times as well as a smaller cache size.

The reason is the following: * The internal representation is already mostly stored in a compact form (using typed arrays). * This means that serialization only adds a small amount of metadata alongside the already available arrays. Deserialization is essentially instantaneous since it's enough to create some typed array views on top of the serialized buffer (think of mmap but using typed arrays). * This also explains the very low memory consumption: after initialization, the memory footprint is only slightly higher than the size of the serialized form.

6. Memory Consumption at Start-up

Here, we consider the memory usage of each content-blocker, initialized from lists (not from cache) after one full garbage collection.

How memory consumption was measured

The measurements were performed using Chrome's devtools memory snapshot. We did not measure Brave here since the memory used from C++ side does not seem to be taken into account in the snapshot. Keep in mind that this memory usage can vary at run-time as content-blockers might cache frequently used resources, etc.

Memory usage at start-up

As mentioned in the previous section on serialization, the very low memory usage of Ghostery can be explained by the fact that the internal representation mostly consists of very compact typed arrays with some small overhead for extra meta-data.

Again, we need to stress here that this measures the network filtering engine of Ghostery only, not the full extension, as described in the introduction.

7. Parsing Lists

In this graph, we present the time it takes for each content-blocker to be initialized from the lists (without any prior caching, which means initializing all internal resources by parsing the raw list).

We see that only Brave seems to be significantly slower and that uBlock Origin, Ghostery, Adblock Plus and DuckDuckGo all perform well.

Time to parse Easylist

It seems that the long parsing time for Brave is a known issue tracked on their GitHub repository.

Key findings on parsing lists

If we remove Brave, we see that there are still differences between uBlock Origin, Ghostery, Adblock Plus and DuckDuckGo. One reason Ghostery is slower than uBlock Origin and AdblockPlus here is that to achieve maximum performance while matching as well as minimize memory usage, there is a bit more work to do up-front.

In practice, this does not matter so much since it is a one-time operation and subsequent loads are performed from cache. This is really fast. In fact, we can even perform the parsing backend-side and just ship the serialized version of the blocker, which removes this step completely.

Time to parse Easylist without Brave

8. Conclusion

In this study, we looked closely at the performance of some of the most popular content-blockers in use today. In particular, we focused on the efficiency of their network filtering engines, which is the most CPU intensive task they perform.

This work was motivated by one of the claims formulated in the Manifest V3 proposal of the Chromium project: "the extension then performs arbitrary (and potentially very slow) JavaScript", talking about content-blockers' ability to process all network requests.

Key findings:

We hope these benchmarks will give content-blocker developers the opportunity to measure their own progress against other popular libraries. This will benefit all users, no matter which extension they use, as the efficiency of content-blockers improves.

Edit of 20-02-2019: The study has been updated with the specific version of each content-blocker measured.

Edit of 15-03-2019: DuckDuckGo's description has been amended to more accurately describe the way their content-blocker is used in practice: focusing on blocking third-party trackers, but not ads.