1 use std
::path
::PathBuf
;
8 use proxmox
::api
::{ApiMethod, RpcEnvironment}
;
15 format_and_print_result_full
,
16 default_table_format_options
,
21 use pbs_client
::tools
::key_source
::get_encryption_key_password
;
22 use pbs_client
::{BackupRepository, BackupWriter}
;
24 use proxmox_backup
::backup
::{
32 KEYFILE_SCHEMA
, REPO_URL_SCHEMA
,
33 extract_repository_from_value
,
39 #[derive(Copy, Clone, Serialize)]
42 /// The meassured speed in Bytes/second
43 #[serde(skip_serializing_if="Option::is_none")]
45 /// Top result we want to compare with
71 #[derive(Copy, Clone, Serialize)]
73 struct BenchmarkResult
{
76 /// SHA256 checksum computation speed
78 /// ZStd level 1 compression speed
80 /// ZStd level 1 decompression speed
82 /// AES256 GCM encryption speed
88 static BENCHMARK_RESULT_2020_TOP
: BenchmarkResult
= BenchmarkResult
{
91 top
: 1_000_000.0 * 1235.0, // TLS to localhost, AMD Ryzen 7 2700X
95 top
: 1_000_000.0 * 2022.0, // AMD Ryzen 7 2700X
99 top
: 1_000_000.0 * 752.0, // AMD Ryzen 7 2700X
103 top
: 1_000_000.0 * 1198.0, // AMD Ryzen 7 2700X
107 top
: 1_000_000.0 * 3645.0, // AMD Ryzen 7 2700X
111 top
: 1_000_000.0 * 758.0, // AMD Ryzen 7 2700X
119 schema
: REPO_URL_SCHEMA
,
123 description
: "Verbose output.",
128 schema
: KEYFILE_SCHEMA
,
132 schema
: OUTPUT_FORMAT
,
138 /// Run benchmark tests
139 pub async
fn benchmark(
142 _rpcenv
: &mut dyn RpcEnvironment
,
143 ) -> Result
<(), Error
> {
145 let repo
= extract_repository_from_value(¶m
).ok();
147 let keyfile
= param
["keyfile"].as_str().map(PathBuf
::from
);
149 let verbose
= param
["verbose"].as_bool().unwrap_or(false);
151 let output_format
= get_output_format(¶m
);
153 let crypt_config
= match keyfile
{
156 let (key
, _
, _
) = load_and_decrypt_key(&path
, &get_encryption_key_password
)?
;
157 let crypt_config
= CryptConfig
::new(key
)?
;
158 Some(Arc
::new(crypt_config
))
162 let mut benchmark_result
= BENCHMARK_RESULT_2020_TOP
;
164 // do repo tests first, because this may prompt for a password
165 if let Some(repo
) = repo
{
166 test_upload_speed(&mut benchmark_result
, repo
, crypt_config
.clone(), verbose
).await?
;
169 test_crypt_speed(&mut benchmark_result
, verbose
)?
;
171 render_result(&output_format
, &benchmark_result
)?
;
176 // print comparison table
179 benchmark_result
: &BenchmarkResult
,
180 ) -> Result
<(), Error
> {
182 let mut data
= serde_json
::to_value(benchmark_result
)?
;
183 let return_type
= ReturnType
::new(false, &BenchmarkResult
::API_SCHEMA
);
185 let render_speed
= |value
: &Value
, _record
: &Value
| -> Result
<String
, Error
> {
186 match value
["speed"].as_f64() {
187 None
=> Ok(String
::from("not tested")),
189 let top
= value
["top"].as_f64().unwrap();
190 Ok(format
!("{:.2} MB/s ({:.0}%)", speed
/1_000_000.0, (speed
*100.0)/top
))
195 let options
= default_table_format_options()
196 .column(ColumnConfig
::new("tls")
197 .header("TLS (maximal backup upload speed)")
198 .right_align(false).renderer(render_speed
))
199 .column(ColumnConfig
::new("sha256")
200 .header("SHA256 checksum computation speed")
201 .right_align(false).renderer(render_speed
))
202 .column(ColumnConfig
::new("compress")
203 .header("ZStd level 1 compression speed")
204 .right_align(false).renderer(render_speed
))
205 .column(ColumnConfig
::new("decompress")
206 .header("ZStd level 1 decompression speed")
207 .right_align(false).renderer(render_speed
))
208 .column(ColumnConfig
::new("verify")
209 .header("Chunk verification speed")
210 .right_align(false).renderer(render_speed
))
211 .column(ColumnConfig
::new("aes256_gcm")
212 .header("AES256 GCM encryption speed")
213 .right_align(false).renderer(render_speed
));
216 format_and_print_result_full(&mut data
, &return_type
, output_format
, &options
);
221 async
fn test_upload_speed(
222 benchmark_result
: &mut BenchmarkResult
,
223 repo
: BackupRepository
,
224 crypt_config
: Option
<Arc
<CryptConfig
>>,
226 ) -> Result
<(), Error
> {
228 let backup_time
= proxmox
::tools
::time
::epoch_i64();
230 let client
= connect(&repo
)?
;
231 record_repository(&repo
);
233 if verbose { eprintln!("Connecting to backup server"); }
234 let client
= BackupWriter
::start(
236 crypt_config
.clone(),
245 if verbose { eprintln!("Start TLS speed test"); }
246 let speed
= client
.upload_speedtest(verbose
).await?
;
248 eprintln
!("TLS speed: {:.2} MB/s", speed
/1_000_000.0);
250 benchmark_result
.tls
.speed
= Some(speed
);
255 // test hash/crypt/compress speed
257 benchmark_result
: &mut BenchmarkResult
,
259 ) -> Result
<(), Error
> {
263 let kdf
= KeyDerivationConfig
::Scrypt
{
270 let testkey
= kdf
.derive_key(pw
)?
;
272 let crypt_config
= CryptConfig
::new(testkey
)?
;
274 //let random_data = proxmox::sys::linux::random_data(1024*1024)?;
275 let mut random_data
= vec
![];
276 // generate pseudo random byte sequence
277 for i
in 0..256*1024 {
279 let byte
= ((i
>> (j
<<3))&0xff) as u8;
280 random_data
.push(byte
);
284 assert_eq
!(random_data
.len(), 1024*1024);
286 let start_time
= std
::time
::Instant
::now();
290 openssl
::sha
::sha256(&random_data
);
291 bytes
+= random_data
.len();
292 if start_time
.elapsed().as_micros() > 1_000_000 { break; }
294 let speed
= (bytes
as f64)/start_time
.elapsed().as_secs_f64();
295 benchmark_result
.sha256
.speed
= Some(speed
);
297 eprintln
!("SHA256 speed: {:.2} MB/s", speed
/1_000_000.0);
300 let start_time
= std
::time
::Instant
::now();
304 let mut reader
= &random_data
[..];
305 zstd
::stream
::encode_all(&mut reader
, 1)?
;
306 bytes
+= random_data
.len();
307 if start_time
.elapsed().as_micros() > 3_000_000 { break; }
309 let speed
= (bytes
as f64)/start_time
.elapsed().as_secs_f64();
310 benchmark_result
.compress
.speed
= Some(speed
);
312 eprintln
!("Compression speed: {:.2} MB/s", speed
/1_000_000.0);
315 let start_time
= std
::time
::Instant
::now();
317 let compressed_data
= {
318 let mut reader
= &random_data
[..];
319 zstd
::stream
::encode_all(&mut reader
, 1)?
324 let mut reader
= &compressed_data
[..];
325 let data
= zstd
::stream
::decode_all(&mut reader
)?
;
327 if start_time
.elapsed().as_micros() > 1_000_000 { break; }
329 let speed
= (bytes
as f64)/start_time
.elapsed().as_secs_f64();
330 benchmark_result
.decompress
.speed
= Some(speed
);
332 eprintln
!("Decompress speed: {:.2} MB/s", speed
/1_000_000.0);
335 let start_time
= std
::time
::Instant
::now();
339 let mut out
= Vec
::new();
340 crypt_config
.encrypt_to(&random_data
, &mut out
)?
;
341 bytes
+= random_data
.len();
342 if start_time
.elapsed().as_micros() > 1_000_000 { break; }
344 let speed
= (bytes
as f64)/start_time
.elapsed().as_secs_f64();
345 benchmark_result
.aes256_gcm
.speed
= Some(speed
);
347 eprintln
!("AES256/GCM speed: {:.2} MB/s", speed
/1_000_000.0);
350 let start_time
= std
::time
::Instant
::now();
352 let (chunk
, digest
) = DataChunkBuilder
::new(&random_data
)
358 chunk
.verify_unencrypted(random_data
.len(), &digest
)?
;
359 bytes
+= random_data
.len();
360 if start_time
.elapsed().as_micros() > 1_000_000 { break; }
362 let speed
= (bytes
as f64)/start_time
.elapsed().as_secs_f64();
363 benchmark_result
.verify
.speed
= Some(speed
);
365 eprintln
!("Verify speed: {:.2} MB/s", speed
/1_000_000.0);