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"]
20 mod macros;
21
22 #[allow(dead_code)]
23 #[path = "../tests/repo/mod.rs"]
24 mod repo;
25
26 use std::fs;
27 use std::time::{Duration, Instant};
28
29 #[cfg(not(syn_only))]
30 mod tokenstream_parse {
31 use proc_macro2::TokenStream;
32 use std::str::FromStr;
33
bench(content: &str) -> Result<(), ()>34 pub fn bench(content: &str) -> Result<(), ()> {
35 TokenStream::from_str(content).map(drop).map_err(drop)
36 }
37 }
38
39 mod syn_parse {
bench(content: &str) -> Result<(), ()>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))]
46 mod 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
bench(content: &str) -> Result<(), ()>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))]
100 mod read_from_disk {
bench(content: &str) -> Result<(), ()>101 pub fn bench(content: &str) -> Result<(), ()> {
102 let _ = content;
103 Ok(())
104 }
105 }
106
exec(mut codepath: impl FnMut(&str) -> Result<(), ()>) -> Duration107 fn 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
main()138 fn 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