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