Skip to content

Commit fc82e92

Browse files
committed
Explain the separate eager and compiled gathers; consolidate permutation tests
Move the zero-padding check to views that leave padding, build CUDA inputs with from_arrow, and merge the payload edge cases into one test.
1 parent 479a0b7 commit fc82e92

2 files changed

Lines changed: 35 additions & 44 deletions

File tree

‎sdm/tensor/var_len.py‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1603,6 +1603,11 @@ def _permute_entries(inp: VarLenTensor, perm: Tensor) -> VarLenTensor:
16031603
A permutation of a view with nonzero stride selects at most the bytes of
16041604
its backing storage, so the output keeps that static capacity regardless
16051605
of individual entry lengths. Bytes after the final offset are zero.
1606+
1607+
Eager CUDA copies in tiles, which needs one host read of entry counts;
1608+
compiled code searches each element's entry, which Inductor fuses into
1609+
one kernel. A single tiled path for both was 2.7x slower eager on 1M
1610+
URLs and used up to 2.9x the memory, so the two paths stay separate.
16061611
"""
16071612
if inp.stride(0) == 0:
16081613
return cast(VarLenTensor, inp[perm])

‎test/tensor/test_var_len.py‎

Lines changed: 30 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1094,6 +1094,7 @@ def test_permute_entries(
10941094
assert actual.device == expected.device
10951095
assert actual.data_offset[1].dtype == expected.data_offset[1].dtype
10961096
assert actual.data_offset[1].diff().equal(expected.data_offset[1].diff())
1097+
assert not actual._data[actual._offset[-1] :].any()
10971098

10981099

10991100
def test_permute_entries_skewed_lengths() -> None:
@@ -1114,32 +1115,12 @@ def test_permute_entries_long_and_short(
11141115
# Mostly long entries, mixed with short, empty and null ones; the last
11151116
# entry ends at the end of the payload.
11161117
values = StringTensor.from_arrow(
1117-
pa.array(["a" * 41, "bc", None, "", "é" * 59 + "z"])
1118-
)
1119-
assert values._valid is not None
1120-
values = StringTensor(
1121-
data=values._data.to(device),
1122-
offset=values._offset.to(device),
1123-
valid=values._valid.to(device),
1124-
size=values.shape,
1118+
pa.array(["a" * 41, "bc", None, "", "é" * 59 + "z"]), device=device
11251119
)
11261120
index = torch.tensor(perm, device=device)
11271121
actual = _permute_entries(values, index)
11281122
assert actual.tolist() == values[index].tolist()
11291123
assert actual._data.numel() == values._data.numel()
1130-
assert not actual._data[actual._offset[-1] :].any()
1131-
1132-
1133-
def test_permute_entries_bool_payload() -> None:
1134-
values = VarLenTensor(
1135-
data=torch.tensor([True, False, True, True]),
1136-
offset=torch.tensor([0, 1, 4]),
1137-
valid=None,
1138-
size=(2,),
1139-
)
1140-
actual = _permute_entries(values, torch.tensor([1, 0]))
1141-
assert actual.dtype == torch.bool
1142-
assert actual.tolist() == [[False, True, True], [True]]
11431124

11441125

11451126
def test_permute_entries_fullgraph(compilation: None) -> None:
@@ -1167,26 +1148,31 @@ def test_permute_entries_meta() -> None:
11671148
assert actual.valid.shape == perm.shape
11681149

11691150

1170-
@pytest.mark.parametrize("empty", [False, True])
1171-
def test_permute_entries_empty_payload(empty: bool) -> None:
1172-
values = StringTensor.from_arrow(
1173-
pa.array([], type=pa.string()) if empty else pa.array(["", None, ""])
1174-
)
1175-
perm = torch.tensor([] if empty else [2, 0, 1], dtype=torch.int64)
1176-
actual = _permute_entries(values, perm)
1177-
assert actual.tolist() == values[perm].tolist()
1178-
assert actual._max_length == 0
1179-
1180-
1181-
def test_permute_entries_null_payload() -> None:
1182-
values = StringTensor(
1183-
data=torch.tensor([97, 255, 98], dtype=torch.uint8),
1184-
offset=torch.tensor([0, 1, 2, 3], dtype=torch.int32),
1185-
valid=torch.tensor([True, False, True]),
1186-
size=(3,),
1187-
)
1188-
perm = torch.tensor([1, 2, 0])
1189-
actual = _permute_entries(values, perm)
1190-
assert actual.tolist() == values[perm].tolist()
1191-
assert actual._max_length is None
1192-
assert actual.data_offset[1].diff().tolist() == [1, 1, 1]
1151+
def test_permute_entries_payloads() -> None:
1152+
cases = [
1153+
# Boolean payload.
1154+
VarLenTensor(
1155+
data=torch.tensor([True, False, True, True]),
1156+
offset=torch.tensor([0, 1, 4]),
1157+
valid=None,
1158+
size=(2,),
1159+
),
1160+
# A null slot holding a byte, and no length bound.
1161+
StringTensor(
1162+
data=torch.tensor([97, 255, 98], dtype=torch.uint8),
1163+
offset=torch.tensor([0, 1, 2, 3], dtype=torch.int32),
1164+
valid=torch.tensor([True, False, True]),
1165+
size=(3,),
1166+
),
1167+
# Empty strings with an empty buffer, and no entries at all.
1168+
StringTensor.from_arrow(pa.array(["", None, ""])),
1169+
StringTensor.from_arrow(pa.array([], type=pa.string())),
1170+
]
1171+
for values in cases:
1172+
perm = torch.arange(values.numel()).roll(1)
1173+
expected = cast(VarLenTensor, values[perm])
1174+
actual = _permute_entries(values, perm)
1175+
assert actual.dtype == values.dtype
1176+
assert actual.tolist() == expected.tolist()
1177+
assert actual.data_offset[1].equal(expected.data_offset[1])
1178+
assert actual._max_length == values._max_length

0 commit comments

Comments
 (0)