1// $ cargo bench --features full,test --bench rust 2// 3// Syn only, useful for profiling: 4// $ RUSTFLAGS='--cfg syn_only' cargo build --release --features full,test --bench rust 5 6#![cfg_attr(not(syn_only), feature(rustc_private))] 7#![recursion_limit = "1024"] 8#![allow( 9 clippy::arc_with_non_send_sync, 10 clippy::cast_lossless, 11 clippy::let_underscore_untyped, 12 clippy::manual_let_else, 13 clippy::match_like_matches_macro, 14 clippy::uninlined_format_args, 15 clippy::unnecessary_wraps 16)] 17 18#[macro_use] 19#[path = "../tests/macros/mod.rs"] 20mod macros; 21 22#[allow(dead_code)] 23#[path = "../tests/repo/mod.rs"] 24mod repo; 25 26use std::fs; 27use std::time::{Duration, Instant}; 28 29#[cfg(not(syn_only))] 30mod tokenstream_parse { 31 use proc_macro2::TokenStream; 32 use std::str::FromStr; 33 34 pub fn bench(content: &str) -> Result<(), ()> { 35 TokenStream::from_str(content).map(drop).map_err(drop) 36 } 37} 38 39mod syn_parse { 40 pub fn bench(content: &str) -> Result<(), ()> { 41 syn::parse_file(content).map(drop).map_err(drop) 42 } 43} 44 45#[cfg(not(syn_only))] 46mod librustc_parse { 47 extern crate rustc_data_structures; 48 extern crate rustc_driver; 49 extern crate rustc_error_messages; 50 extern crate rustc_errors; 51 extern crate rustc_parse; 52 extern crate rustc_session; 53 extern crate rustc_span; 54 55 use rustc_data_structures::sync::Lrc; 56 use rustc_error_messages::FluentBundle; 57 use rustc_errors::{emitter::Emitter, translation::Translate, DiagCtxt, Diagnostic}; 58 use rustc_session::parse::ParseSess; 59 use rustc_span::source_map::{FilePathMapping, SourceMap}; 60 use rustc_span::{edition::Edition, FileName}; 61 62 pub fn bench(content: &str) -> Result<(), ()> { 63 struct SilentEmitter; 64 65 impl Emitter for SilentEmitter { 66 fn emit_diagnostic(&mut self, _diag: &Diagnostic) {} 67 fn source_map(&self) -> Option<&Lrc<SourceMap>> { 68 None 69 } 70 } 71 72 impl Translate for SilentEmitter { 73 fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> { 74 None 75 } 76 fn fallback_fluent_bundle(&self) -> &FluentBundle { 77 panic!("silent emitter attempted to translate a diagnostic"); 78 } 79 } 80 81 rustc_span::create_session_if_not_set_then(Edition::Edition2018, |_| { 82 let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty())); 83 let emitter = Box::new(SilentEmitter); 84 let handler = DiagCtxt::with_emitter(emitter); 85 let sess = ParseSess::with_dcx(handler, source_map); 86 if let Err(diagnostic) = rustc_parse::parse_crate_from_source_str( 87 FileName::Custom("bench".to_owned()), 88 content.to_owned(), 89 &sess, 90 ) { 91 diagnostic.cancel(); 92 return Err(()); 93 }; 94 Ok(()) 95 }) 96 } 97} 98 99#[cfg(not(syn_only))] 100mod read_from_disk { 101 pub fn bench(content: &str) -> Result<(), ()> { 102 let _ = content; 103 Ok(()) 104 } 105} 106 107fn exec(mut codepath: impl FnMut(&str) -> Result<(), ()>) -> Duration { 108 let begin = Instant::now(); 109 let mut success = 0; 110 let mut total = 0; 111 112 ["tests/rust/compiler", "tests/rust/library"] 113 .iter() 114 .flat_map(|dir| { 115 walkdir::WalkDir::new(dir) 116 .into_iter() 117 .filter_entry(repo::base_dir_filter) 118 }) 119 .for_each(|entry| { 120 let entry = entry.unwrap(); 121 let path = entry.path(); 122 if path.is_dir() { 123 return; 124 } 125 let content = fs::read_to_string(path).unwrap(); 126 let ok = codepath(&content).is_ok(); 127 success += ok as usize; 128 total += 1; 129 if !ok { 130 eprintln!("FAIL {}", path.display()); 131 } 132 }); 133 134 assert_eq!(success, total); 135 begin.elapsed() 136} 137 138fn main() { 139 repo::clone_rust(); 140 141 macro_rules! testcases { 142 ($($(#[$cfg:meta])* $name:ident,)*) => { 143 [ 144 $( 145 $(#[$cfg])* 146 (stringify!($name), $name::bench as fn(&str) -> Result<(), ()>), 147 )* 148 ] 149 }; 150 } 151 152 #[cfg(not(syn_only))] 153 { 154 let mut lines = 0; 155 let mut files = 0; 156 exec(|content| { 157 lines += content.lines().count(); 158 files += 1; 159 Ok(()) 160 }); 161 eprintln!("\n{} lines in {} files", lines, files); 162 } 163 164 for (name, f) in testcases!( 165 #[cfg(not(syn_only))] 166 read_from_disk, 167 #[cfg(not(syn_only))] 168 tokenstream_parse, 169 syn_parse, 170 #[cfg(not(syn_only))] 171 librustc_parse, 172 ) { 173 eprint!("{:20}", format!("{}:", name)); 174 let elapsed = exec(f); 175 eprintln!( 176 "elapsed={}.{:03}s", 177 elapsed.as_secs(), 178 elapsed.subsec_millis(), 179 ); 180 } 181 eprintln!(); 182} 183