-
Notifications
You must be signed in to change notification settings - Fork 40
过滤规则性能
也可参见:另类语法和 uBlockOrigin/uBlock-issues#2855
参见:
- https://reddit.com/r/uBlockOrigin/comments/wxdyfn/comment/ilqyveu/
- https://reddit.com/r/uBlockOrigin/comments/j4ewg7/comment/g7imasx/
请记住 Chromium 目前已原生支持 :has()
(Firefox 平台上也很快会默认启用),因此原生的 :has()
将比需要 Javascript 代码才可运行的 :upward()
高效很多。
有时 :upward()
可能 比 :has()
性能更好。:upward()
很快,因为它只查找祖先节点 -- 每个元素只有一个父节点,而 :has()
得查找后台节点,但每个元素可能有多个子节点。
<div class="widget">
<span class="h-text">Promoted Links</span>
<div class="textwidget">
<ul>
<li>
<a href="http://example.com" target="_blank" rel="nofollow noopener">Ad Link</a>
</li>
</ul>
</div>
在上例中,使用 ##.widget .h-text:has-text(Promoted Links):upward(.widget)
代替 ##.widget:has(.h-text:has-text(Promoted Links))
可能会更好。
对于所有静态网络规则,请确保它们拥有尽可能多的缩围选项,即生效目标要精确。单纯基于主机名的规则(例如 ||example.com^
)在内存和 CPU 占用方面拥有最佳优化效果。对于非单纯基于主机名的规则,即基于模板的规则(例如 ||example.com/path/to/file
), 请确保它们可令牌化,利用规则选项缩小它们的生效目标范围,或在仅需要在特定网站生效时使用 domain=
。
对于静态网络规则,纯主机名规则优化效果最好,因为它们被存储在一个压缩过的前缀树中,Firefox 可使用 WebAssembly 代码查找它们。有了前缀树,用于成功找到匹配所执行的比较次数,将始终与主机名中匹配的字符数成比例,比规则列表中的过滤规则数量更少。因此即使这类规则数以万计,也不会对性能有影响。
基于模板的规则,即 ||example.com/path/to/file^
这样的规则,如果 uBO 认为值得,可能会被存储在前缀树里,但无论如何,它们最后都会存储在 TypedArray 里,并且在 Firefox 中再次使用 WebAssembly 代码进行模式匹配。
如果存在一个通配符(出现在中间某处),则需要在 TypedArray 存储两个模板,并使用特殊的匹配算法进行匹配。
如果是两个或多个通配符,则 uBO 会回退并在内部使用正则表达式,因此要尽可能避免这么做。基于正则表达式的规则(应该很少见)在内部使用会被当成正则表达式。
仅当要进行模板匹配的 URL 里发现有匹配的令牌时才会访问基于模板的过滤规则,因此即使你的规则列表充斥着有两个或多个通配符的过滤规则,仅当从 URL 提取出匹配的令牌时,uBO 才会执行相关的内部正则表达式。比如在规则列表里包含出现了多个通配符的过滤规则也不会产生任何问题,只要在这些规则里能提取出良好的令牌。
因此对于非单纯基于主机名的规则,首要考虑的是其是否可令牌化,然后是否规则包含所有必要的缩围选项,以避免在不必要的情况下访问这些规则。
首要的缩围选项包括:可令牌化、1p
、3p
、image
、script
、frame
等等。这些选项可以完全阻止过滤规则被访问。
举个例子,如果过滤规则在使用 domain=
选项的同时加上 3p
这个选项,只要是第一方的网络请求,uBO 就完全不会访问这条过滤规则。如果没有 3p
选项,uBO 将访问过滤规则尝试与所有网络请求进行匹配,在它发现与 domain=
选项匹配不上时才会放弃访问。有了 3p
这个选项,凡是第一方请求,这条规则都不会被访问,就好像规则不存在一样。上面都是从概念上来说的,实际情况是如果其他条件相同,上面两条规则没有什么可测量到的差别,每条请求匹配时间仅有数十毫秒。
次要的缩围的选项是 domain=
,它没有首要的选项效率高。domain=
选项不会阻止过滤规则被访问,但在选项匹配不上时 uBO 不必执行这些规则,它的模板匹配代码更消耗资源。
让我们看看一个最坏的情况, EasyList 有一条不包含任何缩围选项的规则:
/^https?:\/\/(.+?\.)?allthingsvegas\.com\/[a-zA-Z]{7,18}\//
这是一条基于正则表达式的过滤规则。虽然它不包含任何缩围选项。但对于 uBO 仍是一条高效的规则,因为它包含可被提取的令牌 allthingsvegas
。这条规则只有在 uBO 发现网络请求的 URL 里包含 allthingsvegas
令牌时才会访问,但这种情况相当少见,除非它实际上原本就要强制执行。
再看看 EasyList 里的另一条规则:
/(https?:\/\/)\w{30,}.me\/\w{30,}\./$script,third-party
我们关注到这条规则提取不出任何令牌(.
转义以后有可能可以提取出有效的令牌),但它包含缩围选项 script
和 3p
,至少这条规则只有在网络请求类型是脚本且为第三方时才会被访问。
来源:
- https://reddit.com/r/uBlockOrigin/comments/mdh9jz/order_of_complexity_for_network_filters/gsa7gd2/
- https://reddit.com/r/uBlockOrigin/comments/mdh9jz/order_of_complexity_for_network_filters/gxzxfo0/
3- 为了避免一条模板匹配的过滤规则被当成正则表达式规则,你需要在末尾添加一个通配符。
/path/to/
上述规则会被当成正则表达式规则,因为它开头和末尾都有一个 /
。为了把它变成一条普通的模板匹配规则,要在末尾添加一个通配符:
/path/to/*
虽然处理时末尾的通配符会被去掉,但这已经是一条包含有 /path/to/
的模板匹配规则。
来源:https://reddit.com/r/uBlockOrigin/comments/mdh9jz/order_of_complexity_for_network_filters/gsfn0xl/ 相关讨论:Proceeding wildcards in rules
最好是完全避免 removeparam
被访问(可通过使用缩围选项和令牌化的方法),我真的希望过滤规则作者在编写带 removeparam
的规则时能像我在代码里尽量减小开销那样仔细认真,否则所有代码方面的努力都会付之东流。
如果没有可用的模板,uBO 会尝试从 removeparam=
值里推导出一个模板。例如,*$doc,xhr,removeparam=utm_
会在内部被翻译为 utm_$doc,xhr,removeparam=utm_
,因此 uBO 可以使用 utm
作为该条规则的令牌。记录台将始终输出内部翻译后的版本。
在执行 removeparam
代码之前,uBO 也会在内部进行 一遍快速检查,以便查看是否有查询参数,如果没有的,就不执行 removeparam
代码。
来源:
- https://github.com/uBlockOrigin/uBlock-issues/issues/760#issuecomment-719952467
- https://github.com/uBlockOrigin/uBlock-issues/issues/760#issuecomment-726824559
- https://github.com/uBlockOrigin/uBlock-issues/issues/760#issuecomment-734440655
- https://github.com/DandelionSprout/adfilt/discussions/163#discussioncomment-5207364
uBlock Origin - 一款支持 Chromium、Firefox 和 Safari 的高效过滤工具,快速且简洁