• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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"]
12 mod macros;
13 
14 #[path = "../tests/common/mod.rs"]
15 mod common;
16 #[path = "../tests/repo/mod.rs"]
17 mod repo;
18 
19 use std::fs;
20 use std::time::{Duration, Instant};
21 
22 #[cfg(not(syn_only))]
23 mod tokenstream_parse {
24     use proc_macro2::TokenStream;
25     use std::str::FromStr;
26 
bench(content: &str) -> Result<(), ()>27     pub fn bench(content: &str) -> Result<(), ()> {
28         TokenStream::from_str(content).map(drop).map_err(drop)
29     }
30 }
31 
32 mod syn_parse {
bench(content: &str) -> Result<(), ()>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))]
39 mod 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 
bench(content: &str) -> Result<(), ()>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))]
92 mod read_from_disk {
bench(content: &str) -> Result<(), ()>93     pub fn bench(content: &str) -> Result<(), ()> {
94         _ = content;
95         Ok(())
96     }
97 }
98 
exec(mut codepath: impl FnMut(&str) -> Result<(), ()>) -> Duration99 fn 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 
main()126 fn 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