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, Diagnostic, Handler};
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 cm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
83 let emitter = Box::new(SilentEmitter);
84 let handler = Handler::with_emitter(emitter);
85 let sess = ParseSess::with_span_handler(handler, cm);
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