Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion encodings/onpair/src/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ fn build_dictionary(
/// dictionary is memoized in [`OnPairData`]. Once cached, subsequent calls —
/// including on arrays derived by slice / filter / cast, which share the cell —
/// pay neither cost again.
pub(crate) fn dict_view<'a>(
pub fn dict_view<'a>(
array: ArrayView<'a, OnPair>,
ctx: &mut ExecutionCtx,
) -> VortexResult<CompactDictionaryView<'a>> {
Expand Down
4 changes: 0 additions & 4 deletions encodings/onpair/src/decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,6 @@ pub(crate) fn collect_widened<T: NativePType>(
.into_buffer::<T>())
}

/// Read one `codes_offsets` boundary by point lookup. This decodes at most a
/// single chunk of the child — never the whole per-row offsets array — so the
/// callers that only need a row window (`scalar_at`, the canonical decode's
/// start/end bounds) don't pay to materialise every boundary.
pub(crate) fn code_boundary_at(
codes_offsets: &ArrayRef,
index: usize,
Expand Down
3 changes: 3 additions & 0 deletions encodings/onpair/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,12 @@ mod tests;

pub use array::*;
pub use compress::*;
pub use onpair::CompactDictionaryView;
pub use onpair::Config;
pub use onpair::DEFAULT_CONFIG;
pub use onpair::DictionaryView;
pub use onpair::Error as OnPairError;
pub use onpair::MAX_TOKEN_SIZE;
pub use onpair::MaxDictBits;
pub use onpair::Threshold;
use vortex_array::session::ArraySessionExt;
Expand Down
5 changes: 5 additions & 0 deletions vortex-cuda/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ vortex-cub = { path = "cub" }
vortex-cuda-macros = { workspace = true }
vortex-error = { workspace = true, features = ["object_store"] }
vortex-nvcomp = { path = "nvcomp" }
vortex-onpair = { workspace = true }

[target.'cfg(target_os = "linux")'.dependencies]
rustix = { workspace = true }
Expand Down Expand Up @@ -108,6 +109,10 @@ harness = false
name = "fsst_cuda"
harness = false

[[bench]]
name = "onpair_cuda"
harness = false

[[bench]]
name = "list_view_cuda"
harness = false
Expand Down
1 change: 0 additions & 1 deletion vortex-cuda/benches/arrow_binary_cuda.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

#![expect(clippy::cast_possible_truncation)]

#[allow(dead_code)]
mod bench_config;
mod timed_launch_strategy;

Expand Down
4 changes: 4 additions & 0 deletions vortex-cuda/benches/bench_config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ use criterion::Criterion;
///
/// 100M elements keeps every kernel above ~500 µs, well above the
/// ~15 µs CUDA driver noise floor that caused 15-45% swings at 10M.
// Each bench binary includes this module textually, and the string-heavy
// benches (fsst, onpair, arrow_binary, list_view) define their own smaller
// sizes instead of this const — allow it to go unused in those binaries.
#[allow(dead_code)]
Comment thread
robert3005 marked this conversation as resolved.
pub const BENCH_SIZES: &[(usize, &str)] = &[(100_000_000, "100M")];

/// Returns a [`Criterion`] configuration tuned for CUDA benchmarks.
Expand Down
1 change: 0 additions & 1 deletion vortex-cuda/benches/fsst_cuda.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

#![expect(clippy::unwrap_used)]

#[allow(dead_code)]
mod bench_config;
mod timed_launch_strategy;

Expand Down
1 change: 0 additions & 1 deletion vortex-cuda/benches/list_view_cuda.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

#![expect(clippy::cast_possible_truncation)]

#[allow(dead_code)]
mod bench_config;
mod timed_launch_strategy;

Expand Down
110 changes: 110 additions & 0 deletions vortex-cuda/benches/onpair_cuda.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: Copyright the Vortex contributors

//! CUDA benchmarks for OnPair decompression.

#![expect(clippy::unwrap_used)]

mod bench_config;
mod timed_launch_strategy;

use std::sync::Arc;
use std::sync::atomic::Ordering;
use std::time::Duration;

use criterion::BenchmarkId;
use criterion::Criterion;
use criterion::Throughput;
use futures::executor::block_on;
use vortex::array::ArrayRef;
use vortex::array::IntoArray;
use vortex::array::arrays::VarBinArray;
use vortex::dtype::DType;
use vortex::dtype::Nullability;
use vortex::error::VortexExpect;
use vortex_cuda::CudaDispatchMode;
use vortex_cuda::CudaSession;
use vortex_cuda::executor::CudaArrayExt;
use vortex_cuda_macros::cuda_available;
use vortex_cuda_macros::cuda_not_available;
use vortex_onpair::DEFAULT_CONFIG;
use vortex_onpair::onpair_compress;

use crate::timed_launch_strategy::TimedLaunchStrategy;

// Bench-local size instead of the workspace 100M default: each input is a
// URL-shaped string, much heavier per-element than the fixed-width primitives
// other kernels benchmark.
const BENCH_SIZES: &[(usize, &str)] = &[(10_000_000, "10M")];

struct OnPairBenchFixture {
array: ArrayRef,
uncompressed_size: u64,
}

fn make_fixture(n: usize) -> OnPairBenchFixture {
let mut setup_ctx = CudaSession::create_execution_ctx(&vortex_cuda::cuda_session())
.vortex_expect("failed to create execution context");

let strings: Vec<String> = (0..n)
.map(|i| format!("https://www.example.com/path/{i}/segment?q={}", i % 97))
.collect();
let uncompressed_size = strings.iter().map(|s| s.len() as u64).sum();
let varbin = VarBinArray::from_iter(
strings.iter().map(|s| Some(s.as_str())),
DType::Utf8(Nullability::NonNullable),
)
.into_array();
let array = onpair_compress(&varbin, DEFAULT_CONFIG, setup_ctx.execution_ctx())
.vortex_expect("OnPair compression failed");

OnPairBenchFixture {
array,
uncompressed_size,
}
}

fn benchmark_onpair_cuda_decompress(c: &mut Criterion) {
let mut group = c.benchmark_group("cuda");

for &(n, len_str) in BENCH_SIZES {
let fixture = make_fixture(n);

group.throughput(Throughput::Bytes(fixture.uncompressed_size));
group.bench_with_input(
BenchmarkId::new("cuda/onpair/decompress_to_varbinview", len_str),
&fixture.array,
|b, onpair_array| {
b.iter_custom(|iters| {
let timed = TimedLaunchStrategy::default();
let timer = timed.timer();

let mut cuda_ctx =
CudaSession::create_execution_ctx(&vortex_cuda::cuda_session())
.vortex_expect("failed to create execution context")
.with_dispatch_mode(CudaDispatchMode::StandaloneOnly)
.with_launch_strategy(Arc::new(timed));

for _ in 0..iters {
block_on(onpair_array.clone().execute_cuda(&mut cuda_ctx)).unwrap();
}
Duration::from_nanos(timer.load(Ordering::Relaxed))
});
},
);
}

group.finish();
}

criterion::criterion_group! {
name = benches;
config = bench_config::cuda_bench_config();
targets = benchmark_onpair_cuda_decompress
}

#[cuda_available]
criterion::criterion_main!(benches);

#[cuda_not_available]
fn main() {}
1 change: 1 addition & 0 deletions vortex-cuda/cub/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ fn generate_rust_bindings(kernels_dir: &Path, out_dir: &Path) {
.allowlist_function("filter_bytemask_.*")
.allowlist_function("filter_bitmask_.*")
.allowlist_function("scan_exclusive_sum_.*")
.allowlist_function("onpair_.*")
// Allow CUDA types
.allowlist_type("cudaError_t")
// Blocklist cudaStream_t and define it manually as an opaque pointer
Expand Down
20 changes: 20 additions & 0 deletions vortex-cuda/cub/kernels/filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,26 @@ cudaError_t scan_exclusive_sum_i64(void *d_temp,
int64_t num_items,
cudaStream_t stream);

// Fused OnPair per-batch offsets regeneration: a single sweep kernel reduces
// each 128-token batch's decoded size and exclusive-scans the sizes in-kernel
// via decoupled look-back, writing `num_batches + 1` offsets whose last
// element is the total decoded byte count. `code_width` selects the code
// stream's element size in bytes (1 or 2). A code outside the dictionary
// raises `*status` to 1 and contributes zero bytes.
cudaError_t onpair_batch_offsets_temp_size(size_t *temp_bytes, int64_t num_batches);

cudaError_t onpair_batch_offsets(void *d_temp,
size_t temp_bytes,
const void *codes,
uint32_t code_width,
const uint8_t *lens,
uint32_t dict_size,
uint64_t total_tokens,
uint64_t *chunk_offsets,
uint32_t *status,
int64_t num_batches,
cudaStream_t stream);

#ifdef __cplusplus
}
#endif
Loading
Loading