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