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