Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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
5 changes: 5 additions & 0 deletions ci/build_cpp.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,15 @@ export RAPIDS_ARTIFACTS_DIR
# populates `RATTLER_CHANNELS` array and `RATTLER_ARGS` array
source rapids-rattler-channel-string

# Use sed to replace the RAPIDS_CUDA_VERSION string in the variants file with
# the value of that variable in this shell.
sed -i "s/RAPIDS_CUDA_VERSION/${RAPIDS_CUDA_VERSION}/g" "conda/recipes/libkvikio/variants_$(arch).yaml"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing that has worked well before, is to output this to a variant file

It does a similar thing admittedly. Though it can be a bit clearer about what is happening. That could just be personal preference though

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like that alternative! I agree it is clearer.

@vyasr vyasr Jun 13, 2025

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I considered this possibility as well. However, in this instance it's not quite so simple as the pynvjitlink example because we need other things to be encoded in the variants as well. The behavior on x86 and arm is different because the former is a single variant while the latter requires two, so we would need to write out variants conditionally based on that. Moreover, for arm we need to use a different compiler version for each CUDA version, so it's a longer file. If you both prefer that I can switch to it, we just wind up with two fairly long here-docs instead of this command. The flip side is that we can drop the committed variants files if we choose to go that route.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've made the changes in 00b642a. If you like it we can stick with it, otherwise feel free to revert that commit.


Comment on lines +28 to +58

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking, but I think this would be better to encode in individual variant_$ARCH_$CUDA_VERSION.yaml files and then the branching logic here can be to select the appropriate variant file for the build at hand.

That way the various build variants are all in the conda recipe file and not hidden away in this bash script.

I'm fine with this being done in a follow-up to get CI unblocked.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lol, I see that was your original approach. well, not worth going back and forth over it.

My general take is "repeat yourself" if it keeps things clean, and heredocs in a random bash script isn't worth the tradeoff to me.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I liked the old way better too. This version feels wrong in how much of the logic is moved out of the recipe itself. I think we should reconsider what approach we like best, but I'll merge as-is to get things unblocked for now.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Provided a simplification on the current approach in PR: #755

# --no-build-id allows for caching with `sccache`
# more info is available at
# https://rattler.build/latest/tips_and_tricks/#using-sccache-or-ccache-with-rattler-build
rattler-build build --recipe conda/recipes/libkvikio \
--variant-config "conda/recipes/libkvikio/variants_$(arch).yaml" \
"${RATTLER_ARGS[@]}" \
"${RATTLER_CHANNELS[@]}"

Expand Down
6 changes: 0 additions & 6 deletions conda/recipes/libkvikio/conda_build_config.yaml
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
c_compiler_version:
- 13

cxx_compiler_version:
- 13

cmake_version:
- ">=3.30.4"

Expand Down
54 changes: 40 additions & 14 deletions conda/recipes/libkvikio/recipe.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,18 @@ schema_version: 1
context:
version: ${{ env.get("RAPIDS_PACKAGE_VERSION") }}
minor_version: ${{ (version | split("."))[:2] | join(".") }}
cuda_version: ${{ (env.get("RAPIDS_CUDA_VERSION") | split("."))[:2] | join(".") }}
cuda_major: '${{ (env.get("RAPIDS_CUDA_VERSION") | split("."))[0] }}'
# We need to support three cases:
# 1. Linux x86_64, which always uses libcufile
# 2. Linux aarch64 with CUDA >= 12.2, which uses libcufile
# 3. Linux aarch64 with CUDA < 12.2, which does not use libcufile
# Each case has different cuda-version constraints as expressed below
should_use_cufile: ${{ x86_64 or (aarch64 and cuda_version >= "12.2") }}
Comment thread
vyasr marked this conversation as resolved.
# When reverting, instances of cuda_key_string can be replaced with cuda_major
cuda_key_string: ${{ cuda_version | replace(".", "_") }}
#cuda_version: ${{ (env.get("RAPIDS_CUDA_VERSION") | split("."))[:2] | join(".") }}
#cuda_major: '${{ (env.get("RAPIDS_CUDA_VERSION") | split("."))[0] }}'
Comment on lines +15 to +16

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we drop these?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I chose to keep them to simplify reverting the changes, but if others feel like it would be cleaner to drop these I am fine with that.

date_string: '${{ env.get("RAPIDS_DATE_STRING") }}'
head_rev: '${{ git.head_rev(".")[:8] }}'
linux64: ${{ linux and x86_64 }}

recipe:
name: libkvikio-split
Expand Down Expand Up @@ -46,7 +53,7 @@ cache:
SCCACHE_REGION: ${{ env.get("SCCACHE_REGION") }}
SCCACHE_S3_USE_SSL: ${{ env.get("SCCACHE_S3_USE_SSL") }}
SCCACHE_S3_NO_CREDENTIALS: ${{ env.get("SCCACHE_S3_NO_CREDENTIALS") }}
SCCACHE_S3_KEY_PREFIX: libkvikio/${{ env.get("RAPIDS_CONDA_ARCH") }}/cuda${{ cuda_major }}
SCCACHE_S3_KEY_PREFIX: libkvikio/${{ env.get("RAPIDS_CONDA_ARCH") }}/cuda${{ cuda_key_string }}
requirements:
build:
- ${{ compiler("c") }}
Expand All @@ -59,7 +66,7 @@ cache:
host:
- cuda-version =${{ cuda_version }}
- libcurl ${{ libcurl_version }}
- if: (linux and x86_64) or (linux and aarch64 and cuda_version >= "12.2")
- if: should_use_cufile
then:
- libcufile-dev
- libnuma
Expand All @@ -72,7 +79,7 @@ outputs:
script:
content: |
cmake --install cpp/build
string: cuda${{ cuda_major }}_${{ date_string }}_${{ head_rev }}
string: cuda${{ cuda_key_string }}_${{ date_string }}_${{ head_rev }}
dynamic_linking:
overlinking_behavior: "error"
prefix_detection:
Expand All @@ -86,15 +93,25 @@ outputs:
- cuda-version =${{ cuda_version }}
- libcurl ${{ libcurl_version }}
run:
- ${{ pin_compatible("cuda-version", upper_bound="x", lower_bound="x") }}
- if: linux and x86_64
- if: x86_64
then:
- ${{ pin_compatible("cuda-version", upper_bound="x", lower_bound="x") }}
else:
- if: aarch64 and cuda_version >= "12.2"
then:
- ${{ pin_compatible("cuda-version", upper_bound="x", lower_bound="12.2") }}
Comment thread
vyasr marked this conversation as resolved.
Outdated
else:
- ${{ pin_compatible("cuda-version", upper_bound="12.2.0a0", lower_bound="12.0") }}
- if: should_use_cufile
then:
- libcufile-dev
ignore_run_exports:
by_name:
- cuda-version
- libcufile
- libcurl
- if: should_use_cufile
then:
- libcufile
tests:
- script:
- test -f $PREFIX/include/kvikio/file_handle.hpp
Expand All @@ -107,7 +124,7 @@ outputs:
name: libkvikio-tests
version: ${{ version }}
build:
string: cuda${{ cuda_major }}_${{ date_string }}_${{ head_rev }}
string: cuda${{ cuda_key_string }}_${{ date_string }}_${{ head_rev }}
dynamic_linking:
overlinking_behavior: "error"
script:
Expand All @@ -121,20 +138,29 @@ outputs:
- ${{ pin_subpackage("libkvikio", exact=True) }}
- cuda-version =${{ cuda_version }}
- cuda-cudart-dev
- if: linux and x86_64
- if: should_use_cufile
then:
- libcufile-dev
run:
- ${{ pin_compatible("cuda-version", upper_bound="x", lower_bound="x") }}
- if: x86_64
then:
- ${{ pin_compatible("cuda-version", upper_bound="x", lower_bound="x") }}
else:
- if: aarch64 and cuda_version >= "12.2"
then:
- ${{ pin_compatible("cuda-version", upper_bound="x", lower_bound="12.2") }}
Comment thread
vyasr marked this conversation as resolved.
Outdated
else:
- ${{ pin_compatible("cuda-version", upper_bound="12.2.0a0", lower_bound="12.0") }}
- cuda-cudart
- libcufile
ignore_run_exports:
by_name:
- cuda-cudart
- cuda-version
- libcufile
- libcurl
- libnuma
- if: should_use_cufile
then:
- libcufile
about:
homepage: ${{ load_from_file("python/libkvikio/pyproject.toml").project.urls.Homepage }}
license: ${{ load_from_file("python/libkvikio/pyproject.toml").project.license.text }}
Expand Down
18 changes: 18 additions & 0 deletions conda/recipes/libkvikio/variants_aarch64.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
zip_keys:
- [c_compiler_version, cxx_compiler_version, cuda_version, should_use_cufile]

c_compiler_version:
- 12
- 13

cxx_compiler_version:
- 12
- 13

cuda_version:
- 12.1 # The last version to not support cufile
- RAPIDS_CUDA_VERSION

should_use_cufile:
- false
- true
Comment thread
vyasr marked this conversation as resolved.
Outdated
11 changes: 11 additions & 0 deletions conda/recipes/libkvikio/variants_x86_64.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
c_compiler_version:
- 13

cxx_compiler_version:
- 13

cuda_version:
- RAPIDS_CUDA_VERSION

should_use_cufile:
- true
4 changes: 0 additions & 4 deletions cpp/include/kvikio/shim/cufile.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,11 +128,7 @@ bool is_cufile_available() noexcept;
*
* @return The version (1000*major + 10*minor) or zero if older than 1080.
*/
#ifdef KVIKIO_CUFILE_FOUND
int cufile_version() noexcept;
#else
constexpr int cufile_version() noexcept { return 0; }
#endif
Comment on lines -133 to -135

@vyasr vyasr Jun 13, 2025

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is necessary because the dead code optimizer will generally elide constexpr functions from the final binary if no runtime usage is detected, but we need the symbol in the DSO for usage from other libraries (including Python extensions). For the same reason we also avoid inlining the definition here.


/**
* @brief Check if cuFile's batch API is available.
Expand Down
2 changes: 2 additions & 0 deletions cpp/src/shim/cufile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ int cufile_version() noexcept
return 0;
}
}
#else
int cufile_version() noexcept { return 0; }
#endif

bool is_batch_api_available() noexcept { return cufile_version() >= 1060; }
Expand Down
86 changes: 49 additions & 37 deletions python/kvikio/tests/test_cufile_driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,47 +13,59 @@ def test_version():


@pytest.mark.cufile
def test_open_and_close():
kvikio.cufile_driver.driver_open()
kvikio.cufile_driver.driver_close()
def test_open_and_close(request):
try:
kvikio.cufile_driver.driver_open()
kvikio.cufile_driver.driver_close()
except RuntimeError as e:
if "KvikIO not compiled with cuFile.h" in str(e):
pytest.skip("KvikIO not compiled with cuFile.h, skipping cuFile tests")


@pytest.mark.cufile
def test_property_accessor():
"""Test the method `get` and `set`"""

# Attempt to set a nonexistent property
with pytest.raises(KeyError):
kvikio.cufile_driver.set("nonexistent_property", 123)

# Attempt to get a nonexistent property
with pytest.raises(KeyError):
kvikio.cufile_driver.get("nonexistent_property")

# Attempt to set a read-only property
with pytest.raises(KeyError, match="read-only"):
kvikio.cufile_driver.set("major_version", 2077)

# Nested context managers
poll_thresh_size_default = kvikio.cufile_driver.get("poll_thresh_size")
with kvikio.cufile_driver.set("poll_thresh_size", 1024):
assert kvikio.cufile_driver.get("poll_thresh_size") == 1024
with kvikio.cufile_driver.set("poll_thresh_size", 2048):
assert kvikio.cufile_driver.get("poll_thresh_size") == 2048
with kvikio.cufile_driver.set("poll_thresh_size", 4096):
assert kvikio.cufile_driver.get("poll_thresh_size") == 4096
assert kvikio.cufile_driver.get("poll_thresh_size") == 2048
assert kvikio.cufile_driver.get("poll_thresh_size") == 1024
assert kvikio.cufile_driver.get("poll_thresh_size") == poll_thresh_size_default

# Multiple context managers
poll_mode_default = kvikio.cufile_driver.get("poll_mode")
max_device_cache_size_default = kvikio.cufile_driver.get("max_device_cache_size")
with kvikio.cufile_driver.set({"poll_mode": True, "max_device_cache_size": 2048}):
assert kvikio.cufile_driver.get("poll_mode") and (
kvikio.cufile_driver.get("max_device_cache_size") == 2048
try:
# Attempt to set a nonexistent property
with pytest.raises(KeyError):
kvikio.cufile_driver.set("nonexistent_property", 123)

# Attempt to get a nonexistent property
with pytest.raises(KeyError):
kvikio.cufile_driver.get("nonexistent_property")

# Attempt to set a read-only property
with pytest.raises(KeyError, match="read-only"):
kvikio.cufile_driver.set("major_version", 2077)

# Nested context managers
poll_thresh_size_default = kvikio.cufile_driver.get("poll_thresh_size")
with kvikio.cufile_driver.set("poll_thresh_size", 1024):
assert kvikio.cufile_driver.get("poll_thresh_size") == 1024
with kvikio.cufile_driver.set("poll_thresh_size", 2048):
assert kvikio.cufile_driver.get("poll_thresh_size") == 2048
with kvikio.cufile_driver.set("poll_thresh_size", 4096):
assert kvikio.cufile_driver.get("poll_thresh_size") == 4096
assert kvikio.cufile_driver.get("poll_thresh_size") == 2048
assert kvikio.cufile_driver.get("poll_thresh_size") == 1024
assert kvikio.cufile_driver.get("poll_thresh_size") == poll_thresh_size_default

# Multiple context managers
poll_mode_default = kvikio.cufile_driver.get("poll_mode")
max_device_cache_size_default = kvikio.cufile_driver.get(
"max_device_cache_size"
)
with kvikio.cufile_driver.set(
{"poll_mode": True, "max_device_cache_size": 2048}
):
assert kvikio.cufile_driver.get("poll_mode") and (
kvikio.cufile_driver.get("max_device_cache_size") == 2048
)
assert (kvikio.cufile_driver.get("poll_mode") == poll_mode_default) and (
kvikio.cufile_driver.get("max_device_cache_size")
== max_device_cache_size_default
)
assert (kvikio.cufile_driver.get("poll_mode") == poll_mode_default) and (
kvikio.cufile_driver.get("max_device_cache_size")
== max_device_cache_size_default
)
except RuntimeError as e:
if "KvikIO not compiled with cuFile.h" in str(e):
pytest.skip("KvikIO not compiled with cuFile.h, skipping cuFile tests")