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