Perf: minor improvements here and there

This commit is contained in:
SukkaW
2025-01-23 22:30:04 +08:00
parent cf58e215f5
commit fe06774a19
5 changed files with 78 additions and 46 deletions

View File

@@ -23,7 +23,7 @@ if (!fs.existsSync(CACHE_DIR)) {
fs.mkdirSync(CACHE_DIR, { recursive: true }); fs.mkdirSync(CACHE_DIR, { recursive: true });
} }
const agent = new Agent({ allowH2: true }); const agent = new Agent({ allowH2: false });
setGlobalDispatcher(agent.compose( setGlobalDispatcher(agent.compose(
interceptors.retry({ interceptors.retry({

View File

@@ -111,10 +111,8 @@ export function processFilterRulesWithPreload(
}); });
} }
// const R_KNOWN_NOT_NETWORK_FILTER_PATTERN_2 = /(\$popup|\$removeparam|\$popunder|\$cname)/; // many filter that has modifiers can not work on Surge/Clash because browser context is required
// cname exceptional filter can not be parsed by NetworkFilter // we can early bail out those rules
// Surge / Clash can't handle CNAME either, so we just ignore them
const kwfilter = createKeywordFilter([ const kwfilter = createKeywordFilter([
'!', '!',
'?', '?',
@@ -132,10 +130,16 @@ const kwfilter = createKeywordFilter([
// special modifier // special modifier
'$popup', '$popup',
'$removeparam', '$removeparam',
'$redirect',
'$popunder', '$popunder',
'$cname', '$cname',
'$frame', '$frame',
'$domain', '$domain',
'$from',
'$to',
'$csp',
'$replace',
'$urlskip',
// some bad syntax // some bad syntax
'^popup' '^popup'
]); ]);

View File

@@ -91,8 +91,7 @@ describe('Trie', () => {
trie.add('example.com'); trie.add('example.com');
trie.add('moe.sb'); trie.add('moe.sb');
expect(trie.delete('')).toBe(false); expect(trie.delete('no-match.com')).toBe(false);
expect(trie.delete('')).toBe(false);
expect(trie.delete('example.org')).toBe(false); expect(trie.delete('example.org')).toBe(false);
expect(trie.delete('skk.moe')).toBe(true); expect(trie.delete('skk.moe')).toBe(true);
@@ -131,7 +130,7 @@ describe('Trie', () => {
expect(trie.find('.example.com')).toStrictEqual(['blog.example.com', 'cdn.example.com']); expect(trie.find('.example.com')).toStrictEqual(['blog.example.com', 'cdn.example.com']);
expect(trie.find('org')).toStrictEqual(['example.org']); expect(trie.find('org')).toStrictEqual(['example.org']);
expect(trie.find('example.net')).toStrictEqual([]); expect(trie.find('example.net')).toStrictEqual([]);
expect(trie.find('')).toStrictEqual(['example.com', 'example.org', 'blog.example.com', 'cdn.example.com']); expect(trie.dump()).toStrictEqual(['example.com', 'example.org', 'blog.example.com', 'cdn.example.com']);
}); });
it('should be possible to retrieve items matching the given prefix even with a smol trie', () => { it('should be possible to retrieve items matching the given prefix even with a smol trie', () => {
@@ -148,7 +147,7 @@ describe('Trie', () => {
expect(trie.find('.example.com')).toStrictEqual(['.example.com']); expect(trie.find('.example.com')).toStrictEqual(['.example.com']);
expect(trie.find('org')).toStrictEqual(['example.org']); expect(trie.find('org')).toStrictEqual(['example.org']);
expect(trie.find('example.net')).toStrictEqual([]); expect(trie.find('example.net')).toStrictEqual([]);
expect(trie.find('')).toStrictEqual(['.example.com', 'example.org']); expect(trie.dump()).toStrictEqual(['.example.com', 'example.org']);
}); });
it('should be possible to create a trie from an arbitrary iterable.', () => { it('should be possible to create a trie from an arbitrary iterable.', () => {
@@ -192,6 +191,28 @@ describe('surge domainset dedupe', () => {
}); });
describe('smol tree', () => { describe('smol tree', () => {
it('should init tree', () => {
const trie = createTrie([
'skk.moe',
'anotherskk.moe',
'blog.anotherskk.moe',
'blog.skk.moe',
'.cdn.local',
'blog.img.skk.local',
'img.skk.local'
], true);
expect(trie.dump()).toStrictEqual([
'skk.moe',
'anotherskk.moe',
'.cdn.local',
'blog.skk.moe',
'blog.anotherskk.moe',
'img.skk.local',
'blog.img.skk.local'
]);
});
it('should create simple tree - 1', () => { it('should create simple tree - 1', () => {
const trie = createTrie([ const trie = createTrie([
'.skk.moe', 'blog.skk.moe', '.cdn.skk.moe', 'skk.moe', '.skk.moe', 'blog.skk.moe', '.cdn.skk.moe', 'skk.moe',
@@ -264,7 +285,7 @@ describe('smol tree', () => {
]); ]);
}); });
it('should efficiently whitelist domains', () => { it('should effctly whitelist domains', () => {
const trie = createTrie([ const trie = createTrie([
'skk.moe', 'skk.moe',
'anotherskk.moe', 'anotherskk.moe',
@@ -275,16 +296,6 @@ describe('smol tree', () => {
'img.skk.local' 'img.skk.local'
], true); ], true);
expect(trie.dump()).toStrictEqual([
'skk.moe',
'anotherskk.moe',
'.cdn.local',
'blog.skk.moe',
'blog.anotherskk.moe',
'img.skk.local',
'blog.img.skk.local'
]);
trie.whitelist('.skk.moe'); trie.whitelist('.skk.moe');
expect(trie.dump()).toStrictEqual([ expect(trie.dump()).toStrictEqual([

View File

@@ -41,7 +41,7 @@ function deepTrieNodeToJSON(node: TrieNode,
const createNode = <Meta = any>(parent: TrieNode | null = null): TrieNode => [1, parent, new Map<string, TrieNode>(), null] as TrieNode<Meta>; const createNode = <Meta = any>(parent: TrieNode | null = null): TrieNode => [1, parent, new Map<string, TrieNode>(), null] as TrieNode<Meta>;
export function hostnameToTokens(hostname: string, hostnameFromIndex: number): string[] { function hostnameToTokens(hostname: string, hostnameFromIndex: number): string[] {
const tokens = hostname.split('.'); const tokens = hostname.split('.');
const results: string[] = []; const results: string[] = [];
let token = ''; let token = '';
@@ -50,6 +50,8 @@ export function hostnameToTokens(hostname: string, hostnameFromIndex: number): s
token = tokens[i]; token = tokens[i];
if (token.length > 0) { if (token.length > 0) {
results.push(token); results.push(token);
} else {
throw new TypeError(JSON.stringify({ hostname, hostnameFromIndex }, null, 2));
} }
} }
@@ -117,7 +119,9 @@ abstract class Triebase<Meta = any> {
let parent: TrieNode = node; let parent: TrieNode = node;
let token: string; let token: string;
let child: Map<string, TrieNode<Meta>> = node[2];
// reverse lookup from end to start
for (let i = tokens.length - 1; i >= 0; i--) { for (let i = tokens.length - 1; i >= 0; i--) {
token = tokens[i]; token = tokens[i];
@@ -127,8 +131,10 @@ abstract class Triebase<Meta = any> {
parent = node; parent = node;
if (node[2].has(token)) { child = node[2];
node = node[2].get(token)!; // cache node index access is 20% faster than direct access when doing twice
if (child.has(token)) {
node = child.get(token)!;
} else { } else {
return null; return null;
} }
@@ -147,6 +153,8 @@ abstract class Triebase<Meta = any> {
let node: TrieNode = this.$root; let node: TrieNode = this.$root;
let parent: TrieNode = node; let parent: TrieNode = node;
let child: Map<string, TrieNode<Meta>> = node[2];
const onToken = (token: string) => { const onToken = (token: string) => {
// if (token === '') { // if (token === '') {
// return true; // return true;
@@ -154,8 +162,10 @@ abstract class Triebase<Meta = any> {
parent = node; parent = node;
if (node[2].has(token)) { child = node[2];
node = node[2].get(token)!;
if (child.has(token)) {
node = child.get(token)!;
} else { } else {
return null; return null;
} }
@@ -204,12 +214,14 @@ abstract class Triebase<Meta = any> {
const node = nodeStack.shift()!; const node = nodeStack.shift()!;
const suffix = suffixStack.shift()!; const suffix = suffixStack.shift()!;
if (node[2].size) { const child = node[2];
const keys = Array.from(node[2].keys()).sort(Triebase.compare);
if (child.size) {
const keys = Array.from(child.keys()).sort(Triebase.compare);
for (let i = 0, l = keys.length; i < l; i++) { for (let i = 0, l = keys.length; i < l; i++) {
const key = keys[i]; const key = keys[i];
const childNode = node[2].get(key)!; const childNode = child.get(key)!;
// Pushing the child node to the stack for next iteration of DFS // Pushing the child node to the stack for next iteration of DFS
nodeStack.push(childNode); nodeStack.push(childNode);
@@ -271,17 +283,18 @@ abstract class Triebase<Meta = any> {
suffixStack.push(initialSuffix); suffixStack.push(initialSuffix);
let node: TrieNode<Meta> = initialNode; let node: TrieNode<Meta> = initialNode;
let child: Map<string, TrieNode<Meta>> = node[2];
do { do {
node = nodeStack.shift()!; node = nodeStack.shift()!;
const suffix = suffixStack.shift()!; const suffix = suffixStack.shift()!;
child = node[2];
if (node[2].size) { if (child.size) {
const keys = Array.from(node[2].keys()).sort(Triebase.compare); const keys = Array.from(child.keys()).sort(Triebase.compare);
for (let i = 0, l = keys.length; i < l; i++) { for (let i = 0, l = keys.length; i < l; i++) {
const key = keys[i]; const key = keys[i];
const childNode = node[2].get(key)!; const childNode = child.get(key)!;
// Pushing the child node to the stack for next iteration of DFS // Pushing the child node to the stack for next iteration of DFS
nodeStack.push(childNode); nodeStack.push(childNode);
@@ -303,18 +316,21 @@ abstract class Triebase<Meta = any> {
const onLoop = (node: TrieNode, parent: TrieNode, token: string) => { const onLoop = (node: TrieNode, parent: TrieNode, token: string) => {
// Keeping track of a potential branch to prune // Keeping track of a potential branch to prune
// Even if the node size is 1, but the single child is ".", we should retain the branch const child = node[2];
// Since the "." could be special if it is the leaf-est node
const onlyChild = node[2].size === 0 && !node[1];
if (toPrune != null) { // the top-est branch that could potentially being pruned // console.log({
if (!onlyChild) { // child, parent, token
// The branch has moew than single child, retain the branch. // });
// And we need to abort prune the parent, so we set it to null // console.log(this.inspect(0));
if (toPrune !== null) { // the most near branch that could potentially being pruned
if (child.size > 1) {
// The branch has some children, the branch need retain.
// And we need to abort prune that parent branch, so we set it to null
toPrune = null; toPrune = null;
tokenToPrune = null; tokenToPrune = null;
} }
} else if (onlyChild) { } else if (child.size < 1) {
// There is only one token child, or no child at all, we can prune it safely // There is only one token child, or no child at all, we can prune it safely
// It is now the top-est branch that could potentially being pruned // It is now the top-est branch that could potentially being pruned
toPrune = parent; toPrune = parent;
@@ -552,7 +568,6 @@ export class HostnameSmolTrie<Meta = any> extends Triebase<Meta> {
public whitelist(suffix: string, includeAllSubdomain = suffix[0] === '.', hostnameFromIndex = suffix[0] === '.' ? 1 : 0) { public whitelist(suffix: string, includeAllSubdomain = suffix[0] === '.', hostnameFromIndex = suffix[0] === '.' ? 1 : 0) {
const tokens = hostnameToTokens(suffix, hostnameFromIndex); const tokens = hostnameToTokens(suffix, hostnameFromIndex);
const res = this.getSingleChildLeaf(tokens); const res = this.getSingleChildLeaf(tokens);
if (res === null) return; if (res === null) return;
const { node, toPrune, tokenToPrune } = res; const { node, toPrune, tokenToPrune } = res;
@@ -572,7 +587,7 @@ export class HostnameSmolTrie<Meta = any> extends Triebase<Meta> {
// return early if not found // return early if not found
if (missingBit(node[0], START)) return; if (missingBit(node[0], START)) return;
if (tokenToPrune && toPrune) { if (toPrune && tokenToPrune) {
toPrune[2].delete(tokenToPrune); toPrune[2].delete(tokenToPrune);
} else { } else {
node[0] = deleteBit(node[0], START); node[0] = deleteBit(node[0], START);
@@ -587,13 +602,15 @@ export class HostnameTrie<Meta = any> extends Triebase<Meta> {
add(suffix: string, includeAllSubdomain = suffix[0] === '.', meta?: Meta, hostnameFromIndex = suffix[0] === '.' ? 1 : 0): void { add(suffix: string, includeAllSubdomain = suffix[0] === '.', meta?: Meta, hostnameFromIndex = suffix[0] === '.' ? 1 : 0): void {
let node: TrieNode<Meta> = this.$root; let node: TrieNode<Meta> = this.$root;
let child: Map<string, TrieNode<Meta>> = node[2];
const onToken = (token: string) => { const onToken = (token: string) => {
if (node[2].has(token)) { child = node[2];
node = node[2].get(token)!; if (child.has(token)) {
node = child.get(token)!;
} else { } else {
const newNode = createNode(node); const newNode = createNode(node);
node[2].set(token, newNode); child.set(token, newNode);
node = newNode; node = newNode;
} }

View File

@@ -120,7 +120,7 @@ export function task(importMetaMain: boolean, importMetaPath: string) {
dummySpan.traceChildAsync('dummy', (childSpan) => fn(childSpan, onCleanup)).finally(() => { dummySpan.traceChildAsync('dummy', (childSpan) => fn(childSpan, onCleanup)).finally(() => {
dummySpan.stop(); dummySpan.stop();
printTraceResult(dummySpan.traceResult); printTraceResult(dummySpan.traceResult);
whyIsNodeRunning(); process.nextTick(whyIsNodeRunning);
}); });
} }