Skip to content

Commit 134bc9d

Browse files
committed
refactor[cuda]: scope direct I/O to pooled reads
Signed-off-by: Onur Satici <onur@spiraldb.com>
1 parent 0a788c7 commit 134bc9d

16 files changed

Lines changed: 189 additions & 797 deletions

File tree

‎.github/workflows/sql-benchmarks.yml‎

Lines changed: 3 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -219,8 +219,6 @@ on:
219219
"id": "fineweb",
220220
"subcommand": "fineweb",
221221
"name": "FineWeb NVMe",
222-
"cold_io_query": "7",
223-
"cold_io_samples": "5",
224222
"data_formats": ["parquet", "vortex", "vortex-compact"],
225223
"pr_targets": [
226224
{"engine": "datafusion", "format": "parquet"},
@@ -472,8 +470,6 @@ on:
472470
"id": "fineweb",
473471
"subcommand": "fineweb",
474472
"name": "FineWeb NVMe",
475-
"cold_io_query": "7",
476-
"cold_io_samples": "5",
477473
"data_formats": ["parquet", "vortex"],
478474
"pr_targets": [
479475
{"engine": "datafusion", "format": "parquet"},
@@ -613,24 +609,6 @@ jobs:
613609
--formats-json '${{ toJSON(matrix.data_formats) }}' \
614610
${{ matrix.scale_factor && format('--opt scale-factor={0}', matrix.scale_factor) || '' }}
615611
616-
- name: Place cold benchmark data on ephemeral storage
617-
if: inputs.mode == 'pr' && matrix.cold_io_query != null
618-
shell: bash
619-
run: |
620-
set -Eeuo pipefail
621-
622-
if [ ! -d /mnt/ephemeral ]; then
623-
echo "::warning::/mnt/ephemeral is unavailable; using the workspace filesystem"
624-
exit 0
625-
fi
626-
627-
source_dir="vortex-bench/data/${{ matrix.subcommand }}"
628-
data_root="/mnt/ephemeral/vortex-bench-data-${{ github.run_id }}"
629-
data_dir="${data_root}/${{ matrix.subcommand }}"
630-
sudo install -d -o "$(id -u)" -g "$(id -g)" "$data_root"
631-
mv "$source_dir" "$data_dir"
632-
ln -s "$data_dir" "$source_dir"
633-
634612
- name: Setup AWS CLI
635613
if: inputs.mode != 'pr' || github.event.pull_request.head.repo.fork == false
636614
uses: aws-actions/configure-aws-credentials@e7f100cf4c008499ea8adda475de1042d6975c7b # v6
@@ -660,9 +638,7 @@ jobs:
660638
extra_args: "--off-cpu-threshold=0.03" # Personally tuned by @brancz
661639

662640
- name: Run ${{ matrix.name }} benchmark
663-
if: >-
664-
(matrix.remote_storage == null || github.event.pull_request.head.repo.fork == true)
665-
&& (inputs.mode != 'pr' || matrix.cold_io_query == null)
641+
if: matrix.remote_storage == null || github.event.pull_request.head.repo.fork == true
666642
shell: bash
667643
env:
668644
OTEL_SERVICE_NAME: "vortex-bench"
@@ -680,80 +656,6 @@ jobs:
680656
${{ matrix.iterations && format('--iterations {0}', matrix.iterations) || '' }} \
681657
${{ matrix.scale_factor && format('--opt scale-factor={0}', matrix.scale_factor) || '' }}
682658
683-
- name: Run cold buffered vs direct ${{ matrix.name }} benchmark
684-
if: inputs.mode == 'pr' && matrix.cold_io_query != null
685-
shell: bash
686-
env:
687-
OTEL_SERVICE_NAME: "vortex-bench"
688-
OTEL_EXPORTER_OTLP_PROTOCOL: "http/protobuf"
689-
OTEL_EXPORTER_OTLP_ENDPOINT: "${{ github.event.pull_request.head.repo.fork == false && secrets.OTEL_EXPORTER_OTLP_ENDPOINT || '' }}"
690-
OTEL_EXPORTER_OTLP_HEADERS: "${{ github.event.pull_request.head.repo.fork == false && secrets.OTEL_EXPORTER_OTLP_HEADERS || '' }}"
691-
OTEL_RESOURCE_ATTRIBUTES: "bench-name=${{ matrix.id }}-cold-io"
692-
run: |
693-
set -Eeuo pipefail
694-
695-
results_dir="cold-io-results"
696-
mkdir -p "$results_dir"
697-
698-
data_file="$(find \
699-
"vortex-bench/data/${{ matrix.subcommand }}/vortex-file-compressed" \
700-
-type f -name '*.vortex' -print -quit)"
701-
if [ -z "$data_file" ]; then
702-
echo "No Vortex data file found for the cold I/O benchmark" >&2
703-
exit 1
704-
fi
705-
706-
{
707-
echo "Data file: $data_file"
708-
findmnt -T "$data_file"
709-
lsblk -o NAME,TYPE,SIZE,FSTYPE,MOUNTPOINTS
710-
} | tee "$results_dir/filesystem.txt"
711-
712-
sudo -n true
713-
714-
run_cold() {
715-
local mode="$1"
716-
local direct_io="$2"
717-
local sample="$3"
718-
local output="$results_dir/${mode}-${sample}.jsonl"
719-
local log="$results_dir/${mode}-${sample}.log"
720-
721-
sudo sh -c 'sync; echo 3 > /proc/sys/vm/drop_caches'
722-
if command -v fincore >/dev/null 2>&1; then
723-
fincore "$data_file" | tee -a "$log"
724-
fi
725-
726-
VORTEX_DIRECT_IO="$direct_io" \
727-
bash scripts/bench-taskset.sh \
728-
target/release_debug/datafusion-bench "${{ matrix.subcommand }}" \
729-
--display-format gh-json \
730-
--iterations 1 \
731-
--hide-progress-bar \
732-
--formats vortex \
733-
--queries "${{ matrix.cold_io_query }}" \
734-
--runner "ec2_${{ inputs.machine_type }}" \
735-
--show-metrics \
736-
-o "$output" 2>&1 | tee -a "$log"
737-
}
738-
739-
for sample in $(seq 1 "${{ matrix.cold_io_samples }}"); do
740-
if (( sample % 2 == 1 )); then
741-
run_cold buffered 0 "$sample"
742-
run_cold direct 1 "$sample"
743-
else
744-
run_cold direct 1 "$sample"
745-
run_cold buffered 0 "$sample"
746-
fi
747-
done
748-
749-
python3 scripts/summarize-cold-io.py \
750-
"$results_dir" \
751-
--query "${{ matrix.cold_io_query }}" \
752-
--filesystem "$results_dir/filesystem.txt" \
753-
> comment.md
754-
cp comment.md "$results_dir/comment.md"
755-
cat comment.md >> "$GITHUB_STEP_SUMMARY"
756-
757659
- name: Run ${{ matrix.name }} benchmark (remote)
758660
if: matrix.remote_storage != null && (inputs.mode != 'pr' || github.event.pull_request.head.repo.fork == false)
759661
shell: bash
@@ -776,9 +678,7 @@ jobs:
776678
${{ matrix.scale_factor && format('--opt scale-factor={0}', matrix.scale_factor) || '' }}
777679
778680
- name: Capture file sizes
779-
if: >-
780-
matrix.remote_storage == null
781-
&& (inputs.mode != 'pr' || matrix.cold_io_query == null)
681+
if: matrix.remote_storage == null
782682
shell: bash
783683
run: |
784684
uv run --no-project scripts/capture-file-sizes.py \
@@ -789,7 +689,7 @@ jobs:
789689
cat sizes.json >> results.json
790690
791691
- name: Compare results
792-
if: inputs.mode == 'pr' && matrix.cold_io_query == null
692+
if: inputs.mode == 'pr'
793693
shell: bash
794694
run: |
795695
set -Eeu -o pipefail -x
@@ -801,13 +701,6 @@ jobs:
801701
> comment.md
802702
cat comment.md >> "$GITHUB_STEP_SUMMARY"
803703
804-
- name: Upload cold I/O results
805-
if: always() && inputs.mode == 'pr' && matrix.cold_io_query != null
806-
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
807-
with:
808-
name: cold-io-${{ matrix.id }}
809-
path: cold-io-results
810-
811704
- name: Comment PR
812705
if: inputs.mode == 'pr' && github.event.pull_request.head.repo.fork == false
813706
uses: thollander/actions-comment-pull-request@24bffb9b452ba05a4f3f77933840a6a841d1b32b # v3

‎Cargo.lock‎

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎benchmarks/datafusion-bench/src/lib.rs‎

Lines changed: 4 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ use datafusion::datasource::file_format::FileFormat;
1010
use datafusion::datasource::file_format::arrow::ArrowFormat;
1111
use datafusion::datasource::file_format::csv::CsvFormat;
1212
use datafusion::datasource::file_format::parquet::ParquetFormat;
13-
#[cfg(target_os = "linux")]
14-
use datafusion::datasource::listing::PartitionedFile;
1513
use datafusion::datasource::provider::DefaultTableFactory;
1614
use datafusion::execution::SessionStateBuilder;
1715
use datafusion::execution::cache::DefaultListFilesCache;
@@ -21,28 +19,16 @@ use datafusion::execution::runtime_env::RuntimeEnvBuilder;
2119
use datafusion::prelude::SessionConfig;
2220
use datafusion::prelude::SessionContext;
2321
use datafusion_common::GetExt;
24-
#[cfg(target_os = "linux")]
25-
use datafusion_common::Result as DFResult;
2622
use object_store::ObjectStore;
2723
use object_store::aws::AmazonS3Builder;
2824
use object_store::gcp::GoogleCloudStorageBuilder;
2925
use object_store::local::LocalFileSystem;
3026
use url::Url;
31-
#[cfg(target_os = "linux")]
32-
use vortex::array::memory::MemorySessionExt;
33-
#[cfg(target_os = "linux")]
34-
use vortex::io::VortexReadAt;
35-
#[cfg(target_os = "linux")]
36-
use vortex::io::session::RuntimeSessionExt;
37-
#[cfg(target_os = "linux")]
38-
use vortex::io::std_file::FileReadAt;
3927
use vortex_bench::Format;
4028
use vortex_bench::SESSION;
4129
use vortex_datafusion::VortexFormat;
4230
use vortex_datafusion::VortexFormatFactory;
4331
use vortex_datafusion::VortexTableOptions;
44-
#[cfg(target_os = "linux")]
45-
use vortex_datafusion::reader::VortexReaderFactory;
4632

4733
#[expect(clippy::expect_used)]
4834
pub fn get_session_context() -> SessionContext {
@@ -120,52 +106,20 @@ pub fn make_object_store(
120106
}
121107
}
122108

123-
pub fn format_to_df_format(format: Format, source: &Url) -> Arc<dyn FileFormat> {
109+
pub fn format_to_df_format(format: Format) -> Arc<dyn FileFormat> {
124110
match format {
125111
Format::Csv => Arc::new(CsvFormat::default()) as _,
126112
Format::Arrow => Arc::new(ArrowFormat),
127113
Format::Parquet => Arc::new(ParquetFormat::new()),
128-
Format::OnDiskVortex | Format::VortexCompact | Format::VortexNative => {
129-
let format = VortexFormat::new_with_options(SESSION.clone(), vortex_table_options());
130-
#[cfg(target_os = "linux")]
131-
let format = if source.scheme() == "file"
132-
&& std::env::var("VORTEX_DIRECT_IO").is_ok_and(|value| value == "1")
133-
{
134-
format.with_vortex_reader_factory(Arc::new(DirectIoVortexReaderFactory))
135-
} else {
136-
format
137-
};
138-
#[cfg(not(target_os = "linux"))]
139-
let _ = source;
140-
Arc::new(format)
141-
}
114+
Format::OnDiskVortex | Format::VortexCompact | Format::VortexNative => Arc::new(
115+
VortexFormat::new_with_options(SESSION.clone(), vortex_table_options()),
116+
),
142117
Format::OnDiskDuckDB | Format::Lance => {
143118
unimplemented!("Format {format} cannot be turned into a DataFusion `FileFormat`")
144119
}
145120
}
146121
}
147122

148-
#[cfg(target_os = "linux")]
149-
#[derive(Debug)]
150-
struct DirectIoVortexReaderFactory;
151-
152-
#[cfg(target_os = "linux")]
153-
impl VortexReaderFactory for DirectIoVortexReaderFactory {
154-
fn create_reader(
155-
&self,
156-
file: &PartitionedFile,
157-
session: &vortex::session::VortexSession,
158-
) -> DFResult<Arc<dyn VortexReadAt>> {
159-
let path = LocalFileSystem::default()
160-
.path_to_filesystem(file.path())
161-
.map_err(|error| datafusion_common::DataFusionError::External(Box::new(error)))?;
162-
let reader =
163-
FileReadAt::open_direct_with_allocator(path, session.handle(), session.allocator())
164-
.map_err(|error| datafusion_common::DataFusionError::External(Box::new(error)))?;
165-
Ok(Arc::new(reader))
166-
}
167-
}
168-
169123
fn vortex_table_options() -> VortexTableOptions {
170124
let mut opts = VortexTableOptions::default();
171125

‎benchmarks/datafusion-bench/src/main.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ async fn register_benchmark_tables<B: Benchmark + ?Sized>(
263263
}
264264
_ => {
265265
let benchmark_base = benchmark.data_url().join(&format!("{}/", format.name()))?;
266-
let file_format = format_to_df_format(format, benchmark.data_url());
266+
let file_format = format_to_df_format(format);
267267

268268
for table in benchmark.table_specs().iter() {
269269
let pattern = benchmark.pattern(table.name, format);

0 commit comments

Comments
 (0)