From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id 345058D85 for ; Mon, 31 Jul 2023 15:34:45 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 168B8397D for ; Mon, 31 Jul 2023 15:34:15 +0200 (CEST) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [94.136.29.106]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS for ; Mon, 31 Jul 2023 15:34:14 +0200 (CEST) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id CB9A942D2C for ; Mon, 31 Jul 2023 15:34:13 +0200 (CEST) From: Max Carrara To: pbs-devel@lists.proxmox.com Date: Mon, 31 Jul 2023 15:34:03 +0200 Message-Id: <20230731133404.859756-1-m.carrara@proxmox.com> X-Mailer: git-send-email 2.39.2 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL -0.008 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record T_SCC_BODY_TEXT_LINE -0.01 - Subject: [pbs-devel] [PATCH v2 pxar 1/2] Add `compare-read.rs` to examples and drop feature `async-examples` X-BeenThere: pbs-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox Backup Server development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 31 Jul 2023 13:34:45 -0000 `compare-read.rs` may be used to compare read speeds between pxar accessors and decoders, both synchronous and asynchronous versions. The `async-examples` feature is dropped in favour of declaring `[dev-dependencies]` in `Cargo.toml`. Signed-off-by: Max Carrara --- Changes v1 --> v2: * Remove addition of `tokio/io-util` as dependency * Add example `compare-read` * Remove feature `async-example` in favour of `[dev-dependencies]` Cargo.toml | 20 +++--- examples/compare-read.rs | 150 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 162 insertions(+), 8 deletions(-) create mode 100644 examples/compare-read.rs diff --git a/Cargo.toml b/Cargo.toml index d120e70..8669e30 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,10 @@ exclude = [ [[example]] name = "apxar" path = "examples/apxar.rs" -required-features = [ "async-example" ] + +[[example]] +name = "compare-read" +path = "examples/compare-read.rs" [[example]] name = "pxarcmd" @@ -47,6 +50,14 @@ siphasher = "0.3" tokio = { version = "1.0", optional = true, default-features = false } +[dev-dependencies] +anyhow = "1.0" +tokio = { version = "1.0", default-features = false, features = [ + "fs", + "macros", + "rt-multi-thread" +] } + [target.'cfg(target_os = "linux")'.dependencies] libc = "0.2" @@ -57,11 +68,4 @@ tokio-fs = [ "tokio-io", "tokio/fs" ] full = [ "tokio-fs"] -async-example = [ - "tokio-io", - "tokio-fs", - "tokio/rt-multi-thread", - "tokio/macros", -] - test-harness = [] diff --git a/examples/compare-read.rs b/examples/compare-read.rs new file mode 100644 index 0000000..c908077 --- /dev/null +++ b/examples/compare-read.rs @@ -0,0 +1,150 @@ +//! # Compare Read Speeds of Accessors and Decoders +//! +//! This example is used to compare read speeds between: +//! +//! * [`aio::Accessor`][pxar::accessor::aio::Accessor] +//! * [`sync::Accessor`][pxar::accessor::sync::Accessor] +//! * [`aio::Decoder`][pxar::decoder::aio::Decoder] +//! * [`sync::Decoder`][pxar::decoder::sync::Decoder] +//! +//! ## Usage +//! +//! You may run this example directly on a PXAR archive: +//! +//! ```bash +//! cargo run -q --example compare-read [FILE_PATH] +//! cargo run -q --release --example compare-read [FILE_PATH] +//! ``` + +use std::ffi::OsStr; +use std::future::Future; +use std::time::Duration; + +use anyhow::{Context, Result}; +use pxar::accessor::aio::Accessor as AioAccessor; +use pxar::accessor::sync::Accessor as SyncAccessor; +use pxar::decoder::aio::Decoder as AioDecoder; +use pxar::decoder::sync::Decoder as SyncDecoder; + +async fn read_with_decoder(file: &OsStr) -> Result<()> { + let file = tokio::fs::File::open(file) + .await + .context("failed to open file")?; + + let mut reader = AioDecoder::from_tokio(file) + .await + .context("failed to open pxar archive contents")?; + + let mut entries = vec![]; + + while let Some(entry) = reader.next().await { + let entry = entry.context("failed to parse entry")?; + + entries.push(entry); + } + + Ok(()) +} + +async fn read_with_decoder_sync(file: &OsStr) -> Result<()> { + let file = std::fs::File::open(file).context("failed to open file")?; + + let reader = SyncDecoder::from_std(file).context("failed to open pxar archive contents")?; + + let mut entries = vec![]; + + for entry in reader { + let entry = entry.context("failed to parse entry")?; + entries.push(entry); + } + + Ok(()) +} + +async fn read_with_accessor(file: &OsStr) -> Result<()> { + let accessor = AioAccessor::open(file) + .await + .context("failed to open pxar archive contents")?; + + let dir = accessor.open_root_ref().await?; + let mut decode_full = dir.decode_full().await?; + + let mut entries = vec![]; + + while let Some(entry) = decode_full.next().await { + let entry = entry.context("failed to parse entry")?; + + entries.push(entry); + } + + Ok(()) +} + +async fn read_with_accessor_sync(file: &OsStr) -> Result<()> { + let accessor = SyncAccessor::open(file).context("failed to open pxar archive contents")?; + + let dir = accessor.open_root_ref()?; + let decode_full = dir.decode_full()?; + + let mut entries = vec![]; + + for entry in decode_full { + let entry = entry.context("failed to parse entry")?; + + entries.push(entry); + } + + Ok(()) +} + +async fn measure_duration(future: F) -> (R, Duration) +where + F: Future, + R: Send + 'static, +{ + use std::time::Instant; + let start = Instant::now(); + let return_value = future.await; + let elapsed = start.elapsed(); + + (return_value, elapsed) +} + +async fn run_reads(file: &OsStr) -> Result<()> { + let (result, elapsed) = measure_duration(read_with_decoder(&file)).await; + println!("With aio::Decoder: {result:?} (elapsed: {elapsed:#?})"); + + let (result, elapsed) = measure_duration(read_with_decoder_sync(&file)).await; + println!("With sync::Decoder: {result:?} (elapsed: {elapsed:#?})"); + + let (result, elapsed) = measure_duration(read_with_accessor(&file)).await; + println!("With aio::Accessor: {result:?} (elapsed: {elapsed:#?})"); + + let (result, elapsed) = measure_duration(read_with_accessor_sync(&file)).await; + println!("With sync::Accessor: {result:?} (elapsed: {elapsed:#?})"); + + Ok(()) +} + +#[tokio::main] +async fn main() -> Result<()> { + let mode = if cfg!(debug_assertions) { + "debug" + } else { + "release" + }; + + println!("PXAR Read Performance Comparison"); + println!("Running in mode: {mode}\n"); + + let mut args = std::env::args_os().skip(1); + + let file = args.next().context("expected file name")?; + println!("First pass:"); + run_reads(&file).await?; + + println!("\nSecond pass:"); + run_reads(&file).await?; + + Ok(()) +} -- 2.39.2