Avoid matching the block-important realm unconditionally

When matching a network request in the static network filtering
engine ("snfe"), these are the possible outcomes, from most
to least likely:

- No block
- Block
- Unblock ("exception" filter overriding the block)
- Block-important ("important" filter override the unblock)

Hence why the matching in the snfe always check for a match in
the "block" realm, and the "unblock" realm would be checked
if and only if there was a match in the "block" realm.

However the "block-important" realm was always matched against
first, and when a match in that realm was found, there would
be no need to check in other realms since nothing can override
the "important" option. The problem with this approach though
is that matches in the "block-important" realm are most
unlikely, which means pointless work being done for vast
majority of network requests.

This commit makes it so that the "block-important" realm is
matched against ONLY when there is a matched "unblock" filter.
The result is a measurable improvement in the snfe-related
benchmarks (though given the numbers involved, end users won't
perceive a difference).

Somewhat related discussion which was the motivation to look
more into this:

https://github.com/cliqz-oss/adblocker/discussions/2170#discussioncomment-1168125
This commit is contained in:
Raymond Hill 2021-08-16 10:58:04 -04:00
parent 10ca7438d7
commit a2a8ef7e85
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
2 changed files with 59 additions and 10 deletions

View File

@ -154,7 +154,7 @@ const µBlock = { // jshint ignore:line
// Read-only // Read-only
systemSettings: { systemSettings: {
compiledMagic: 37, // Increase when compiled format changes compiledMagic: 38, // Increase when compiled format changes
selfieMagic: 38, // Increase when selfie format changes selfieMagic: 38, // Increase when selfie format changes
}, },

View File

@ -297,9 +297,6 @@ class LogData {
isRegex: false, isRegex: false,
}; };
filterUnits[iunit].logData(logData); filterUnits[iunit].logData(logData);
if ( (categoryBits & Important) !== 0 ) {
logData.options.unshift('important');
}
if ( (categoryBits & ThirdParty) !== 0 ) { if ( (categoryBits & ThirdParty) !== 0 ) {
logData.options.unshift('3p'); logData.options.unshift('3p');
} else if ( (categoryBits & FirstParty) !== 0 ) { } else if ( (categoryBits & FirstParty) !== 0 ) {
@ -599,6 +596,42 @@ registerFilterClass(FilterTrue);
/******************************************************************************/ /******************************************************************************/
// The only purpose of this class is so that the `important` filter
// option is added to the logged raw filter.
const FilterImportant = class {
match() {
return true;
}
logData(details) {
details.options.unshift('important');
}
toSelfie() {
return FilterImportant.compile();
}
static compile() {
return [ FilterImportant.fid ];
}
static fromCompiled() {
return new FilterImportant();
}
static fromSelfie() {
return new FilterImportant();
}
static keyFromArgs() {
}
};
registerFilterClass(FilterImportant);
/******************************************************************************/
const FilterPatternPlain = class { const FilterPatternPlain = class {
constructor(i, n) { constructor(i, n) {
this.i = i | 0; this.i = i | 0;
@ -3398,6 +3431,11 @@ class FilterCompiler {
units.push(FilterAnchorRight.compile()); units.push(FilterAnchorRight.compile());
} }
// Important
if ( (this.action & Important) !== 0 ) {
units.push(FilterImportant.compile());
}
// Strict partiness // Strict partiness
if ( this.strictParty !== 0 ) { if ( this.strictParty !== 0 ) {
units.push(FilterStrictParty.compile(this)); units.push(FilterStrictParty.compile(this));
@ -3434,7 +3472,19 @@ class FilterCompiler {
const fdata = units.length === 1 const fdata = units.length === 1
? units[0] ? units[0]
: FilterCompositeAll.compile(units); : FilterCompositeAll.compile(units);
this.compileToAtomicFilter(fdata, writer); this.compileToAtomicFilter(fdata, writer);
// Add block-important filters to the block realm, so as to avoid
// to unconditionally match against the block-important realm for
// every network request. Block-important filters are quite rare so
// the block-important realm should be checked when and only when
// there is a matched exception filter, which important filters are
// meant to override.
if ( (this.action & BlockImportant) !== 0 ) {
this.action &= ~Important;
this.compileToAtomicFilter(fdata, writer);
}
} }
compileToAtomicFilter(fdata, writer) { compileToAtomicFilter(fdata, writer) {
@ -4202,15 +4252,14 @@ FilterContainer.prototype.matchRequest = function(fctxt, modifiers = 0) {
$docEntity.reset(); $docEntity.reset();
$requestHostname = fctxt.getHostname(); $requestHostname = fctxt.getHostname();
// Important block realm. // Evaluate block realm before allow realm, and allow realm before
if ( this.realmMatchString(BlockImportant, typeValue, partyBits) ) { // block-important realm, i.e. by order of likelihood of a match.
return 1;
}
// Evaluate block realm before allow realm.
const r = this.realmMatchString(BlockAction, typeValue, partyBits); const r = this.realmMatchString(BlockAction, typeValue, partyBits);
if ( r || (modifiers & 0b0010) !== 0 ) { if ( r || (modifiers & 0b0010) !== 0 ) {
if ( this.realmMatchString(AllowAction, typeValue, partyBits) ) { if ( this.realmMatchString(AllowAction, typeValue, partyBits) ) {
if ( this.realmMatchString(BlockImportant, typeValue, partyBits) ) {
return 1;
}
return 2; return 2;
} }
if ( r ) { return 1; } if ( r ) { return 1; }