Skip to content

Fix DLPack byte_offset handling on import#1573

Open
fallintoplace wants to merge 2 commits into
NVIDIA:mainfrom
fallintoplace:fix/dlpack-byte-offset-import
Open

Fix DLPack byte_offset handling on import#1573
fallintoplace wants to merge 2 commits into
NVIDIA:mainfrom
fallintoplace:fix/dlpack-byte-offset-import

Conversation

@fallintoplace

@fallintoplace fallintoplace commented Jun 21, 2026

Copy link
Copy Markdown

Description

DLTensor.byte_offset is part of the logical address of element zero in the DLPack tensor: consumers should read from data + byte_offset. Warp imported DLPack arrays using dlt.data directly, so a valid producer that keeps data at the allocation base and describes a view with byte_offset would be imported at the wrong address.

I found this while checking Warp's DLPack import path against the DLPack tensor layout. I have not seen this from a production failure. The new regression test builds a small valid CPU DLPack capsule with a nonzero byte_offset and shows that the imported Warp array should read and write the logical view, not the allocation base.

Changes

  • Apply DLTensor.byte_offset when constructing the imported Warp array pointer.
  • Add a focused DLPack import regression test that verifies both reads and writes through the imported array use the offset view.
  • Add a short changelog entry for the wp.from_dlpack() fix.

Checklist

  • I am familiar with the Contributing Guidelines.
  • New or existing tests cover these changes.
  • The documentation is up to date with these changes.
  • CHANGELOG.md is updated for any user-facing changes under the Unreleased section.

Validation summary

I verified the change locally on macOS arm64 with a CPU-only Warp build:

  • uv run build_lib.py --quick
  • uv run warp/tests/interop/test_dlpack.py -k test_dlpack_from_dlpack_byte_offset
  • uv run warp/tests/interop/test_dlpack.py
  • uvx pre-commit run --files CHANGELOG.md warp/_src/dlpack.py warp/tests/interop/test_dlpack.py
  • git diff --check

The local run does not have Torch, JAX, or Paddle installed, so those optional DLPack interop tests were skipped. The new regression test does not depend on those packages.

Bug fix

The added test is the minimal repro: it creates a DLPack tensor whose data points to the start of an int32 NumPy allocation while byte_offset points to element 3. wp.from_dlpack() should import the logical three-element view [3, 4, 5] and writes through the Warp array should update only that slice.

arr = wp.from_dlpack(capsule_with_data_base_and_nonzero_byte_offset)
assert arr.ptr == backing.ctypes.data + byte_offset

New feature / enhancement

Not applicable.

Summary by CodeRabbit

  • Bug Fixes
    • Fixed DLPack tensor import to correctly respect DLTensor.byte_offset when converting external (zero-copy) DLPack tensors into Warp arrays.
  • Tests
    • Added coverage for byte-offset handling in DLPack interop, including validation that writes through the imported array update the expected memory region.

Signed-off-by: Minh Vu <vuhoangminh97@gmail.com>
@copy-pr-bot

copy-pr-bot Bot commented Jun 21, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Enterprise

Run ID: 5fc89627-21eb-4144-92cb-7747efb7bd97

📥 Commits

Reviewing files that changed from the base of the PR and between 59d0dad and d7f098c.

📒 Files selected for processing (1)
  • warp/tests/interop/test_dlpack.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • warp/tests/interop/test_dlpack.py

📝 Walkthrough

Walkthrough

_unpack_array in warp/_src/dlpack.py is updated to compute the array pointer as dlt.data + dlt.byte_offset (or None when dlt.data is None) instead of passing dlt.data directly. A new test manually constructs a DLManagedTensor capsule with a non-zero byte_offset, converts it via wp.from_dlpack, and verifies the resulting pointer and backing-memory writes. A changelog entry records the fix.

Changes

DLPack byte_offset fix

Layer / File(s) Summary
byte_offset pointer fix in _unpack_array
warp/_src/dlpack.py, CHANGELOG.md
_unpack_array now sets ptr = dlt.data + dlt.byte_offset (guarded for None) before passing it to the array constructor. Changelog entry added under "Fixed".
byte_offset test: imports, destructor, test body, registration
warp/tests/interop/test_dlpack.py
Imports DLDataTypeCode, DLDeviceType, DLManagedTensor, and _c_str_dltensor; defines _free_dlpack_managed_tensor ctypes destructor and PyCapsule_New bindings; adds test_dlpack_from_dlpack_byte_offset which builds a raw DLManagedTensor capsule with non-zero byte_offset, calls wp.from_dlpack, and asserts the Warp array pointer and backing NumPy array writes are correct; registers the test with devices=None.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Fix DLPack byte_offset handling on import' directly and clearly describes the main change: fixing how the byte_offset field is handled during DLPack tensor import, which is the core fix across all modified files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps

greptile-apps Bot commented Jun 21, 2026

Copy link
Copy Markdown

Greptile Summary

This PR fixes wp.from_dlpack() to correctly apply DLTensor.byte_offset when constructing the imported Warp array pointer, so that consumers that set data to the allocation base and describe a logical view via byte_offset are imported at the right address rather than the start of the allocation.

  • Core fix (warp/_src/dlpack.py): Adds ptr = dlt.data + dlt.byte_offset if dlt.data is not None else None before constructing the Warp array, replacing the previous bare dlt.data usage.
  • Regression test (warp/tests/interop/test_dlpack.py): Manually builds a valid DLManagedTensor capsule with a nonzero byte_offset and asserts that the imported array's pointer, read values, and write-through all reflect the logical offset view. Padding in the test uses the correct (-managed_tensor_size) & 7 formula.
  • Changelog entry added under the Unreleased / Fixed section.

Confidence Score: 5/5

Safe to merge — the change is a minimal, spec-correct one-liner in the import path with a targeted regression test that validates pointer address, read values, and write-through behavior.

The fix is two lines, directly implements what the DLPack spec requires (element 0 is at data + byte_offset), and is guarded for the null-data case. The new test exercises the full round-trip — pointer identity, NumPy read-back, and Warp-to-backing write — on a manually constructed capsule. No pre-existing behavior is altered for the common case where byte_offset is zero.

No files require special attention.

Important Files Changed

Filename Overview
warp/_src/dlpack.py Two-line targeted fix: computes ptr = dlt.data + dlt.byte_offset (guarded for null data) and passes ptr to the array constructor. Correctly follows DLPack spec semantics.
warp/tests/interop/test_dlpack.py Adds a focused regression test that manually constructs a DLManagedTensor capsule with nonzero byte_offset and verifies pointer address, read values, and write-through behavior. Padding formula is correct ((-managed_tensor_size) & 7).
CHANGELOG.md One-line changelog entry added under the Unreleased/Fixed section, correctly describes the bug fix.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Producer as DLPack Producer (e.g. NumPy)
    participant Capsule as PyCapsule
    participant Warp as wp.from_dlpack()
    participant Array as Warp Array

    Producer->>Capsule: "PyCapsule_New(DLManagedTensor) data=alloc_base, byte_offset=N"
    Capsule->>Warp: pass capsule
    Warp->>Warp: PyCapsule_GetPointer → managed_ptr
    Warp->>Warp: "dlt = managed_tensor.dl_tensor"
    Note over Warp: Before fix: ptr = dlt.data, After fix: ptr = dlt.data + dlt.byte_offset
    Warp->>Array: "array(ptr=ptr, shape=shape, ...)"
    Warp->>Capsule: PyCapsule_SetName(used_dltensor)
    Array-->>Warp: return arr (ptr points to logical element 0)
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Producer as DLPack Producer (e.g. NumPy)
    participant Capsule as PyCapsule
    participant Warp as wp.from_dlpack()
    participant Array as Warp Array

    Producer->>Capsule: "PyCapsule_New(DLManagedTensor) data=alloc_base, byte_offset=N"
    Capsule->>Warp: pass capsule
    Warp->>Warp: PyCapsule_GetPointer → managed_ptr
    Warp->>Warp: "dlt = managed_tensor.dl_tensor"
    Note over Warp: Before fix: ptr = dlt.data, After fix: ptr = dlt.data + dlt.byte_offset
    Warp->>Array: "array(ptr=ptr, shape=shape, ...)"
    Warp->>Capsule: PyCapsule_SetName(used_dltensor)
    Array-->>Warp: return arr (ptr points to logical element 0)
Loading

Reviews (2): Last reviewed commit: "Fix DLPack test alignment padding" | Re-trigger Greptile

Comment thread warp/tests/interop/test_dlpack.py Outdated
shape = (3,)

managed_tensor_size = ctypes.sizeof(DLManagedTensor)
padding = managed_tensor_size & 7

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 The padding formula managed_tensor_size & 7 is semantically inverted. It yields the number of bytes already consumed within the current 8-byte block, not the bytes needed to reach the next boundary. For a size of 57 it gives 1 instead of 7. The result happens to be correct here because DLManagedTensor is 64 bytes (a multiple of 8), but the formula would silently misalign the shape array if the struct size ever changed. The correct expression is (-managed_tensor_size) & 7.

Suggested change
padding = managed_tensor_size & 7
padding = (-managed_tensor_size) & 7

Signed-off-by: Minh Vu <vuhoangminh97@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant