Skip to content

Commit 0a1b983

Browse files
committed
add dist.compression-profile option to control compression speed
1 parent bd43458 commit 0a1b983

File tree

7 files changed

+122
-47
lines changed

7 files changed

+122
-47
lines changed

config.example.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -803,3 +803,9 @@ changelog-seen = 2
803803
#
804804
# This list must be non-empty.
805805
#compression-formats = ["gz", "xz"]
806+
807+
# How much time should be spent compressing the tarballs. The better the
808+
# compression profile, the longer compression will take.
809+
#
810+
# Available options: fast, balanced, best
811+
#compression-profile = "balanced"

src/bootstrap/config.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ pub struct Config {
190190
pub dist_sign_folder: Option<PathBuf>,
191191
pub dist_upload_addr: Option<String>,
192192
pub dist_compression_formats: Option<Vec<String>>,
193+
pub dist_compression_profile: String,
193194
pub dist_include_mingw_linker: bool,
194195

195196
// libstd features
@@ -701,6 +702,7 @@ define_config! {
701702
src_tarball: Option<bool> = "src-tarball",
702703
missing_tools: Option<bool> = "missing-tools",
703704
compression_formats: Option<Vec<String>> = "compression-formats",
705+
compression_profile: Option<String> = "compression-profile",
704706
include_mingw_linker: Option<bool> = "include-mingw-linker",
705707
}
706708
}
@@ -819,6 +821,7 @@ impl Config {
819821
config.deny_warnings = true;
820822
config.bindir = "bin".into();
821823
config.dist_include_mingw_linker = true;
824+
config.dist_compression_profile = "balanced".into();
822825

823826
// set by build.rs
824827
config.build = TargetSelection::from_user(&env!("BUILD_TRIPLE"));
@@ -1300,6 +1303,7 @@ impl Config {
13001303
config.dist_sign_folder = t.sign_folder.map(PathBuf::from);
13011304
config.dist_upload_addr = t.upload_addr;
13021305
config.dist_compression_formats = t.compression_formats;
1306+
set(&mut config.dist_compression_profile, t.compression_profile);
13031307
set(&mut config.rust_dist_src, t.src_tarball);
13041308
set(&mut config.missing_tools, t.missing_tools);
13051309
set(&mut config.dist_include_mingw_linker, t.include_mingw_linker)

src/bootstrap/tarball.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,7 @@ impl<'a> Tarball<'a> {
318318
assert!(!formats.is_empty(), "dist.compression-formats can't be empty");
319319
cmd.arg("--compression-formats").arg(formats.join(","));
320320
}
321+
cmd.args(&["--compression-profile", &self.builder.config.dist_compression_profile]);
321322
self.builder.run(&mut cmd);
322323

323324
// Ensure there are no symbolic links in the tarball. In particular,

src/tools/rust-installer/src/combiner.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use super::Scripter;
22
use super::Tarballer;
33
use crate::{
4-
compression::{CompressionFormat, CompressionFormats},
4+
compression::{CompressionFormat, CompressionFormats, CompressionProfile},
55
util::*,
66
};
77
use anyhow::{bail, Context, Result};
@@ -48,6 +48,10 @@ actor! {
4848
#[clap(value_name = "DIR")]
4949
output_dir: String = "./dist",
5050

51+
/// The profile used to compress the tarball.
52+
#[clap(value_name = "FORMAT", default_value_t)]
53+
compression_profile: CompressionProfile,
54+
5155
/// The formats used to compress the tarball
5256
#[clap(value_name = "FORMAT", default_value_t)]
5357
compression_formats: CompressionFormats,
@@ -153,6 +157,7 @@ impl Combiner {
153157
.work_dir(self.work_dir)
154158
.input(self.package_name)
155159
.output(path_to_str(&output)?.into())
160+
.compression_profile(self.compression_profile)
156161
.compression_formats(self.compression_formats.clone());
157162
tarballer.run()?;
158163

src/tools/rust-installer/src/compression.rs

Lines changed: 93 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,37 @@ use rayon::prelude::*;
44
use std::{convert::TryFrom, fmt, io::Read, io::Write, path::Path, str::FromStr};
55
use xz2::{read::XzDecoder, write::XzEncoder};
66

7+
#[derive(Default, Debug, Copy, Clone)]
8+
pub enum CompressionProfile {
9+
Fast,
10+
#[default]
11+
Balanced,
12+
Best,
13+
}
14+
15+
impl FromStr for CompressionProfile {
16+
type Err = Error;
17+
18+
fn from_str(input: &str) -> Result<Self, Error> {
19+
Ok(match input {
20+
"fast" => Self::Fast,
21+
"balanced" => Self::Balanced,
22+
"best" => Self::Best,
23+
other => anyhow::bail!("invalid compression profile: {other}"),
24+
})
25+
}
26+
}
27+
28+
impl fmt::Display for CompressionProfile {
29+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30+
match self {
31+
CompressionProfile::Fast => f.write_str("fast"),
32+
CompressionProfile::Balanced => f.write_str("balanced"),
33+
CompressionProfile::Best => f.write_str("best"),
34+
}
35+
}
36+
}
37+
738
#[derive(Debug, Copy, Clone)]
839
pub enum CompressionFormat {
940
Gz,
@@ -26,7 +57,11 @@ impl CompressionFormat {
2657
}
2758
}
2859

29-
pub(crate) fn encode(&self, path: impl AsRef<Path>) -> Result<Box<dyn Encoder>, Error> {
60+
pub(crate) fn encode(
61+
&self,
62+
path: impl AsRef<Path>,
63+
profile: CompressionProfile,
64+
) -> Result<Box<dyn Encoder>, Error> {
3065
let mut os = path.as_ref().as_os_str().to_os_string();
3166
os.push(format!(".{}", self.extension()));
3267
let path = Path::new(&os);
@@ -37,49 +72,64 @@ impl CompressionFormat {
3772
let file = crate::util::create_new_file(path)?;
3873

3974
Ok(match self {
40-
CompressionFormat::Gz => Box::new(GzEncoder::new(file, flate2::Compression::best())),
75+
CompressionFormat::Gz => Box::new(GzEncoder::new(
76+
file,
77+
match profile {
78+
CompressionProfile::Fast => flate2::Compression::fast(),
79+
CompressionProfile::Balanced => flate2::Compression::new(6),
80+
CompressionProfile::Best => flate2::Compression::best(),
81+
},
82+
)),
4183
CompressionFormat::Xz => {
42-
let mut filters = xz2::stream::Filters::new();
43-
// the preset is overridden by the other options so it doesn't matter
44-
let mut lzma_ops = xz2::stream::LzmaOptions::new_preset(9).unwrap();
45-
// This sets the overall dictionary size, which is also how much memory (baseline)
46-
// is needed for decompression.
47-
lzma_ops.dict_size(64 * 1024 * 1024);
48-
// Use the best match finder for compression ratio.
49-
lzma_ops.match_finder(xz2::stream::MatchFinder::BinaryTree4);
50-
lzma_ops.mode(xz2::stream::Mode::Normal);
51-
// Set nice len to the maximum for best compression ratio
52-
lzma_ops.nice_len(273);
53-
// Set depth to a reasonable value, 0 means auto, 1000 is somwhat high but gives
54-
// good results.
55-
lzma_ops.depth(1000);
56-
// 2 is the default and does well for most files
57-
lzma_ops.position_bits(2);
58-
// 0 is the default and does well for most files
59-
lzma_ops.literal_position_bits(0);
60-
// 3 is the default and does well for most files
61-
lzma_ops.literal_context_bits(3);
62-
63-
filters.lzma2(&lzma_ops);
64-
65-
let mut builder = xz2::stream::MtStreamBuilder::new();
66-
builder.filters(filters);
67-
68-
// On 32-bit platforms limit ourselves to 3 threads, otherwise we exceed memory
69-
// usage this process can take. In the future we'll likely only do super-fast
70-
// compression in CI and move this heavyweight processing to promote-release (which
71-
// is always 64-bit and can run on big-memory machines) but for now this lets us
72-
// move forward.
73-
if std::mem::size_of::<usize>() == 4 {
74-
builder.threads(3);
75-
} else {
76-
builder.threads(6);
77-
}
78-
79-
let compressor = XzEncoder::new_stream(
80-
std::io::BufWriter::new(file),
81-
builder.encoder().unwrap(),
82-
);
84+
let encoder = match profile {
85+
CompressionProfile::Fast => {
86+
xz2::stream::MtStreamBuilder::new().threads(6).preset(1).encoder().unwrap()
87+
}
88+
CompressionProfile::Balanced => {
89+
xz2::stream::MtStreamBuilder::new().threads(6).preset(6).encoder().unwrap()
90+
}
91+
CompressionProfile::Best => {
92+
let mut filters = xz2::stream::Filters::new();
93+
// the preset is overridden by the other options so it doesn't matter
94+
let mut lzma_ops = xz2::stream::LzmaOptions::new_preset(9).unwrap();
95+
// This sets the overall dictionary size, which is also how much memory (baseline)
96+
// is needed for decompression.
97+
lzma_ops.dict_size(64 * 1024 * 1024);
98+
// Use the best match finder for compression ratio.
99+
lzma_ops.match_finder(xz2::stream::MatchFinder::BinaryTree4);
100+
lzma_ops.mode(xz2::stream::Mode::Normal);
101+
// Set nice len to the maximum for best compression ratio
102+
lzma_ops.nice_len(273);
103+
// Set depth to a reasonable value, 0 means auto, 1000 is somwhat high but gives
104+
// good results.
105+
lzma_ops.depth(1000);
106+
// 2 is the default and does well for most files
107+
lzma_ops.position_bits(2);
108+
// 0 is the default and does well for most files
109+
lzma_ops.literal_position_bits(0);
110+
// 3 is the default and does well for most files
111+
lzma_ops.literal_context_bits(3);
112+
113+
filters.lzma2(&lzma_ops);
114+
115+
let mut builder = xz2::stream::MtStreamBuilder::new();
116+
builder.filters(filters);
117+
118+
// On 32-bit platforms limit ourselves to 3 threads, otherwise we exceed memory
119+
// usage this process can take. In the future we'll likely only do super-fast
120+
// compression in CI and move this heavyweight processing to promote-release (which
121+
// is always 64-bit and can run on big-memory machines) but for now this lets us
122+
// move forward.
123+
if std::mem::size_of::<usize>() == 4 {
124+
builder.threads(3);
125+
} else {
126+
builder.threads(6);
127+
}
128+
builder.encoder().unwrap()
129+
}
130+
};
131+
132+
let compressor = XzEncoder::new_stream(std::io::BufWriter::new(file), encoder);
83133
Box::new(compressor)
84134
}
85135
})

src/tools/rust-installer/src/generator.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use super::Scripter;
22
use super::Tarballer;
3-
use crate::compression::CompressionFormats;
3+
use crate::compression::{CompressionFormats, CompressionProfile};
44
use crate::util::*;
55
use anyhow::{bail, format_err, Context, Result};
66
use std::collections::BTreeSet;
@@ -54,6 +54,10 @@ actor! {
5454
#[clap(value_name = "DIR")]
5555
output_dir: String = "./dist",
5656

57+
/// The profile used to compress the tarball.
58+
#[clap(value_name = "FORMAT", default_value_t)]
59+
compression_profile: CompressionProfile,
60+
5761
/// The formats used to compress the tarball
5862
#[clap(value_name = "FORMAT", default_value_t)]
5963
compression_formats: CompressionFormats,
@@ -113,6 +117,7 @@ impl Generator {
113117
.work_dir(self.work_dir)
114118
.input(self.package_name)
115119
.output(path_to_str(&output)?.into())
120+
.compression_profile(self.compression_profile)
116121
.compression_formats(self.compression_formats.clone());
117122
tarballer.run()?;
118123

src/tools/rust-installer/src/tarballer.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use tar::{Builder, Header};
66
use walkdir::WalkDir;
77

88
use crate::{
9-
compression::{CombinedEncoder, CompressionFormats},
9+
compression::{CombinedEncoder, CompressionFormats, CompressionProfile},
1010
util::*,
1111
};
1212

@@ -25,6 +25,10 @@ actor! {
2525
#[clap(value_name = "DIR")]
2626
work_dir: String = "./workdir",
2727

28+
/// The profile used to compress the tarball.
29+
#[clap(value_name = "FORMAT", default_value_t)]
30+
compression_profile: CompressionProfile,
31+
2832
/// The formats used to compress the tarball.
2933
#[clap(value_name = "FORMAT", default_value_t)]
3034
compression_formats: CompressionFormats,
@@ -38,7 +42,7 @@ impl Tarballer {
3842
let encoder = CombinedEncoder::new(
3943
self.compression_formats
4044
.iter()
41-
.map(|f| f.encode(&tarball_name))
45+
.map(|f| f.encode(&tarball_name, self.compression_profile))
4246
.collect::<Result<Vec<_>>>()?,
4347
);
4448

0 commit comments

Comments
 (0)