|
| 1 | +use std::hint::black_box; |
| 2 | +use std::path::Path; |
| 3 | +use std::sync::Arc; |
| 4 | +use std::time::Duration; |
| 5 | +use std::time::Instant; |
| 6 | + |
| 7 | +use deno_ast::MediaType; |
| 8 | +use deno_ast::ModuleSpecifier; |
| 9 | +use deno_ast::ParsedSource; |
| 10 | +use dprint_plugin_typescript::configuration::BuiltinCategory; |
| 11 | +use dprint_plugin_typescript::configuration::Configuration; |
| 12 | +use dprint_plugin_typescript::configuration::ConfigurationBuilder; |
| 13 | +use dprint_plugin_typescript::configuration::ImportGroup; |
| 14 | +use dprint_plugin_typescript::configuration::ImportGroupMatch; |
| 15 | +use dprint_plugin_typescript::configuration::ImportMatcher; |
| 16 | +use dprint_plugin_typescript::configuration::SortOrder; |
| 17 | +use dprint_plugin_typescript::format_parsed_source; |
| 18 | +use dprint_plugin_typescript::format_text; |
| 19 | +use dprint_plugin_typescript::FormatTextOptions; |
| 20 | + |
| 21 | +fn main() { |
| 22 | + let iterations = env_usize("DPRINT_BENCH_ITERS").unwrap_or(10); |
| 23 | + let import_count = env_usize("DPRINT_BENCH_IMPORTS").unwrap_or(2_000); |
| 24 | + let named_count = env_usize("DPRINT_BENCH_NAMED_IMPORTS").unwrap_or(2_000); |
| 25 | + let parent_segments = env_usize("DPRINT_BENCH_PARENT_SEGMENTS").unwrap_or(6).max(1); |
| 26 | + let config = ConfigurationBuilder::new().build(); |
| 27 | + let maintain_order_config = ConfigurationBuilder::new() |
| 28 | + .module_sort_import_declarations(SortOrder::Maintain) |
| 29 | + .module_sort_export_declarations(SortOrder::Maintain) |
| 30 | + .import_declaration_sort_named_imports(SortOrder::Maintain) |
| 31 | + .export_declaration_sort_named_exports(SortOrder::Maintain) |
| 32 | + .build(); |
| 33 | + let grouped_imports_config = ConfigurationBuilder::new() |
| 34 | + .module_import_groups(vec![ |
| 35 | + import_group(BuiltinCategory::Builtin), |
| 36 | + import_group(BuiltinCategory::External), |
| 37 | + import_group(BuiltinCategory::Parent), |
| 38 | + ImportGroup { |
| 39 | + matchers: ImportGroupMatch::Multiple(vec![ |
| 40 | + ImportMatcher::Category(BuiltinCategory::Sibling), |
| 41 | + ImportMatcher::Category(BuiltinCategory::Index), |
| 42 | + ]), |
| 43 | + }, |
| 44 | + ]) |
| 45 | + .build(); |
| 46 | + |
| 47 | + let sorted_imports_text = sorted_import_declarations(import_count, parent_segments); |
| 48 | + let sorted_exports_text = sorted_export_declarations(import_count, parent_segments); |
| 49 | + let grouped_imports_text = grouped_import_declarations(import_count, parent_segments); |
| 50 | + let sorted_named_imports_text = sorted_named_imports(named_count); |
| 51 | + let sorted_named_exports_text = sorted_named_exports(named_count); |
| 52 | + let sorted_imports_parsed = parse_source(&sorted_imports_text); |
| 53 | + let sorted_exports_parsed = parse_source(&sorted_exports_text); |
| 54 | + let grouped_imports_parsed = parse_source(&grouped_imports_text); |
| 55 | + let sorted_named_imports_parsed = parse_source(&sorted_named_imports_text); |
| 56 | + let sorted_named_exports_parsed = parse_source(&sorted_named_exports_text); |
| 57 | + |
| 58 | + println!("iterations: {iterations}"); |
| 59 | + println!("import declarations: {import_count}"); |
| 60 | + println!("named imports: {named_count}"); |
| 61 | + println!("max parent segments: {parent_segments}"); |
| 62 | + |
| 63 | + bench("sorted_import_declarations/full", iterations, || { |
| 64 | + run_format_text(&sorted_imports_text, &config) |
| 65 | + }); |
| 66 | + bench("sorted_import_declarations/parsed", iterations, || { |
| 67 | + run_format_parsed_source(&sorted_imports_parsed, &config) |
| 68 | + }); |
| 69 | + bench("maintain_import_declarations/parsed", iterations, || { |
| 70 | + run_format_parsed_source(&sorted_imports_parsed, &maintain_order_config) |
| 71 | + }); |
| 72 | + bench("sorted_export_declarations/full", iterations, || { |
| 73 | + run_format_text(&sorted_exports_text, &config) |
| 74 | + }); |
| 75 | + bench("sorted_export_declarations/parsed", iterations, || { |
| 76 | + run_format_parsed_source(&sorted_exports_parsed, &config) |
| 77 | + }); |
| 78 | + bench("maintain_export_declarations/parsed", iterations, || { |
| 79 | + run_format_parsed_source(&sorted_exports_parsed, &maintain_order_config) |
| 80 | + }); |
| 81 | + bench("grouped_import_declarations/full", iterations, || { |
| 82 | + run_format_text(&grouped_imports_text, &grouped_imports_config) |
| 83 | + }); |
| 84 | + bench("grouped_import_declarations/parsed", iterations, || { |
| 85 | + run_format_parsed_source(&grouped_imports_parsed, &grouped_imports_config) |
| 86 | + }); |
| 87 | + bench("sorted_named_imports/full", iterations, || { |
| 88 | + run_format_text(&sorted_named_imports_text, &config) |
| 89 | + }); |
| 90 | + bench("sorted_named_imports/parsed", iterations, || { |
| 91 | + run_format_parsed_source(&sorted_named_imports_parsed, &config) |
| 92 | + }); |
| 93 | + bench("maintain_named_imports/parsed", iterations, || { |
| 94 | + run_format_parsed_source(&sorted_named_imports_parsed, &maintain_order_config) |
| 95 | + }); |
| 96 | + bench("sorted_named_exports/full", iterations, || { |
| 97 | + run_format_text(&sorted_named_exports_text, &config) |
| 98 | + }); |
| 99 | + bench("sorted_named_exports/parsed", iterations, || { |
| 100 | + run_format_parsed_source(&sorted_named_exports_parsed, &config) |
| 101 | + }); |
| 102 | + bench("maintain_named_exports/parsed", iterations, || { |
| 103 | + run_format_parsed_source(&sorted_named_exports_parsed, &maintain_order_config) |
| 104 | + }); |
| 105 | +} |
| 106 | + |
| 107 | +fn bench(name: &str, iterations: usize, mut run: impl FnMut() -> usize) { |
| 108 | + for _ in 0..2 { |
| 109 | + black_box(run()); |
| 110 | + } |
| 111 | + |
| 112 | + let mut timings = Vec::with_capacity(iterations); |
| 113 | + for _ in 0..iterations { |
| 114 | + let start = Instant::now(); |
| 115 | + black_box(run()); |
| 116 | + timings.push(start.elapsed()); |
| 117 | + } |
| 118 | + |
| 119 | + timings.sort(); |
| 120 | + let total = timings.iter().copied().fold(Duration::ZERO, |acc, timing| acc + timing); |
| 121 | + let min = timings[0]; |
| 122 | + let median = timings[timings.len() / 2]; |
| 123 | + let avg = total / timings.len() as u32; |
| 124 | + |
| 125 | + println!( |
| 126 | + "{name}: min={:.2}ms median={:.2}ms avg={:.2}ms", |
| 127 | + ms(min), |
| 128 | + ms(median), |
| 129 | + ms(avg), |
| 130 | + ); |
| 131 | +} |
| 132 | + |
| 133 | +fn run_format_text(file_text: &str, config: &Configuration) -> usize { |
| 134 | + let result = format_text(FormatTextOptions { |
| 135 | + path: Path::new("bench.ts"), |
| 136 | + extension: None, |
| 137 | + text: file_text.to_string(), |
| 138 | + config, |
| 139 | + external_formatter: None, |
| 140 | + }) |
| 141 | + .unwrap(); |
| 142 | + result.as_deref().unwrap_or(file_text).len() |
| 143 | +} |
| 144 | + |
| 145 | +fn run_format_parsed_source(parsed_source: &ParsedSource, config: &Configuration) -> usize { |
| 146 | + let result = format_parsed_source(parsed_source, config, None).unwrap(); |
| 147 | + result.as_deref().unwrap_or(parsed_source.text()).len() |
| 148 | +} |
| 149 | + |
| 150 | +fn parse_source(file_text: &str) -> ParsedSource { |
| 151 | + let media_type = MediaType::TypeScript; |
| 152 | + deno_ast::parse_program(deno_ast::ParseParams { |
| 153 | + specifier: ModuleSpecifier::parse("file:///bench.ts").unwrap(), |
| 154 | + capture_tokens: true, |
| 155 | + maybe_syntax: Some(deno_ast::get_syntax(media_type)), |
| 156 | + media_type, |
| 157 | + scope_analysis: false, |
| 158 | + text: Arc::from(file_text), |
| 159 | + }) |
| 160 | + .unwrap() |
| 161 | +} |
| 162 | + |
| 163 | +fn sorted_import_declarations(import_count: usize, max_parent_segments: usize) -> String { |
| 164 | + let mut text = String::new(); |
| 165 | + for i in (0..import_count).rev() { |
| 166 | + let parent_count = i % max_parent_segments; |
| 167 | + let prefix = if parent_count == 0 { |
| 168 | + ".".to_string() |
| 169 | + } else { |
| 170 | + "../".repeat(parent_count) |
| 171 | + }; |
| 172 | + text.push_str(&format!( |
| 173 | + "import {{ Z{i}, a{i}, type T{i}, b{i} as B{i} }} from \"{prefix}pkg{}/mod{i}\";\n", |
| 174 | + i % 97, |
| 175 | + )); |
| 176 | + } |
| 177 | + text.push_str("\nexport const value = 1;\n"); |
| 178 | + text |
| 179 | +} |
| 180 | + |
| 181 | +fn grouped_import_declarations(import_count: usize, max_parent_segments: usize) -> String { |
| 182 | + let mut text = String::new(); |
| 183 | + for i in (0..import_count).rev() { |
| 184 | + let source = match i % 4 { |
| 185 | + 0 => format!("node:bench{i}/mod"), |
| 186 | + 1 => format!("pkg{}/mod{i}", i % 97), |
| 187 | + 2 => format!("{}pkg{}/mod{i}", "../".repeat((i % max_parent_segments) + 1), i % 97), |
| 188 | + _ => format!("./pkg{}/mod{i}", i % 97), |
| 189 | + }; |
| 190 | + text.push_str(&format!( |
| 191 | + "import {{ Z{i}, a{i}, type T{i}, b{i} as B{i} }} from \"{source}\";\n", |
| 192 | + )); |
| 193 | + } |
| 194 | + text.push_str("\nexport const value = 1;\n"); |
| 195 | + text |
| 196 | +} |
| 197 | + |
| 198 | +fn sorted_export_declarations(export_count: usize, max_parent_segments: usize) -> String { |
| 199 | + let mut text = String::new(); |
| 200 | + for i in (0..export_count).rev() { |
| 201 | + let parent_count = i % max_parent_segments; |
| 202 | + let prefix = if parent_count == 0 { |
| 203 | + ".".to_string() |
| 204 | + } else { |
| 205 | + "../".repeat(parent_count) |
| 206 | + }; |
| 207 | + text.push_str(&format!( |
| 208 | + "export {{ Z{i}, a{i}, type T{i}, b{i} as B{i} }} from \"{prefix}pkg{}/mod{i}\";\n", |
| 209 | + i % 97, |
| 210 | + )); |
| 211 | + } |
| 212 | + text.push_str("\nexport const value = 1;\n"); |
| 213 | + text |
| 214 | +} |
| 215 | + |
| 216 | +fn sorted_named_imports(named_count: usize) -> String { |
| 217 | + let mut text = String::from("import {\n"); |
| 218 | + for i in (0..named_count).rev() { |
| 219 | + if i % 5 == 0 { |
| 220 | + text.push_str(&format!(" type T{i},\n")); |
| 221 | + } else { |
| 222 | + text.push_str(&format!(" Z{i} as z{i},\n")); |
| 223 | + } |
| 224 | + } |
| 225 | + text.push_str("} from \"pkg\";\n\nexport const value = 1;\n"); |
| 226 | + text |
| 227 | +} |
| 228 | + |
| 229 | +fn sorted_named_exports(named_count: usize) -> String { |
| 230 | + let mut text = String::from("export {\n"); |
| 231 | + for i in (0..named_count).rev() { |
| 232 | + if i % 5 == 0 { |
| 233 | + text.push_str(&format!(" type T{i},\n")); |
| 234 | + } else { |
| 235 | + text.push_str(&format!(" Z{i} as z{i},\n")); |
| 236 | + } |
| 237 | + } |
| 238 | + text.push_str("} from \"pkg\";\n\nexport const value = 1;\n"); |
| 239 | + text |
| 240 | +} |
| 241 | + |
| 242 | +fn env_usize(name: &str) -> Option<usize> { |
| 243 | + std::env::var(name).ok().and_then(|value| value.parse().ok()) |
| 244 | +} |
| 245 | + |
| 246 | +fn ms(duration: Duration) -> f64 { |
| 247 | + duration.as_secs_f64() * 1_000.0 |
| 248 | +} |
| 249 | + |
| 250 | +fn import_group(category: BuiltinCategory) -> ImportGroup { |
| 251 | + ImportGroup { |
| 252 | + matchers: ImportGroupMatch::Single(ImportMatcher::Category(category)), |
| 253 | + } |
| 254 | +} |
0 commit comments