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