Open acheechee opened 3 years ago
EDIT: a 10 minute save with this patch (down to 45mins from 56mins) to emerge 'rust' on Gentoo.
I've decided that it's unlikely this will be implemented soon enough so I went back to re-make my hacky patch(that avoids compression and keeps the files as .tar
) to work for 1.52.0 rust version, and this is it:
Index: /var/tmp/portage/dev-lang/rust-1.52.0/work/rustc-1.52.0-src/src/tools/rust-installer/src/tarballer.rs
===================================================================
--- rustc-1.52.0-src/src/tools/rust-installer/src/tarballer.rs
+++ rustc-1.52.0-src/src/tools/rust-installer/src/tarballer.rs
@@ -6,7 +6,7 @@ use tar::{Builder, Header};
use walkdir::WalkDir;
use crate::{
- compression::{CombinedEncoder, CompressionFormats},
+ //compression::{CombinedEncoder, CompressionFormats},
util::*,
};
@@ -22,8 +22,8 @@ actor! {
/// The folder in which the input is to be found.
work_dir: String = "./workdir",
- /// The formats used to compress the tarball.
- compression_formats: CompressionFormats = CompressionFormats::default(),
+ ///// The formats used to compress the tarball.
+ //compression_formats: CompressionFormats = CompressionFormats::default(),
}
}
@@ -31,12 +31,13 @@ impl Tarballer {
/// Generates the actual tarballs
pub fn run(self) -> Result<()> {
let tarball_name = self.output.clone() + ".tar";
- let encoder = CombinedEncoder::new(
- self.compression_formats
- .iter()
- .map(|f| f.encode(&tarball_name))
- .collect::<Result<Vec<_>>>()?,
- );
+ let encoder = ensure_new_file(tarball_name)?;
+ //let encoder = CombinedEncoder::new(
+ // self.compression_formats
+ // .iter()
+ // .map(|f| f.encode(&tarball_name))
+ // .collect::<Result<Vec<_>>>()?,
+ //);
// Sort files by their suffix, to group files with the same name from
// different locations (likely identical) and files with the same
#@@ -51,7 +52,7 @@ impl Tarballer {
# let mut builder = Builder::new(buf);
#
# let pool = rayon::ThreadPoolBuilder::new()
#- .num_threads(2)
#+ .num_threads(12)
# .build()
# .unwrap();
# pool.install(move || {
@@ -71,8 +72,8 @@ impl Tarballer {
.context("failed to finish writing .tar stream")?
.into_inner()
.ok()
- .unwrap()
- .finish()?;
+ .unwrap();
+ //.finish()?;
Ok(())
})
Index: /var/tmp/portage/dev-lang/rust-1.52.0/work/rustc-1.52.0-src/src/tools/rust-installer/src/combiner.rs
===================================================================
--- rustc-1.52.0-src/src/tools/rust-installer/src/combiner.rs
+++ rustc-1.52.0-src/src/tools/rust-installer/src/combiner.rs
@@ -1,7 +1,7 @@
use super::Scripter;
use super::Tarballer;
use crate::{
- compression::{CompressionFormat, CompressionFormats},
+ //compression::{CompressionFormat, CompressionFormats},
util::*,
};
use anyhow::{bail, Context, Result};
@@ -39,8 +39,8 @@ actor! {
/// The location to put the final image and tarball.
output_dir: String = "./dist",
- /// The formats used to compress the tarball
- compression_formats: CompressionFormats = CompressionFormats::default(),
+ // /// The formats used to compress the tarball
+ //compression_formats: CompressionFormats = CompressionFormats::default(),
}
}
@@ -64,11 +64,11 @@ impl Combiner {
.filter(|s| !s.is_empty())
{
// Extract the input tarballs
- let compression =
- CompressionFormat::detect_from_path(input_tarball).ok_or_else(|| {
- anyhow::anyhow!("couldn't figure out the format of {}", input_tarball)
- })?;
- Archive::new(compression.decode(input_tarball)?)
+ //let compression =
+ // CompressionFormat::detect_from_path(input_tarball).ok_or_else(|| {
+ // anyhow::anyhow!("couldn't figure out the format of {}", input_tarball)
+ /* })?;*/ let input_tarball=input_tarball.trim_end_matches(".gz").trim_end_matches(".xz");
+ Archive::new(open_file(&input_tarball)?)//compression.decode(input_tarball)?)
.unpack(&self.work_dir)
.with_context(|| {
format!(
#@@ -68,6 +68,7 @@ impl Combiner {
# // CompressionFormat::detect_from_path(input_tarball).ok_or_else(|| {
# // anyhow::anyhow!("couldn't figure out the format of {}", input_tarball)
# // })?;
#+ let input_tarball=input_tarball.trim_end_matches(".gz").trim_end_matches(".xz");
# Archive::new(open_file(&input_tarball)?)//compression.decode(input_tarball)?)
# .unpack(&self.work_dir)
# .with_context(|| {
@@ -78,7 +78,8 @@ impl Combiner {
})?;
let pkg_name =
- input_tarball.trim_end_matches(&format!(".tar.{}", compression.extension()));
+ input_tarball.trim_end_matches(".tar");
+ //input_tarball.trim_end_matches(&format!(".tar.{}", compression.extension()));
let pkg_name = Path::new(pkg_name).file_name().unwrap();
let pkg_dir = Path::new(&self.work_dir).join(&pkg_name);
@@ -142,8 +143,8 @@ impl Combiner {
tarballer
.work_dir(self.work_dir)
.input(self.package_name)
- .output(path_to_str(&output)?.into())
- .compression_formats(self.compression_formats.clone());
+ .output(path_to_str(&output)?.into());
+ //.compression_formats(self.compression_formats.clone());
tarballer.run()?;
Ok(())
Index: /var/tmp/portage/dev-lang/rust-1.52.0/work/rustc-1.52.0-src/src/tools/rust-installer/src/generator.rs
===================================================================
--- rustc-1.52.0-src/src/tools/rust-installer/src/generator.rs
+++ rustc-1.52.0-src/src/tools/rust-installer/src/generator.rs
@@ -42,8 +42,8 @@ actor! {
/// The location to put the final image and tarball
output_dir: String = "./dist",
- /// The formats used to compress the tarball
- compression_formats: CompressionFormats = CompressionFormats::default(),
+ // /// The formats used to compress the tarball
+ // compression_formats: CompressionFormats = CompressionFormats::default(),
}
}
@@ -99,8 +99,8 @@ impl Generator {
tarballer
.work_dir(self.work_dir)
.input(self.package_name)
- .output(path_to_str(&output)?.into())
- .compression_formats(self.compression_formats.clone());
+ .output(path_to_str(&output)?.into());
+ //.compression_formats(self.compression_formats.clone());
tarballer.run()?;
Ok(())
Index: /var/tmp/portage/dev-lang/rust-1.52.0/work/rustc-1.52.0-src/src/tools/rust-installer/src/main.rs
===================================================================
--- rustc-1.52.0-src/src/tools/rust-installer/src/main.rs
+++ rustc-1.52.0-src/src/tools/rust-installer/src/main.rs
@@ -41,7 +41,7 @@ fn combine(matches: &ArgMatches<'_>) ->
"non-installed-overlay" => non_installed_overlay,
"work-dir" => work_dir,
"output-dir" => output_dir,
- "compression-formats" => compression_formats,
+ //"compression-formats" => compression_formats,
});
combiner.run().context("failed to combine installers")?;
@@ -61,7 +61,7 @@ fn generate(matches: &ArgMatches<'_>) ->
"image-dir" => image_dir,
"work-dir" => work_dir,
"output-dir" => output_dir,
- "compression-formats" => compression_formats,
+ //"compression-formats" => compression_formats,
});
generator.run().context("failed to generate installer")?;
@@ -88,7 +88,7 @@ fn tarball(matches: &ArgMatches<'_>) ->
"input" => input,
"output" => output,
"work-dir" => work_dir,
- "compression-formats" => compression_formats,
+ //"compression-formats" => compression_formats,
});
tarballer.run().context("failed to generate tarballs")?;
Index: /var/tmp/portage/dev-lang/rust-1.52.0/work/rustc-1.52.0-src/src/tools/rust-installer/src/util.rs
===================================================================
--- rustc-1.52.0-src/src/tools/rust-installer/src/util.rs
+++ rustc-1.52.0-src/src/tools/rust-installer/src/util.rs
@@ -73,6 +73,16 @@ pub fn create_new_file<P: AsRef<Path>>(p
Ok(file)
}
+/// Ensure a new file or truncates existing one
+pub fn ensure_new_file<P: AsRef<Path>>(path: P) -> Result<fs::File> {
+ let file = fs::OpenOptions::new()
+ .write(true)
+ .create(true).truncate(true)
+ .open(&path)
+ .with_context(|| format!("failed to truncate existing or create new file '{}'", path.as_ref().display()))?;
+ Ok(file)
+}
+
/// Wraps `fs::File::open()` with a nicer error message.
pub fn open_file<P: AsRef<Path>>(path: P) -> Result<fs::File> {
let file = fs::File::open(&path)
I'm using this on Gentoo if it matters... and with this ebuild:
Without patch it used to emerge rust in 56 mins (ran it twice and got the same time), but now, with this patch, it does it in ... about 43 mins (second run was: 45mins because I was also watching a movie meanwhile
real 44m56.608s
user 353m59.402s
sys 5m25.139s
)
This has been on my wishlist for a while, compressing tarballs only to immediately decompress them is a big bottleneck for local installs. Here's a proof of concept that adds a proper no-compression mode. If there's maintainer interest, I'd be happy to turn this into a PR.
The speedup is significant, since the vast majority of the time during dist
or install
is spent on compression. Tested on my PC with an i7-8086K and an NVMe drive. I configured an extended build (cargo, clippy, rustfmt) and did ./x.py build --stage 2
so all the times here are just dist/install and no recompiling.
Command \ Compression | gz | xz | none |
---|---|---|---|
./x.py install |
42s | 51s | 16s |
./x.py dist |
2m 12s | 2m 4s | 3s |
size of all tarballs in build/dist/ |
515 MiB | 333 MiB | 1766 MiB |
rust-installer patch:
diff --git a/src/combiner.rs b/src/combiner.rs
index 006a40c..549b753 100644
--- a/src/combiner.rs
+++ b/src/combiner.rs
@@ -78,7 +78,7 @@ impl Combiner {
})?;
let pkg_name =
- input_tarball.trim_end_matches(&format!(".tar.{}", compression.extension()));
+ input_tarball.trim_end_matches(&format!(".tar{}", compression.extension()));
let pkg_name = Path::new(pkg_name).file_name().unwrap();
let pkg_dir = Path::new(&self.work_dir).join(&pkg_name);
diff --git a/src/compression.rs b/src/compression.rs
index b3010cb..91c7ae6 100644
--- a/src/compression.rs
+++ b/src/compression.rs
@@ -6,6 +6,7 @@ use xz2::{read::XzDecoder, write::XzEncoder};
#[derive(Debug, Copy, Clone)]
pub enum CompressionFormat {
+ None,
Gz,
Xz,
}
@@ -13,6 +14,7 @@ pub enum CompressionFormat {
impl CompressionFormat {
pub(crate) fn detect_from_path(path: impl AsRef<Path>) -> Option<Self> {
match path.as_ref().extension().and_then(|e| e.to_str()) {
+ Some("tar") => Some(CompressionFormat::None),
Some("gz") => Some(CompressionFormat::Gz),
Some("xz") => Some(CompressionFormat::Xz),
_ => None,
@@ -21,14 +23,15 @@ impl CompressionFormat {
pub(crate) fn extension(&self) -> &'static str {
match self {
- CompressionFormat::Gz => "gz",
- CompressionFormat::Xz => "xz",
+ CompressionFormat::None => "",
+ CompressionFormat::Gz => ".gz",
+ CompressionFormat::Xz => ".xz",
}
}
pub(crate) fn encode(&self, path: impl AsRef<Path>) -> Result<Box<dyn Encoder>, Error> {
let mut os = path.as_ref().as_os_str().to_os_string();
- os.push(format!(".{}", self.extension()));
+ os.push(format!("{}", self.extension()));
let path = Path::new(&os);
if path.exists() {
@@ -37,6 +40,7 @@ impl CompressionFormat {
let file = crate::util::create_new_file(path)?;
Ok(match self {
+ CompressionFormat::None => Box::new(NopEncoder::new(file)),
CompressionFormat::Gz => Box::new(GzEncoder::new(file, flate2::Compression::best())),
CompressionFormat::Xz => {
// Note that preset 6 takes about 173MB of memory per thread, so we limit the number of
@@ -54,6 +58,7 @@ impl CompressionFormat {
pub(crate) fn decode(&self, path: impl AsRef<Path>) -> Result<Box<dyn Read>, Error> {
let file = crate::util::open_file(path.as_ref())?;
Ok(match self {
+ CompressionFormat::None => Box::new(file),
CompressionFormat::Gz => Box::new(GzDecoder::new(file)),
CompressionFormat::Xz => Box::new(XzDecoder::new(file)),
})
@@ -71,6 +76,7 @@ impl TryFrom<&'_ str> for CompressionFormats {
let mut parsed = Vec::new();
for format in value.split(',') {
match format.trim() {
+ "none" => parsed.push(CompressionFormat::None),
"gz" => parsed.push(CompressionFormat::Gz),
"xz" => parsed.push(CompressionFormat::Xz),
other => anyhow::bail!("unknown compression format: {}", other),
@@ -96,6 +102,31 @@ pub(crate) trait Encoder: Send + Write {
fn finish(self: Box<Self>) -> Result<(), Error>;
}
+#[derive(Debug)]
+struct NopEncoder<W>(W);
+
+impl<W> NopEncoder<W> {
+ fn new(writer: W) -> Self {
+ Self(writer)
+ }
+}
+
+impl<W: Send + Write> Write for NopEncoder<W> {
+ fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
+ self.0.write(buf)
+ }
+
+ fn flush(&mut self) -> std::io::Result<()> {
+ self.0.flush()
+ }
+}
+
+impl<W: Send + Write> Encoder for NopEncoder<W> {
+ fn finish(self: Box<Self>) -> Result<(), Error> {
+ Ok(())
+ }
+}
+
impl<W: Send + Write> Encoder for GzEncoder<W> {
fn finish(self: Box<Self>) -> Result<(), Error> {
GzEncoder::finish(*self).context("failed to finish .gz file")?;
rust (bootstrap) patch:
diff --git a/src/bootstrap/tarball.rs b/src/bootstrap/tarball.rs
index 9ff5c2327e0..bca82e0489e 100644
--- a/src/bootstrap/tarball.rs
+++ b/src/bootstrap/tarball.rs
@@ -322,11 +322,14 @@ fn run(self, build_cli: impl FnOnce(&Tarball<'a>, &mut Command)) -> GeneratedTar
.dist_compression_formats
.as_ref()
.and_then(|formats| formats.get(0))
- .map(|s| s.as_str())
- .unwrap_or("gz");
+ .map(|format| match format.as_str() {
+ "none" => "".to_string(),
+ s => format!(".{}", s),
+ })
+ .unwrap_or_else(|| ".gz".to_string());
GeneratedTarball {
- path: crate::dist::distdir(self.builder).join(format!("{}.tar.{}", package_name, ext)),
+ path: crate::dist::distdir(self.builder).join(format!("{}.tar{}", package_name, ext)),
decompressed_output: self.temp_dir.join(package_name),
work: self.temp_dir,
}
Thanks aswild! I'm using your patch on Gentoo for dev-lang/rust-1.64.0-r1
and the install
phase takes (at least) this long:
real 2m17.875s
user 1m12.461s
sys 1m37.807s
and yield the following in /var/tmp/portage/dev-lang/rust-1.64.0-r1/work/rustc-1.64.0-src/build/dist
:
total 3618000
drwxr-xr-x 2 root root 4096 Sep 28 09:21 .
drwxr-xr-x 6 portage portage 4096 Sep 28 08:59 ..
-rw-r--r-- 1 root root 285510656 Sep 28 09:20 cargo-nightly-x86_64-unknown-linux-gnu.tar
-rw-r--r-- 1 root root 187013120 Sep 28 09:20 clippy-nightly-x86_64-unknown-linux-gnu.tar
-rw-r--r-- 1 root root 605404672 Sep 28 09:20 rls-nightly-x86_64-unknown-linux-gnu.tar
-rw-r--r-- 1 root root 51220992 Sep 28 09:20 rust-analysis-nightly-x86_64-unknown-linux-gnu.tar
-rw-r--r-- 1 root root 2114523136 Sep 28 09:21 rustc-nightly-x86_64-unknown-linux-gnu.tar
-rw-r--r-- 1 root root 254292480 Sep 28 09:20 rustfmt-nightly-x86_64-unknown-linux-gnu.tar
-rw-r--r-- 1 root root 25233408 Sep 28 09:20 rust-src-nightly.tar
-rw-r--r-- 1 root root 181593600 Sep 28 09:20 rust-std-nightly-x86_64-unknown-linux-gnu.tar
(while the compile
phase takes this long:
real 20m43.942s
user 184m16.076s
sys 3m38.705s
)
One thing to note is that user can override compression format in config.toml
(via a modified .ebuild
) like: compression-formats = ["gz"]
(while the default is the overridden 'xz' in Gentoo) and wonder why the patch isn't working.
That (gz) install
phase would take, at least:
real 5m51.131s
user 4m15.991s
sys 2m13.200s
and yield:
total 680092
drwxr-xr-x 2 root root 4096 Sep 28 09:15 .
drwxr-xr-x 6 portage portage 4096 Sep 28 08:59 ..
-rw-r--r-- 1 root root 52474477 Sep 28 09:12 cargo-nightly-x86_64-unknown-linux-gnu.tar.gz
-rw-r--r-- 1 root root 35523955 Sep 28 09:13 clippy-nightly-x86_64-unknown-linux-gnu.tar.gz
-rw-r--r-- 1 root root 111652340 Sep 28 09:12 rls-nightly-x86_64-unknown-linux-gnu.tar.gz
-rw-r--r-- 1 root root 5110512 Sep 28 09:13 rust-analysis-nightly-x86_64-unknown-linux-gnu.tar.gz
-rw-r--r-- 1 root root 386615670 Sep 28 09:16 rustc-nightly-x86_64-unknown-linux-gnu.tar.gz
-rw-r--r-- 1 root root 49449083 Sep 28 09:12 rustfmt-nightly-x86_64-unknown-linux-gnu.tar.gz
-rw-r--r-- 1 root root 3437413 Sep 28 09:13 rust-src-nightly.tar.gz
-rw-r--r-- 1 root root 52129123 Sep 28 09:11 rust-std-nightly-x86_64-unknown-linux-gnu.tar.gz
This should be no longer needed as https://github.com/rust-lang/rust/pull/118724 PR is merged.
Unless I'm misunderstanding something, currently one must use
gz
orxz
as compression formats. Because of this, during the building of rust itself, thefabricate
process uses 100% CPU (aka 1 core only) and thus wastes a lot of time trying to compress to one or both of the two formats (specified in config.toml)On systems where the filesystem is btrfs and using zstd compression already, some may wish to use no compression at all, thus just
.tar
(instead of.tar.gz
or.tar.xz
would be preferred). Maybe such people don't care about any extra used space(presumably filesystem compression if poorer than .gz or .xz ?), or they do care about how much time rust compilation takes (it would take less time this way with avoiding doing any compression especially since it's using only one CPU core).Either way, I'd like to suggest supporting no compression at all - which would probably mean simply just
.tar
In the past I used to hack a patch to do this (
.tar
instead of.tar.gz
) and it worked just fine, but since v1.48.0 rust's changed that code a bit in 1.52.0 and I'm unable to understand how to change the patch to apply it, so far.