Skip to content

About

A scalable single-GPU engine for subgraph query processing on large labeled graphs, featuring vectorized execution and workload decomposition.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

VORA

VORA is a scalable single-GPU engine for subgraph query processing on large, labeled graphs. It combines fused vector-based matching with workload decomposition, so it can evaluate graph-pattern queries whose data and intermediate results exceed GPU memory while still producing either count(*) or columnar match results for downstream query processing.

VORA supports all nine queries (Q1–Q9) in the Labelled Subgraph Query Benchmark (LSQB). Its design and evaluation are presented in Vora: A Vector-Based Engine for Scalable GPU-Accelerated Subgraph Query Processing, accepted for publication at the 2027 IEEE International Conference on Data Engineering (ICDE).

VORA architecture showing the decomposition optimizer, vectorized graph storage, and vector-based GPU execution engine

VORA decomposes a query into local tasks, transfers and merges graph shards, and evaluates local tasks using fused vector operators.

Features

  • VectorFlux execution. A lightweight CUDA vector library provides composable data-parallel operators and compile-time programmer-controllable operator fusion, reducing intermediate materialization and global-memory traffic.
  • GPU graph primitives. Batched neighbor retrieval and batched edge existence checking implement the expand and expand-into operations used by vectorized matching.
  • Compact typed graph layout. Edges are organized into relations by (source-label, edge-label, destination-label) and stored in forward and reverse Compressed Unique Source (CUS) form.
  • Out-of-core single-GPU execution. Fine-grained physical shards are merged into query-dependent logical shards at runtime; each local task obeys an explicit GPU-memory budget.
  • LSQB support. Utilities and query definitions support all nine LSQB benchmark queries, with count-only and materialized-result execution.

Requirements

  • Linux
  • An NVIDIA GPU and a CUDA toolkit compatible with the installed RAPIDS stack
  • CMake 3.30.4 or newer (required by the tested RAFT 25.10 package)
  • A C++17-capable compiler
  • RMM and RAFT, installed with CMake package files discoverable through CMAKE_PREFIX_PATH, rmm_DIR, and raft_DIR
  • liburing development files when building with the default VORA_ENABLE_IO_URING=ON I/O backend

The default CUDA architecture is 86 (for example, RTX 3090). Override it through CMake's standard CMAKE_CUDA_ARCHITECTURES variable when building for another GPU. For example, use -DCMAKE_CUDA_ARCHITECTURES=80 for a single architecture or -DCMAKE_CUDA_ARCHITECTURES="80;86" for multiple architectures.

Tested environment

The end-to-end smoke test below was verified with:

  • Ubuntu 22.04, Linux 5.15
  • NVIDIA RTX 3090, compute capability 8.6
  • NVIDIA driver 535.309.01
  • CUDA Toolkit 12.1.105
  • GCC 11.4.0
  • CMake 4.1.0
  • RMM 25.10.0
  • RAFT 25.10.0

Other versions and GPU architectures have not been systematically tested.

The pinned Conda environment installs CMake, the CUDA compiler components, RMM, and RAFT used by this configuration. GCC 11.4.0, liburing 2.1 development files (when VORA_ENABLE_IO_URING=ON), and the NVIDIA driver are supplied by the host system.

conda env create -f environment.yml
conda activate vora

Build

Build outside the source directory:

cmake -S . -B build \
  -DCMAKE_BUILD_TYPE=Release \
  -DCMAKE_CUDA_ARCHITECTURES=86 \
  -DCMAKE_PREFIX_PATH=/path/to/rapids/install
cmake --build build --parallel

If RMM and RAFT were installed independently, pass their package directories instead:

cmake -S . -B build \
  -Drmm_DIR=/path/to/rmm/lib/cmake/rmm \
  -Draft_DIR=/path/to/raft/lib/cmake/raft

Enabling or disabling operator fusion

Operator fusion is enabled by default. Control it at CMake configuration time with VORA_ENABLE_OPERATOR_FUSION:

# Enable operator fusion (default)
cmake -S . -B build-fusion \
  -DCMAKE_BUILD_TYPE=Release \
  -DCMAKE_CUDA_ARCHITECTURES=86 \
  -DVORA_ENABLE_OPERATOR_FUSION=ON
cmake --build build-fusion --parallel

# Disable operator fusion
cmake -S . -B build-no-fusion \
  -DCMAKE_BUILD_TYPE=Release \
  -DCMAKE_CUDA_ARCHITECTURES=86 \
  -DVORA_ENABLE_OPERATOR_FUSION=OFF
cmake --build build-no-fusion --parallel

The option applies to all targets through the cuda_interface compile definition. Use separate build directories when comparing the two variants so that objects compiled with different settings are not mixed.

Building with or without io_uring

The high-performance file-backed reader uses io_uring with O_DIRECT and is enabled by default. This mode requires the liburing development headers and library:

cmake -S . -B build \
  -DCMAKE_BUILD_TYPE=Release \
  -DVORA_ENABLE_IO_URING=ON
cmake --build build --parallel

On a machine without liburing, disable this backend explicitly. VORA then uses synchronous buffered pread while preserving the same file-backed graph functionality:

cmake -S . -B build-buffered \
  -DCMAKE_BUILD_TYPE=Release \
  -DVORA_ENABLE_IO_URING=OFF
cmake --build build-buffered --parallel

The buffered backend may provide lower shard-loading throughput. --disk_read_block_size controls its maximum read chunk; --disk_read_queue_depth is accepted for command-line compatibility but has no effect. The --use_mmap runtime mode is independent of this build option. Use separate build directories when comparing I/O backends.

The build provides the following VORA executables:

Executable Purpose
preprocess Converts raw LSQB input into VORA's preprocessed representation.
partition Creates hash-based physical graph partitions.
vora_lsqb Runs LSQB queries using VORA's hash-partitioned execution.

It also provides executables for the simple-partition baseline:

Executable Purpose
simple_partition Creates edge-based physical graph partitions for the baseline.
vora_lsqb_simple_partitioned Runs LSQB queries using the simple-partition baseline.

Datasets

VORA uses the Linked Streams Query Benchmark (LSQB), which defines nine subgraph queries (Q1–Q9) over labeled social-network graphs.

Dataset Included Purpose
LSQB SF0.003 projected-FK Yes Correctness and smoke testing
Larger LSQB scale factors No Performance evaluation and full experiments

The included SF0.003 projected-FK dataset is located at data/lsqb/social-network-sf0.003-projected-fk. It is intended only for correctness testing and is too small for performance evaluation. Obtain larger projected-FK datasets from https://ldbcouncil.org/data-sets-surf-repository/lsqb.html.

Data preparation and execution

The usual workflow is:

  1. Preprocess raw graph files with build/preprocess.
  2. Partition the preprocessed graph using build/partition, or use build/simple_partition for the simple-partition baseline.
  3. Run one of the partitioned execution binaries on the generated partition directory.

End-to-end smoke test

From the repository root, run the following commands after building VORA. They preprocess the included fixture, create a single hash partition per vertex label, execute count-only LSQB Q1, and verify its deterministic match count. Set SMOKE_GPU to the GPU that should run the test.

set -o pipefail

SMOKE_GPU=${SMOKE_GPU:-0}
SMOKE_DIR=${SMOKE_DIR:-build/smoke-test}
mkdir -p "$SMOKE_DIR"

build/preprocess \
  --input_dir data/lsqb/social-network-sf0.003-projected-fk \
  --output_dir "$SMOKE_DIR/preprocessed" \
  --device "$SMOKE_GPU"

build/partition \
  --input_dir "$SMOKE_DIR/preprocessed" \
  --output_dir "$SMOKE_DIR/partitioned" \
  --device "$SMOKE_GPU" \
  --load_preprocessed_raw_edge_sets_from_file True \
  --partition_threshold_for_vertices 50000 \
  --num_physical_partitions_for_vertices 1

build/vora_lsqb \
  --data "$SMOKE_DIR/partitioned" \
  --query 1 \
  --device "$SMOKE_GPU" \
  --load_built_relations_from_file True \
  --count_only True | tee "$SMOKE_DIR/query.log"

grep -Eq '^NUM_MATCHES[[:space:]]+20608$' "$SMOKE_DIR/query.log"

The preprocessing and partitioning stages should each print [Info] All Finished!. Near the end of query execution, the stable result is:

RUN Q1
NUM_MATCHES                  20608

Timing and transfer-statistic lines vary by machine. The final grep exits successfully only when the expected match count is present. This fixture is a correctness smoke test and is too small for meaningful performance evaluation.

Data representation and partitioning

For each directed edge type, VORA stores both forward and reverse CUS relations. A CUS relation contains uniqueSrc, deg, and dst vectors: uniqueSrc lists the distinct source vertices, deg gives each source's degree, and dst densely stores all destination vertices. This layout is compact for sparse typed relations and enables traversal in either direction without runtime reordering.

Conversion from a graph and sorted edge table to the Compressed Unique Source representation

CUS stores each distinct source once, its degree, and a densely packed destination vector.

The default partition workflow uses a two-key hash partitioning scheme over source and destination vertex IDs. VORA first builds fine-grained physical shards offline. At query time it combines selected physical shards into logical shards and processes the resulting local tasks sequentially on one GPU. The simple_partition and vora_lsqb_simple_partitioned executables implement the edge-based simple-partition baseline used for comparison in the paper; they are not VORA's default execution path.

The main execution options include --query, --data, and --device. For example, a hash-partitioned execution has this general form:

build/vora_lsqb \
  --query 1-2-3 \
  --data /path/to/partitioned-data \
  --device 0

scripts/run_binary.sh automates LSQB experiment runs. Its arguments are <scale-factor-list> <query-list> <gpu-id> <number-of-runs> <output-suffix>. For example, 1-3 selects scale factors 1 and 3; 1-2-3 selects LSQB Q1–Q3.

Portable experiment-script paths

The relation-building and experiment scripts do not assume a particular home directory or storage device. Configure external data locations with environment variables:

Variable Purpose Default
VORA_BUILD_DIR Directory containing VORA executables build/ under the repository root
VORA_LOG_DIR Directory for logs and collected results output/ under the repository root
VORA_DATA_ROOT Preprocessed and partitioned VORA data Required
LSQB_DATA_ROOT Directory containing social-network-sf*-projected-fk Required for CSV input
COMMAND Override the executable selected by a script Script-specific build target
VORA_NO_FUSION_BUILD_DIR Operator-fusion-disabled build directory build-no-fusion/ under the repository root

For repository-local defaults, source the provided environment helper:

source scripts/import-vars.sh

It assigns build/, build-no-fusion/, output/, data/vora/, and data/lsqb/ under the repository root. Existing variable values take precedence, so machine-specific storage roots can be exported before sourcing the helper without recording those paths in Git.

For example:

export VORA_BUILD_DIR="$PWD/build"
export VORA_LOG_DIR="$PWD/output"
export LSQB_DATA_ROOT=/path/to/lsqb/data
export VORA_DATA_ROOT=/path/to/vora-data

# Build hash-partitioned binary relations from LSQB CSV input.
OUTPUT_FILE_FORMAT=binary \
  scripts/build_relations.sh 1 0 hash csv ""

# Run Q1 once against the generated binary relations.
scripts/run_binary.sh 1 1 0 1 example

scripts/run_binary.sh expects the _binary hash-relation suffix by default. Set HASH_RELATION_SUFFIX when using another relation-directory naming scheme. Users may place the two data roots on any suitable storage volume.

The included fixture also demonstrates the projected-foreign-key CSV layout expected by preprocess.

Operator stack

VORA is organized into three conceptual operator layers:

  1. Vector operators perform backend-aware operations over vectors and spans, including selection, sorting, scans, masking, and arithmetic. They are primarily implemented by VectorOp in utils/array_like/operators.hpp.
  2. Graph primitives provide batched operations over the CUS graph representation, including degree lookup, neighbor retrieval, and edge-existence testing. Examples include getDegreeBatched, getNeighborsBatched, and checkEdgeExistenceBatched in data_graph/cus.cuh.
  3. Subgraph-query operators manipulate intermediate match tables using operations such as expand_, path_expand_, optional_expand_, expand_into_, and anti_expand_into_. They are implemented by TreeTable in execution/tree_table.hpp.
Query execution and matching-order orchestration
                         ↓
              Subgraph-query operators
                         ↓
                  Graph primitives
                         ↓
                  Vector operators

execution/match.hpp and execution/lsqb_match.hpp assemble these operators according to a query's matching order. These layers describe VORA's internal architecture.

Paper notation and implementation names

The paper uses op and vop as conceptual prefixes for eager and lazy VectorFlux operators. In the implementation, op corresponds to vora::VectorOp, commonly shortened to the vo namespace alias, while vop corresponds to vora::VectorOp_phantom, exposed at selected call sites through aliases such as vo_phantom1.

Paper notation Implementation Behavior
op VectorOp / vo Eager operator that materializes its result
vop VectorOp_phantom / vo_phantom* Lazy operator that returns a fusible iterator-backed view

When operator fusion is disabled, the vo_phantom* aliases resolve to vo, so the same call sites use materializing operators instead.

Repository layout

./
├── src/
│   ├── vora/                     Shared Vora engine
│   │   ├── config.h              Build/runtime configuration state and defaults
│   │   ├── config_cli.hpp        Command-line configuration setup and reporting
│   │   ├── lsqb_data_pipeline.hpp  LSQB preprocessing and relation data pipeline
│   │   ├── data_graph/           Graph loading and representations
│   │   ├── execution/            Matching operators and execution engine
│   │   ├── partition/            Hash-based partitioning and optimization
│   │   ├── query_graph/          Query representation and matching orders
│   │   └── utils/                Memory, vector, and general utilities
│   └── vora_simple_partition/    Simple-partition baseline module
├── third_party/                  Vendored header-only dependencies
├── data/lsqb/                    Redistributable LSQB fixture and its license
├── docs/assets/                  README figures
├── scripts/                      Experiment and data-preparation scripts
└── test/                         Unit and I/O tests

Citation

See CITATION.cff for the preferred citation for VORA and its accepted ICDE 2027 paper.

Third-party notices

Vendored dependency notices and licenses are listed in THIRD_PARTY_NOTICES.md, including cereal and rapidcsv.

About

A scalable single-GPU engine for subgraph query processing on large labeled graphs, featuring vectorized execution and workload decomposition.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages