1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5 use cssparser::SourceLocation;
6 use rayon;
7 use servo_arc::Arc;
8 use servo_url::ServoUrl;
9 use style::context::QuirksMode;
10 use style::error_reporting::{ParseErrorReporter, ContextualParseError};
11 use style::media_queries::MediaList;
12 use style::properties::{longhands, Importance, PropertyDeclaration, PropertyDeclarationBlock};
13 use style::rule_tree::{CascadeLevel, RuleTree, StrongRuleNode, StyleSource};
14 use style::shared_lock::SharedRwLock;
15 use style::stylesheets::{Origin, Stylesheet, CssRule};
16 use style::thread_state::{self, ThreadState};
17 use test::{self, Bencher};
18
19 struct ErrorringErrorReporter;
20 impl ParseErrorReporter for ErrorringErrorReporter {
report_error(&self, url: &ServoUrl, location: SourceLocation, error: ContextualParseError)21 fn report_error(&self,
22 url: &ServoUrl,
23 location: SourceLocation,
24 error: ContextualParseError) {
25 panic!("CSS error: {}\t\n{}:{} {}", url.as_str(), location.line, location.column, error);
26 }
27 }
28
29 struct AutoGCRuleTree<'a>(&'a RuleTree);
30
31 impl<'a> AutoGCRuleTree<'a> {
new(r: &'a RuleTree) -> Self32 fn new(r: &'a RuleTree) -> Self {
33 AutoGCRuleTree(r)
34 }
35 }
36
37 impl<'a> Drop for AutoGCRuleTree<'a> {
drop(&mut self)38 fn drop(&mut self) {
39 unsafe {
40 self.0.gc();
41 assert!(::std::thread::panicking() ||
42 !self.0.root().has_children_for_testing(),
43 "No rule nodes other than the root shall remain!");
44 }
45 }
46 }
47
parse_rules(css: &str) -> Vec<(StyleSource, CascadeLevel)>48 fn parse_rules(css: &str) -> Vec<(StyleSource, CascadeLevel)> {
49 let lock = SharedRwLock::new();
50 let media = Arc::new(lock.wrap(MediaList::empty()));
51
52 let s = Stylesheet::from_str(css,
53 ServoUrl::parse("http://localhost").unwrap(),
54 Origin::Author,
55 media,
56 lock,
57 None,
58 Some(&ErrorringErrorReporter),
59 QuirksMode::NoQuirks,
60 0);
61 let guard = s.shared_lock.read();
62 let rules = s.contents.rules.read_with(&guard);
63 rules.0.iter().filter_map(|rule| {
64 match *rule {
65 CssRule::Style(ref style_rule) => Some((
66 StyleSource::from_rule(style_rule.clone()),
67 CascadeLevel::UserNormal,
68 )),
69 _ => None,
70 }
71 }).collect()
72 }
73
test_insertion(rule_tree: &RuleTree, rules: Vec<(StyleSource, CascadeLevel)>) -> StrongRuleNode74 fn test_insertion(rule_tree: &RuleTree, rules: Vec<(StyleSource, CascadeLevel)>) -> StrongRuleNode {
75 rule_tree.insert_ordered_rules(rules.into_iter())
76 }
77
test_insertion_style_attribute(rule_tree: &RuleTree, rules: &[(StyleSource, CascadeLevel)], shared_lock: &SharedRwLock) -> StrongRuleNode78 fn test_insertion_style_attribute(rule_tree: &RuleTree, rules: &[(StyleSource, CascadeLevel)],
79 shared_lock: &SharedRwLock)
80 -> StrongRuleNode {
81 let mut rules = rules.to_vec();
82 rules.push((StyleSource::from_declarations(Arc::new(shared_lock.wrap(PropertyDeclarationBlock::with_one(
83 PropertyDeclaration::Display(
84 longhands::display::SpecifiedValue::Block),
85 Importance::Normal
86 )))), CascadeLevel::UserNormal));
87 test_insertion(rule_tree, rules)
88 }
89
90 #[bench]
bench_insertion_basic(b: &mut Bencher)91 fn bench_insertion_basic(b: &mut Bencher) {
92 let r = RuleTree::new();
93 thread_state::initialize(ThreadState::SCRIPT);
94
95 let rules_matched = parse_rules(
96 ".foo { width: 200px; } \
97 .bar { height: 500px; } \
98 .baz { display: block; }");
99
100 b.iter(|| {
101 let _gc = AutoGCRuleTree::new(&r);
102
103 for _ in 0..(4000 + 400) {
104 test::black_box(test_insertion(&r, rules_matched.clone()));
105 }
106 })
107 }
108
109 #[bench]
bench_insertion_basic_per_element(b: &mut Bencher)110 fn bench_insertion_basic_per_element(b: &mut Bencher) {
111 let r = RuleTree::new();
112 thread_state::initialize(ThreadState::SCRIPT);
113
114 let rules_matched = parse_rules(
115 ".foo { width: 200px; } \
116 .bar { height: 500px; } \
117 .baz { display: block; }");
118
119 b.iter(|| {
120 let _gc = AutoGCRuleTree::new(&r);
121
122 test::black_box(test_insertion(&r, rules_matched.clone()));
123 });
124 }
125
126 #[bench]
bench_expensive_insertion(b: &mut Bencher)127 fn bench_expensive_insertion(b: &mut Bencher) {
128 let r = RuleTree::new();
129 thread_state::initialize(ThreadState::SCRIPT);
130
131 // This test case tests a case where you style a bunch of siblings
132 // matching the same rules, with a different style attribute each
133 // one.
134 let rules_matched = parse_rules(
135 ".foo { width: 200px; } \
136 .bar { height: 500px; } \
137 .baz { display: block; }");
138
139 let shared_lock = SharedRwLock::new();
140 b.iter(|| {
141 let _gc = AutoGCRuleTree::new(&r);
142
143 for _ in 0..(4000 + 400) {
144 test::black_box(test_insertion_style_attribute(&r, &rules_matched, &shared_lock));
145 }
146 });
147 }
148
149 #[bench]
bench_insertion_basic_parallel(b: &mut Bencher)150 fn bench_insertion_basic_parallel(b: &mut Bencher) {
151 let r = RuleTree::new();
152 thread_state::initialize(ThreadState::SCRIPT);
153
154 let rules_matched = parse_rules(
155 ".foo { width: 200px; } \
156 .bar { height: 500px; } \
157 .baz { display: block; }");
158
159 b.iter(|| {
160 let _gc = AutoGCRuleTree::new(&r);
161
162 rayon::scope(|s| {
163 for _ in 0..4 {
164 s.spawn(|s| {
165 for _ in 0..1000 {
166 test::black_box(test_insertion(&r,
167 rules_matched.clone()));
168 }
169 s.spawn(|_| {
170 for _ in 0..100 {
171 test::black_box(test_insertion(&r,
172 rules_matched.clone()));
173 }
174 })
175 })
176 }
177 });
178 });
179 }
180
181 #[bench]
bench_expensive_insertion_parallel(b: &mut Bencher)182 fn bench_expensive_insertion_parallel(b: &mut Bencher) {
183 let r = RuleTree::new();
184 thread_state::initialize(ThreadState::SCRIPT);
185
186 let rules_matched = parse_rules(
187 ".foo { width: 200px; } \
188 .bar { height: 500px; } \
189 .baz { display: block; }");
190
191 let shared_lock = SharedRwLock::new();
192 b.iter(|| {
193 let _gc = AutoGCRuleTree::new(&r);
194
195 rayon::scope(|s| {
196 for _ in 0..4 {
197 s.spawn(|s| {
198 for _ in 0..1000 {
199 test::black_box(test_insertion_style_attribute(&r,
200 &rules_matched,
201 &shared_lock));
202 }
203 s.spawn(|_| {
204 for _ in 0..100 {
205 test::black_box(test_insertion_style_attribute(&r,
206 &rules_matched,
207 &shared_lock));
208 }
209 })
210 })
211 }
212 });
213 });
214 }
215