1 use std
::path
::PathBuf
;
8 use proxmox
::api
::{ApiMethod, RpcEnvironment}
;
15 format_and_print_result_full
,
16 default_table_format_options
,
20 use proxmox_backup
::backup
::{
27 use proxmox_backup
::client
::*;
30 KEYFILE_SCHEMA
, REPO_URL_SCHEMA
,
31 extract_repository_from_value
,
37 #[derive(Copy, Clone, Serialize)]
40 /// The meassured speed in Bytes/second
41 #[serde(skip_serializing_if="Option::is_none")]
43 /// Top result we want to compare with
69 #[derive(Copy, Clone, Serialize)]
71 struct BenchmarkResult
{
74 /// SHA256 checksum computation speed
76 /// ZStd level 1 compression speed
78 /// ZStd level 1 decompression speed
80 /// AES256 GCM encryption speed
86 static BENCHMARK_RESULT_2020_TOP
: BenchmarkResult
= BenchmarkResult
{
89 top
: 1_000_000.0 * 1235.0, // TLS to localhost, AMD Ryzen 7 2700X
93 top
: 1_000_000.0 * 2022.0, // AMD Ryzen 7 2700X
97 top
: 1_000_000.0 * 752.0, // AMD Ryzen 7 2700X
101 top
: 1_000_000.0 * 1198.0, // AMD Ryzen 7 2700X
105 top
: 1_000_000.0 * 3645.0, // AMD Ryzen 7 2700X
109 top
: 1_000_000.0 * 758.0, // AMD Ryzen 7 2700X
117 schema
: REPO_URL_SCHEMA
,
121 description
: "Verbose output.",
126 schema
: KEYFILE_SCHEMA
,
130 schema
: OUTPUT_FORMAT
,
136 /// Run benchmark tests
137 pub async
fn benchmark(
140 _rpcenv
: &mut dyn RpcEnvironment
,
141 ) -> Result
<(), Error
> {
143 let repo
= extract_repository_from_value(¶m
).ok();
145 let keyfile
= param
["keyfile"].as_str().map(PathBuf
::from
);
147 let verbose
= param
["verbose"].as_bool().unwrap_or(false);
149 let output_format
= get_output_format(¶m
);
151 let crypt_config
= match keyfile
{
154 let (key
, _
) = load_and_decrypt_key(&path
, &crate::key
::get_encryption_key_password
)?
;
155 let crypt_config
= CryptConfig
::new(key
)?
;
156 Some(Arc
::new(crypt_config
))
160 let mut benchmark_result
= BENCHMARK_RESULT_2020_TOP
;
162 // do repo tests first, because this may prompt for a password
163 if let Some(repo
) = repo
{
164 test_upload_speed(&mut benchmark_result
, repo
, crypt_config
.clone(), verbose
).await?
;
167 test_crypt_speed(&mut benchmark_result
, verbose
)?
;
169 render_result(&output_format
, &benchmark_result
)?
;
174 // print comparison table
177 benchmark_result
: &BenchmarkResult
,
178 ) -> Result
<(), Error
> {
180 let mut data
= serde_json
::to_value(benchmark_result
)?
;
181 let schema
= &BenchmarkResult
::API_SCHEMA
;
183 let render_speed
= |value
: &Value
, _record
: &Value
| -> Result
<String
, Error
> {
184 match value
["speed"].as_f64() {
185 None
=> Ok(String
::from("not tested")),
187 let top
= value
["top"].as_f64().unwrap();
188 Ok(format
!("{:.2} MB/s ({:.0}%)", speed
/1_000_000.0, (speed
*100.0)/top
))
193 let options
= default_table_format_options()
194 .column(ColumnConfig
::new("tls")
195 .header("TLS (maximal backup upload speed)")
196 .right_align(false).renderer(render_speed
))
197 .column(ColumnConfig
::new("sha256")
198 .header("SHA256 checksum computation speed")
199 .right_align(false).renderer(render_speed
))
200 .column(ColumnConfig
::new("compress")
201 .header("ZStd level 1 compression speed")
202 .right_align(false).renderer(render_speed
))
203 .column(ColumnConfig
::new("decompress")
204 .header("ZStd level 1 decompression speed")
205 .right_align(false).renderer(render_speed
))
206 .column(ColumnConfig
::new("verify")
207 .header("Chunk verification speed")
208 .right_align(false).renderer(render_speed
))
209 .column(ColumnConfig
::new("aes256_gcm")
210 .header("AES256 GCM encryption speed")
211 .right_align(false).renderer(render_speed
));
214 format_and_print_result_full(&mut data
, schema
, output_format
, &options
);
219 async
fn test_upload_speed(
220 benchmark_result
: &mut BenchmarkResult
,
221 repo
: BackupRepository
,
222 crypt_config
: Option
<Arc
<CryptConfig
>>,
224 ) -> Result
<(), Error
> {
226 let backup_time
= proxmox
::tools
::time
::epoch_i64();
228 let client
= connect(repo
.host(), repo
.port(), repo
.user())?
;
229 record_repository(&repo
);
231 if verbose { eprintln!("Connecting to backup server"); }
232 let client
= BackupWriter
::start(
234 crypt_config
.clone(),
243 if verbose { eprintln!("Start TLS speed test"); }
244 let speed
= client
.upload_speedtest(verbose
).await?
;
246 eprintln
!("TLS speed: {:.2} MB/s", speed
/1_000_000.0);
248 benchmark_result
.tls
.speed
= Some(speed
);
253 // test hash/crypt/compress speed
255 benchmark_result
: &mut BenchmarkResult
,
257 ) -> Result
<(), Error
> {
261 let kdf
= KeyDerivationConfig
::Scrypt
{
268 let testkey
= kdf
.derive_key(pw
)?
;
270 let crypt_config
= CryptConfig
::new(testkey
)?
;
272 //let random_data = proxmox::sys::linux::random_data(1024*1024)?;
273 let mut random_data
= vec
![];
274 // generate pseudo random byte sequence
275 for i
in 0..256*1024 {
277 let byte
= ((i
>> (j
<<3))&0xff) as u8;
278 random_data
.push(byte
);
282 assert_eq
!(random_data
.len(), 1024*1024);
284 let start_time
= std
::time
::Instant
::now();
288 openssl
::sha
::sha256(&random_data
);
289 bytes
+= random_data
.len();
290 if start_time
.elapsed().as_micros() > 1_000_000 { break; }
292 let speed
= (bytes
as f64)/start_time
.elapsed().as_secs_f64();
293 benchmark_result
.sha256
.speed
= Some(speed
);
295 eprintln
!("SHA256 speed: {:.2} MB/s", speed
/1_000_000_.0);
298 let start_time
= std
::time
::Instant
::now();
302 let mut reader
= &random_data
[..];
303 zstd
::stream
::encode_all(&mut reader
, 1)?
;
304 bytes
+= random_data
.len();
305 if start_time
.elapsed().as_micros() > 3_000_000 { break; }
307 let speed
= (bytes
as f64)/start_time
.elapsed().as_secs_f64();
308 benchmark_result
.compress
.speed
= Some(speed
);
310 eprintln
!("Compression speed: {:.2} MB/s", speed
/1_000_000_.0);
313 let start_time
= std
::time
::Instant
::now();
315 let compressed_data
= {
316 let mut reader
= &random_data
[..];
317 zstd
::stream
::encode_all(&mut reader
, 1)?
322 let mut reader
= &compressed_data
[..];
323 let data
= zstd
::stream
::decode_all(&mut reader
)?
;
325 if start_time
.elapsed().as_micros() > 1_000_000 { break; }
327 let speed
= (bytes
as f64)/start_time
.elapsed().as_secs_f64();
328 benchmark_result
.decompress
.speed
= Some(speed
);
330 eprintln
!("Decompress speed: {:.2} MB/s", speed
/1_000_000_.0);
333 let start_time
= std
::time
::Instant
::now();
337 let mut out
= Vec
::new();
338 crypt_config
.encrypt_to(&random_data
, &mut out
)?
;
339 bytes
+= random_data
.len();
340 if start_time
.elapsed().as_micros() > 1_000_000 { break; }
342 let speed
= (bytes
as f64)/start_time
.elapsed().as_secs_f64();
343 benchmark_result
.aes256_gcm
.speed
= Some(speed
);
345 eprintln
!("AES256/GCM speed: {:.2} MB/s", speed
/1_000_000_.0);
348 let start_time
= std
::time
::Instant
::now();
350 let (chunk
, digest
) = DataChunkBuilder
::new(&random_data
)
356 chunk
.verify_unencrypted(random_data
.len(), &digest
)?
;
357 bytes
+= random_data
.len();
358 if start_time
.elapsed().as_micros() > 1_000_000 { break; }
360 let speed
= (bytes
as f64)/start_time
.elapsed().as_secs_f64();
361 benchmark_result
.verify
.speed
= Some(speed
);
363 eprintln
!("Verify speed: {:.2} MB/s", speed
/1_000_000_.0);