Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: lib/filterText.js

Issue 29909576: [experiment] Issue 7045 - Optimize V8 memory layout of filter text Base URL: https://hg.adblockplus.org/adblockpluscore/
Patch Set: Make domains parameter nullable and optional Created Oct. 15, 2018, 9:28 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « lib/filterClasses.js ('k') | test/filterText.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 /*
2 * This file is part of Adblock Plus <https://adblockplus.org/>,
3 * Copyright (C) 2006-present eyeo GmbH
4 *
5 * Adblock Plus is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 3 as
7 * published by the Free Software Foundation.
8 *
9 * Adblock Plus is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 "use strict";
19
20 /**
21 * @fileOverview Filter text operations.
22 */
23
24 // The memory optimizations in this file work as of V8 7.1.314.
25
26 /**
27 * The minimum length of a domain string for it to be considered long enough to
28 * be eligible for optimization.
29 * @type {number}
30 */
31 const LONG_DOMAINS_THRESHOLD = 1000;
32
33 /**
34 * Cached strings.
35 * @type {Map.<string,string>}
36 */
37 let strings = new Map();
38
39 /**
40 * Slices a string out of its internal parent string, thus allowing the memory
41 * occupied by the parent string to be freed up.
42 *
43 * JavaScript engines like V8 tend to hold on to the parent of a sliced string
44 * even if there are no references to the parent string. While this
45 * optimization is enormously beneficial in general, in certain cases it has
46 * the opposite effect of needlessly holding on to large amounts of unused
47 * memory (V8 issue #2869). This operation creates an entirely new internal
48 * string with its own copy of the original string's memory, thus allowing the
49 * original string and any of its parents to be freed up.
50 *
51 * Note: This is a relatively expensive operation and should be used only when
52 * the benefit outweighs the cost.
53 *
54 * @param {string} string The string to slice.
55 *
56 * @returns {string} An entirely new copy of the original string.
57 */
58 function trueSlice(string)
59 {
60 return JSON.parse(JSON.stringify(string));
61 }
62
63 /**
64 * Optimizes content filter text.
65 *
66 * @param {string} text The filter text.
67 * @param {?string} [domains] The domains part of the filter text.
68 * @param {?string} [type] The type part of the filter text, either
69 * <code>null</code> or <code>undefined</code>, or one of <code>@</code>,
70 * <code>?</code>, and <code>$</code>.
71 * @param {string} body The body part of the filter text.
72 *
73 * @returns {Array.<string>} An array containing the optimized filter text and
74 * its optimized parts.
75 */
76 function optimizeContentFilterText(text, domains, type, body)
77 {
78 // In EasyList+AA there are a handful of filters with very long domain
79 // strings of 1,000 characters and even up to 100,000 characters. These tend
80 // to take up a lot of memory. We can restructure the text here to optimize
81 // for better memory usage on V8.
82 if (domains && domains.length >= LONG_DOMAINS_THRESHOLD)
83 {
84 let copy = strings.get(domains);
85 if (copy)
86 {
87 // Point to the cached copy.
88 domains = copy;
89
90 // V8 tends to hold on to the parent of a sliced string even if there are
91 // no references to it. We must "slice" out these strings properly so the
92 // original filter text is freed up.
93 // https://bugs.chromium.org/p/v8/issues/detail?id=2869
94 if (typeof type == "string")
95 type = trueSlice(type);
96 body = trueSlice(body);
97
98 // Reconstruct the text with an optimized layout.
99 text = domains + "#" + (type || "") + "#" + body;
100 }
101 else
102 {
103 strings.set(domains, domains);
104 }
105 }
106
107 return [text, domains, type, body];
108 }
109
110 exports.optimizeContentFilterText = optimizeContentFilterText;
111
112 /**
113 * Optimizes blocking and whitelist filter text.
114 *
115 * @param {string} text The filter text.
116 * @param {string} pattern The pattern part of the filter text.
117 * @param {?string} [domains] The domains part of the filter text.
118 * @param {?string} [sitekeys] The sitekeys part of the filter text.
119 * @param {?string} [csp] The CSP part of the filter text.
120 * @param {?string} [rewrite] The rewrite pattern part of the filter text.
121 *
122 * @returns {Array.<string>} An array containing the optimized filter text and
123 * its optimized parts.
124 */
125 function optimizeRegExpFilterText(text, pattern, domains, sitekeys, csp,
126 rewrite)
127 {
128 if (!sitekeys && !csp && !rewrite &&
129 domains && domains.length >= LONG_DOMAINS_THRESHOLD &&
130 text.endsWith(domains))
131 {
132 let copy = strings.get(domains);
133 if (copy)
134 {
135 domains = copy;
136
137 text = trueSlice(text.substring(0, text.length - domains.length));
138
139 if (text[0] == "@" && text[1] == "@")
140 pattern = text.substring(2, 2 + pattern.length);
141 else
142 pattern = text.substring(0, pattern.length);
143
144 // Note: This must be the last operation on the text in order for this
145 // optimization to work. Any further operations on the text may undo the
146 // optimization.
147 text += domains;
148 }
149 else
150 {
151 strings.set(domains, domains);
152 }
153 }
154
155 return [text, pattern, domains, sitekeys, csp, rewrite];
156 }
157
158 exports.optimizeRegExpFilterText = optimizeRegExpFilterText;
OLDNEW
« no previous file with comments | « lib/filterClasses.js ('k') | test/filterText.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld