From afbb89ee1d0b8346da5bd445f39ca1638732241b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Mon, 28 Oct 2019 00:00:00 +0000 Subject: [PATCH] Optimize long-linker-command-line test Replace O(n^3) text matching with inexpensive hash set lookups. On my machine this reduces the total runtime of complete run-make-fulldeps suite from roughly 75 seconds to 45 seconds. --- .../long-linker-command-lines/foo.rs | 77 ++++++++++++------- 1 file changed, 48 insertions(+), 29 deletions(-) diff --git a/src/test/run-make-fulldeps/long-linker-command-lines/foo.rs b/src/test/run-make-fulldeps/long-linker-command-lines/foo.rs index ac42141365e9d..fb76de571d105 100644 --- a/src/test/run-make-fulldeps/long-linker-command-lines/foo.rs +++ b/src/test/run-make-fulldeps/long-linker-command-lines/foo.rs @@ -7,12 +7,43 @@ // Eventually we should see an argument that looks like `@` as we switch from // passing literal arguments to passing everything in the file. +use std::collections::HashSet; use std::env; use std::fs::{self, File}; -use std::io::{BufWriter, Write, Read}; -use std::path::PathBuf; +use std::io::{BufWriter, Write}; +use std::path::{Path, PathBuf}; use std::process::Command; +fn write_test_case(file: &Path, n: usize) -> HashSet { + let mut libs = HashSet::new(); + let mut f = BufWriter::new(File::create(&file).unwrap()); + let mut prefix = String::new(); + for _ in 0..n { + prefix.push_str("foo"); + } + for i in 0..n { + writeln!(f, "#[link(name = \"{}{}\")]", prefix, i).unwrap(); + libs.insert(format!("{}{}", prefix, i)); + } + writeln!(f, "extern {{}}\nfn main() {{}}").unwrap(); + f.into_inner().unwrap(); + + libs +} + +fn read_linker_args(path: &Path) -> String { + let contents = fs::read(path).unwrap(); + if cfg!(target_env = "msvc") { + let mut i = contents.chunks(2).map(|c| { + c[0] as u16 | ((c[1] as u16) << 8) + }); + assert_eq!(i.next(), Some(0xfeff), "Expected UTF-16 BOM"); + String::from_utf16(&i.collect::>()).unwrap() + } else { + String::from_utf8(contents).unwrap() + } +} + fn main() { let tmpdir = PathBuf::from(env::var_os("TMPDIR").unwrap()); let ok = tmpdir.join("ok"); @@ -29,16 +60,7 @@ fn main() { for i in (1..).map(|i| i * 100) { println!("attempt: {}", i); let file = tmpdir.join("bar.rs"); - let mut f = BufWriter::new(File::create(&file).unwrap()); - let mut lib_name = String::new(); - for _ in 0..i { - lib_name.push_str("foo"); - } - for j in 0..i { - writeln!(f, "#[link(name = \"{}{}\")]", lib_name, j).unwrap(); - } - writeln!(f, "extern {{}}\nfn main() {{}}").unwrap(); - f.into_inner().unwrap(); + let mut expected_libs = write_test_case(&file, i); drop(fs::remove_file(&ok)); let output = Command::new(&rustc) @@ -67,25 +89,22 @@ fn main() { continue } - let mut contents = Vec::new(); - File::open(&ok).unwrap().read_to_end(&mut contents).unwrap(); - - for j in 0..i { - let exp = format!("{}{}", lib_name, j); - let exp = if cfg!(target_env = "msvc") { - let mut out = Vec::with_capacity(exp.len() * 2); - for c in exp.encode_utf16() { - // encode in little endian - out.push(c as u8); - out.push((c >> 8) as u8); - } - out - } else { - exp.into_bytes() - }; - assert!(contents.windows(exp.len()).any(|w| w == &exp[..])); + for mut arg in read_linker_args(&ok).lines() { + if arg.starts_with("-l") { + arg = &arg["-l".len()..]; + } + if arg.ends_with(".lib") { + arg = &arg[..arg.len() - ".lib".len()]; + } + expected_libs.remove(arg); } + assert!( + expected_libs.is_empty(), + "expected but missing libraries: {:#?}", + expected_libs, + ); + break } }