1 use std
::path
::PathBuf
;
8 use proxmox
::api
::{ApiMethod, RpcEnvironment}
;
15 format_and_print_result_full
,
16 default_table_format_options
,
22 use pbs_tools
::crypt_config
::CryptConfig
;
23 use pbs_config
::key_config
::{KeyDerivationConfig, load_and_decrypt_key}
;
24 use pbs_client
::tools
::key_source
::get_encryption_key_password
;
25 use pbs_client
::{BackupRepository, BackupWriter}
;
26 use pbs_datastore
::data_blob
::{DataBlob, DataChunkBuilder}
;
29 KEYFILE_SCHEMA
, REPO_URL_SCHEMA
,
30 extract_repository_from_value
,
36 #[derive(Copy, Clone, Serialize)]
39 /// The meassured speed in Bytes/second
40 #[serde(skip_serializing_if="Option::is_none")]
42 /// Top result we want to compare with
68 #[derive(Copy, Clone, Serialize)]
70 struct BenchmarkResult
{
73 /// SHA256 checksum computation speed
75 /// ZStd level 1 compression speed
77 /// ZStd level 1 decompression speed
79 /// AES256 GCM encryption speed
85 static BENCHMARK_RESULT_2020_TOP
: BenchmarkResult
= BenchmarkResult
{
88 top
: 1_000_000.0 * 1235.0, // TLS to localhost, AMD Ryzen 7 2700X
92 top
: 1_000_000.0 * 2022.0, // AMD Ryzen 7 2700X
96 top
: 1_000_000.0 * 752.0, // AMD Ryzen 7 2700X
100 top
: 1_000_000.0 * 1198.0, // AMD Ryzen 7 2700X
104 top
: 1_000_000.0 * 3645.0, // AMD Ryzen 7 2700X
108 top
: 1_000_000.0 * 758.0, // AMD Ryzen 7 2700X
116 schema
: REPO_URL_SCHEMA
,
120 description
: "Verbose output.",
125 schema
: KEYFILE_SCHEMA
,
129 schema
: OUTPUT_FORMAT
,
135 /// Run benchmark tests
136 pub async
fn benchmark(
139 _rpcenv
: &mut dyn RpcEnvironment
,
140 ) -> Result
<(), Error
> {
142 let repo
= extract_repository_from_value(¶m
).ok();
144 let keyfile
= param
["keyfile"].as_str().map(PathBuf
::from
);
146 let verbose
= param
["verbose"].as_bool().unwrap_or(false);
148 let output_format
= get_output_format(¶m
);
150 let crypt_config
= match keyfile
{
153 let (key
, _
, _
) = load_and_decrypt_key(&path
, &get_encryption_key_password
)?
;
154 let crypt_config
= CryptConfig
::new(key
)?
;
155 Some(Arc
::new(crypt_config
))
159 let mut benchmark_result
= BENCHMARK_RESULT_2020_TOP
;
161 // do repo tests first, because this may prompt for a password
162 if let Some(repo
) = repo
{
163 test_upload_speed(&mut benchmark_result
, repo
, crypt_config
.clone(), verbose
).await?
;
166 test_crypt_speed(&mut benchmark_result
, verbose
)?
;
168 render_result(&output_format
, &benchmark_result
)?
;
173 // print comparison table
176 benchmark_result
: &BenchmarkResult
,
177 ) -> Result
<(), Error
> {
179 let mut data
= serde_json
::to_value(benchmark_result
)?
;
180 let return_type
= ReturnType
::new(false, &BenchmarkResult
::API_SCHEMA
);
182 let render_speed
= |value
: &Value
, _record
: &Value
| -> Result
<String
, Error
> {
183 match value
["speed"].as_f64() {
184 None
=> Ok(String
::from("not tested")),
186 let top
= value
["top"].as_f64().unwrap();
187 Ok(format
!("{:.2} MB/s ({:.0}%)", speed
/1_000_000.0, (speed
*100.0)/top
))
192 let options
= default_table_format_options()
193 .column(ColumnConfig
::new("tls")
194 .header("TLS (maximal backup upload speed)")
195 .right_align(false).renderer(render_speed
))
196 .column(ColumnConfig
::new("sha256")
197 .header("SHA256 checksum computation speed")
198 .right_align(false).renderer(render_speed
))
199 .column(ColumnConfig
::new("compress")
200 .header("ZStd level 1 compression speed")
201 .right_align(false).renderer(render_speed
))
202 .column(ColumnConfig
::new("decompress")
203 .header("ZStd level 1 decompression speed")
204 .right_align(false).renderer(render_speed
))
205 .column(ColumnConfig
::new("verify")
206 .header("Chunk verification speed")
207 .right_align(false).renderer(render_speed
))
208 .column(ColumnConfig
::new("aes256_gcm")
209 .header("AES256 GCM encryption speed")
210 .right_align(false).renderer(render_speed
));
213 format_and_print_result_full(&mut data
, &return_type
, output_format
, &options
);
218 async
fn test_upload_speed(
219 benchmark_result
: &mut BenchmarkResult
,
220 repo
: BackupRepository
,
221 crypt_config
: Option
<Arc
<CryptConfig
>>,
223 ) -> Result
<(), Error
> {
225 let backup_time
= proxmox
::tools
::time
::epoch_i64();
227 let client
= connect(&repo
)?
;
228 record_repository(&repo
);
230 if verbose { eprintln!("Connecting to backup server"); }
231 let client
= BackupWriter
::start(
233 crypt_config
.clone(),
242 if verbose { eprintln!("Start TLS speed test"); }
243 let speed
= client
.upload_speedtest(verbose
).await?
;
245 eprintln
!("TLS speed: {:.2} MB/s", speed
/1_000_000.0);
247 benchmark_result
.tls
.speed
= Some(speed
);
252 // test hash/crypt/compress speed
254 benchmark_result
: &mut BenchmarkResult
,
256 ) -> Result
<(), Error
> {
260 let kdf
= KeyDerivationConfig
::Scrypt
{
267 let testkey
= kdf
.derive_key(pw
)?
;
269 let crypt_config
= CryptConfig
::new(testkey
)?
;
271 //let random_data = proxmox::sys::linux::random_data(1024*1024)?;
272 let mut random_data
= vec
![];
273 // generate pseudo random byte sequence
274 for i
in 0..256*1024 {
276 let byte
= ((i
>> (j
<<3))&0xff) as u8;
277 random_data
.push(byte
);
281 assert_eq
!(random_data
.len(), 1024*1024);
283 let start_time
= std
::time
::Instant
::now();
287 openssl
::sha
::sha256(&random_data
);
288 bytes
+= random_data
.len();
289 if start_time
.elapsed().as_micros() > 1_000_000 { break; }
291 let speed
= (bytes
as f64)/start_time
.elapsed().as_secs_f64();
292 benchmark_result
.sha256
.speed
= Some(speed
);
294 eprintln
!("SHA256 speed: {:.2} MB/s", speed
/1_000_000.0);
297 let start_time
= std
::time
::Instant
::now();
301 let mut reader
= &random_data
[..];
302 zstd
::stream
::encode_all(&mut reader
, 1)?
;
303 bytes
+= random_data
.len();
304 if start_time
.elapsed().as_micros() > 3_000_000 { break; }
306 let speed
= (bytes
as f64)/start_time
.elapsed().as_secs_f64();
307 benchmark_result
.compress
.speed
= Some(speed
);
309 eprintln
!("Compression speed: {:.2} MB/s", speed
/1_000_000.0);
312 let start_time
= std
::time
::Instant
::now();
314 let compressed_data
= {
315 let mut reader
= &random_data
[..];
316 zstd
::stream
::encode_all(&mut reader
, 1)?
321 let mut reader
= &compressed_data
[..];
322 let data
= zstd
::stream
::decode_all(&mut reader
)?
;
324 if start_time
.elapsed().as_micros() > 1_000_000 { break; }
326 let speed
= (bytes
as f64)/start_time
.elapsed().as_secs_f64();
327 benchmark_result
.decompress
.speed
= Some(speed
);
329 eprintln
!("Decompress speed: {:.2} MB/s", speed
/1_000_000.0);
332 let start_time
= std
::time
::Instant
::now();
336 let mut out
= Vec
::new();
337 DataBlob
::encrypt_benchmark(&crypt_config
, &random_data
, &mut out
)?
;
338 bytes
+= random_data
.len();
339 if start_time
.elapsed().as_micros() > 1_000_000 { break; }
341 let speed
= (bytes
as f64)/start_time
.elapsed().as_secs_f64();
342 benchmark_result
.aes256_gcm
.speed
= Some(speed
);
344 eprintln
!("AES256/GCM speed: {:.2} MB/s", speed
/1_000_000.0);
347 let start_time
= std
::time
::Instant
::now();
349 let (chunk
, digest
) = DataChunkBuilder
::new(&random_data
)
355 chunk
.verify_unencrypted(random_data
.len(), &digest
)?
;
356 bytes
+= random_data
.len();
357 if start_time
.elapsed().as_micros() > 1_000_000 { break; }
359 let speed
= (bytes
as f64)/start_time
.elapsed().as_secs_f64();
360 benchmark_result
.verify
.speed
= Some(speed
);
362 eprintln
!("Verify speed: {:.2} MB/s", speed
/1_000_000.0);