From 9aa1a7224f25c1cc15c6edb3c9fda25b13b048f6 Mon Sep 17 00:00:00 2001 From: Wei Ji <23487320+weiji14@users.noreply.github.com> Date: Sat, 22 Nov 2025 18:22:37 +1300 Subject: [PATCH 1/4] Benchmark code for reading sample Sentinel-2 TCI file Adapted from https://github.com/weiji14/foss4g2025/pull/3 --- Cargo.toml | 6 +++ benches/read_tiff.rs | 97 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 benches/read_tiff.rs diff --git a/Cargo.toml b/Cargo.toml index 3c58d05..12e8135 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,9 @@ tokio = { version = "1.43.0", optional = true, default-features = false, feature weezl = "0.1.0" [dev-dependencies] +criterion = { package = "codspeed-criterion-compat", version = "4.1.0" } object_store = { version = "0.12", features = ["http"] } +rayon = "1.11.0" tiff = "0.9.1" tokio = { version = "1.9", features = [ "macros", @@ -42,3 +44,7 @@ reqwest = ["dep:reqwest"] object_store = ["dep:object_store"] [package.metadata.cargo-all-features] + +[[bench]] +name = "read_tiff" +harness = false diff --git a/benches/read_tiff.rs b/benches/read_tiff.rs new file mode 100644 index 0000000..fdcca5d --- /dev/null +++ b/benches/read_tiff.rs @@ -0,0 +1,97 @@ +/// Benchmarks on reading a GeoTIFF +use std::path::PathBuf; +use std::sync::Arc; + +use async_tiff::decoder::DecoderRegistry; +use async_tiff::error::{AsyncTiffError, AsyncTiffResult}; +use async_tiff::metadata::{PrefetchBuffer, TiffMetadataReader}; +use async_tiff::reader::ObjectReader; +use async_tiff::{ImageFileDirectory, Tile}; +use criterion::{criterion_group, criterion_main, Criterion, Throughput}; +use object_store::path::Path; +use object_store::{parse_url, ObjectStore}; +use rayon::iter::{IntoParallelIterator, ParallelIterator}; +use rayon::ThreadPoolBuilder; +use reqwest::Url; +use tokio::runtime; + +fn read_tiff(fpath: &str) -> AsyncTiffResult<()> { + let abs_path: PathBuf = std::path::Path::new(fpath).canonicalize()?; + let tif_url: Url = Url::from_file_path(abs_path).expect("Failed to parse url: {abs_path}"); + let (store, path): (Box, Path) = parse_url(&tif_url)?; + + let reader = ObjectReader::new(Arc::new(store), path); + let decoder_registry = DecoderRegistry::default(); + + // Initialize async runtime + let runtime = runtime::Builder::new_current_thread() + .enable_all() + .build()?; + + // Get list of tiles in TIFF file stream (using tokio async runtime) + let tiles: Vec = runtime + .block_on(async { + // Read metadata header + let prefetch_reader = PrefetchBuffer::new(reader.clone(), 32 * 1024).await?; + let mut metadata_reader = TiffMetadataReader::try_open(&prefetch_reader).await?; + + // Read Image File Directories + let ifds: Vec = + metadata_reader.read_all_ifds(&prefetch_reader).await?; + + assert_eq!(ifds.len(), 1); // should have only 1 IFD + let ifd: &ImageFileDirectory = ifds.first().ok_or(AsyncTiffError::General( + "unable to read first IFD".to_string(), + ))?; + + let (x_count, y_count) = ifd.tile_count().ok_or(AsyncTiffError::General( + "unable to get IFD count".to_string(), + ))?; + // dbg!(x_count, y_count); // 43 * 43 = 1849 + + // Get cartesian product of x and y tile ids + let x_ids: Vec = (0..x_count) + .flat_map(|i| (0..y_count).map(move |_j| i)) + .collect(); + let y_ids: Vec = (0..x_count).flat_map(|_i| 0..y_count).collect(); + + let tiles: Vec = ifd.fetch_tiles(&x_ids, &y_ids, &reader).await?; + assert_eq!(tiles.len(), 1849); + + Ok::, AsyncTiffError>(tiles) + }) + .unwrap(); + + // Do actual decoding of TIFF tile data (multi-threaded using rayon) + let pool = ThreadPoolBuilder::new() + .num_threads(4) + .build() + .map_err(|err| AsyncTiffError::External(Box::new(err)))?; + + let tile_bytes: Vec = pool.install(|| { + tiles + .into_par_iter() + .flat_map_iter(|tile| tile.decode(&decoder_registry).unwrap()) + .collect() + }); + assert_eq!(tile_bytes.len(), 363528192); // should be 361681200, why not? + + Ok(()) +} + +pub fn criterion_benchmark(c: &mut Criterion) { + let mut group = c.benchmark_group("read_tiff"); + + let fsize: u64 = std::fs::metadata("benches/TCI_lzw.tif").unwrap().len(); + group.throughput(Throughput::BytesDecimal(fsize)); // 55MB filesize + + // CPU decoding using async-tiff + group.sample_size(30); + group.bench_function("async-tiff", move |b| { + b.iter(|| read_tiff("benches/TCI_lzw.tif")) + }); + group.finish(); +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); From 247d699c412263b60ade1cd07a745a503828df74 Mon Sep 17 00:00:00 2001 From: Wei Ji <23487320+weiji14@users.noreply.github.com> Date: Sat, 22 Nov 2025 18:23:09 +1300 Subject: [PATCH 2/4] ci: Setup benchmark CI running cargo-codspeed Xref https://github.com/weiji14/foss4g2025/pull/2 --- .github/workflows/bench.yml | 59 +++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 .github/workflows/bench.yml diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml new file mode 100644 index 0000000..db17008 --- /dev/null +++ b/.github/workflows/bench.yml @@ -0,0 +1,59 @@ +# Run performance benchmarks +name: Benchmarks + +on: + push: + branches: ["main"] + release: + types: [published] + pull_request: + types: [opened, reopened, synchronize] + branches: ["main"] + +env: + CARGO_TERM_COLOR: always + +permissions: + contents: read + +jobs: + rust: + runs-on: ubuntu-24.04 + container: + image: ghcr.io/osgeo/gdal:ubuntu-small-3.11.5 + options: --privileged + + steps: + - name: Install dev dependencies and setup git + run: | + apt update + apt install -y build-essential cmake git libclang-dev pkg-config + git config --global --add safe.directory $GITHUB_WORKSPACE + + - name: Checkout repository + uses: actions/checkout@v6 + with: + persist-credentials: false + + - name: Download and extract files + run: | + gdal raster convert --co COMPRESS=LZW --co TILED=YES --co PREDICTOR=2 https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/12/S/UF/2022/6/S2B_12SUF_20220609_0_L2A/TCI.tif benches/TCI_lzw.tif + ls -lh benches/ + + - name: Setup rust toolchain, cache and cargo-codspeed binary + uses: moonrepo/setup-rust@v1 + with: + channel: stable + cache: false + cache-target: release + bins: cargo-codspeed + + - name: Build the benchmark target(s) + run: cargo codspeed build + + - name: Run the benchmarks + if: matrix.toolchain == 'stable' + uses: CodSpeedHQ/action@v4 + with: + mode: instrumentation + run: cargo codspeed run From 660cc971ab8215225b7afbd83f9bf2836d0c76cc Mon Sep 17 00:00:00 2001 From: Wei Ji <23487320+weiji14@users.noreply.github.com> Date: Sat, 22 Nov 2025 18:32:03 +1300 Subject: [PATCH 3/4] Remove extra if-statement --- .github/workflows/bench.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index db17008..8194fad 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -52,7 +52,6 @@ jobs: run: cargo codspeed build - name: Run the benchmarks - if: matrix.toolchain == 'stable' uses: CodSpeedHQ/action@v4 with: mode: instrumentation From 3e6cfb1a123c286834f8e51d5f376d729211fb32 Mon Sep 17 00:00:00 2001 From: Wei Ji <23487320+weiji14@users.noreply.github.com> Date: Sun, 23 Nov 2025 07:59:29 +1300 Subject: [PATCH 4/4] Set id-token: write permissions for OIDC auth Xref https://codspeed.io/changelog/2025-11-19-simpler-authentication-with-oidc. Also change from the deprecated 'instrumentation' mode to 'simulation' --- .github/workflows/bench.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index 8194fad..66831cb 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -13,15 +13,15 @@ on: env: CARGO_TERM_COLOR: always -permissions: - contents: read - jobs: - rust: + rust-bench: runs-on: ubuntu-24.04 container: image: ghcr.io/osgeo/gdal:ubuntu-small-3.11.5 options: --privileged + permissions: + contents: read # required for actions/checkout + id-token: write # required for OIDC authentication with CodSpeed steps: - name: Install dev dependencies and setup git @@ -54,5 +54,5 @@ jobs: - name: Run the benchmarks uses: CodSpeedHQ/action@v4 with: - mode: instrumentation + mode: simulation run: cargo codspeed run