@@ -62,6 +62,9 @@ class VarLenTensor(Tensor):
6262 size: The shape of the tensor.
6363 stride: The stride of the tensor.
6464 storage_offset: The offset into the logical ``offset`` storage.
65+ max_length: Upper bound on the length of every stored entry, or
66+ :obj:`None` if unknown. Lets compiled operations size buffers
67+ without reading lengths from the device. Must not underestimate.
6568 """
6669
6770 ALLOWED_DTYPES : ClassVar [tuple [torch .dtype , ...] | None ] = None
@@ -72,6 +75,7 @@ class VarLenTensor(Tensor):
7275 _data : Tensor
7376 _offset : Tensor
7477 _valid : Tensor | None
78+ _max_length : int | None
7579 _layout : Tensor
7680
7781 # Constructors ############################################################
@@ -84,6 +88,7 @@ def __init__(
8488 size : Sequence [int ],
8589 stride : Sequence [int ] | None = None ,
8690 storage_offset : int = 0 ,
91+ max_length : int | None = None ,
8792 ) -> None :
8893 pass
8994
@@ -95,6 +100,7 @@ def __new__(
95100 size : Sequence [int ],
96101 stride : Sequence [int ] | None = None ,
97102 storage_offset : int = 0 ,
103+ max_length : int | None = None ,
98104 ) -> Self :
99105 r"""Create a tensor wrapper."""
100106 size = tuple (size )
@@ -222,6 +228,7 @@ def __new__(
222228 size = size ,
223229 stride = stride ,
224230 storage_offset = storage_offset ,
231+ max_length = max_length ,
225232 cls = None if "_make_var_len_tensor" in cls .__dict__ else cls ,
226233 )
227234
@@ -235,6 +242,7 @@ def _make_var_len_tensor(
235242 size : tuple [int , ...],
236243 stride : tuple [int , ...],
237244 storage_offset : int ,
245+ max_length : int | None ,
238246 cls : type [VarLenTensor ] | None = None ,
239247 ) -> VarLenTensor :
240248 cls = VarLenTensor if cls is None else cls
@@ -250,6 +258,7 @@ def _make_var_len_tensor(
250258 out ._data = data
251259 out ._offset = offset
252260 out ._valid = valid
261+ out ._max_length = max_length
253262 # Mirror size and stride in an empty tensor's shape so torch.compile
254263 # can make them symbolic. A tensor nested in another one, like a
255264 # categorical vocabulary, would otherwise keep its first traced size.
@@ -292,6 +301,7 @@ def from_tensor(
292301 size = tensor .size (),
293302 stride = tensor .stride (),
294303 storage_offset = int (tensor .storage_offset ()),
304+ max_length = min (data .numel (), 1 ), # Every entry has length 1.
295305 )
296306
297307 @classmethod
@@ -566,7 +576,7 @@ def __tensor_flatten__(self) -> tuple[list[str], tuple[Any, ...]]:
566576 attrs = ["_offset" , "_data" , "_layout" ]
567577 if self ._valid is not None :
568578 attrs .append ("_valid" )
569- return attrs , (self .__class__ , self .storage_offset ())
579+ return attrs , (self .__class__ , self .storage_offset (), self . _max_length )
570580
571581 @staticmethod
572582 def __tensor_unflatten__ (
@@ -575,7 +585,7 @@ def __tensor_unflatten__(
575585 outer_size : tuple [int , ...],
576586 outer_stride : tuple [int , ...],
577587 ) -> VarLenTensor :
578- cls , storage_offset = ctx
588+ cls , storage_offset , max_length = ctx
579589 layout = inner_tensors ["_layout" ]
580590 out = cls (
581591 data = inner_tensors ["_data" ],
@@ -584,6 +594,7 @@ def __tensor_unflatten__(
584594 size = layout .size ()[:- 1 ],
585595 stride = layout .stride ()[:- 1 ],
586596 storage_offset = storage_offset ,
597+ max_length = max_length ,
587598 )
588599 out ._layout = layout
589600 return out
@@ -596,6 +607,7 @@ def __reduce_ex__(self, proto: SupportsIndex) -> Any:
596607 tuple (self .size ()),
597608 tuple (self .stride ()),
598609 int (self .storage_offset ()),
610+ self ._max_length ,
599611 )
600612 return (self .__class__ , args )
601613
@@ -740,6 +752,7 @@ def _alias(inp: VarLenTensor) -> VarLenTensor:
740752 size = inp .size (),
741753 stride = inp .stride (),
742754 storage_offset = int (inp .storage_offset ()),
755+ max_length = inp ._max_length ,
743756 )
744757
745758
@@ -849,6 +862,7 @@ def _to_dtype_layout(
849862 if memory_format == torch .preserve_format
850863 else None ,
851864 storage_offset = 0 ,
865+ max_length = inp ._max_length ,
852866 )
853867
854868
@@ -958,6 +972,7 @@ def _detach(inp: VarLenTensor) -> VarLenTensor:
958972 size = inp .size (),
959973 stride = inp .stride (),
960974 storage_offset = int (inp .storage_offset ()),
975+ max_length = inp ._max_length ,
961976 )
962977
963978
@@ -976,6 +991,7 @@ def _pin_memory(inp: VarLenTensor) -> VarLenTensor:
976991 size = inp .size (),
977992 stride = inp .stride (),
978993 storage_offset = int (inp .storage_offset ()),
994+ max_length = inp ._max_length ,
979995 )
980996
981997
@@ -1370,6 +1386,10 @@ def _cat(tensors: Sequence[Tensor], dim: int = 0) -> VarLenTensor:
13701386 tensors = tuple (
13711387 cast (VarLenTensor , tensor .contiguous ()) for tensor in tensors
13721388 )
1389+ lengths = tuple (tensor ._max_length for tensor in tensors )
1390+ max_length = (
1391+ None if None in lengths else max (cast (tuple [int , ...], lengths ))
1392+ )
13731393 data_list , offsets = zip (* (_data_offset (tensor ) for tensor in tensors ))
13741394 valid : Tensor | None = None
13751395 if any (tensor ._valid is not None for tensor in tensors ):
@@ -1411,15 +1431,27 @@ def _cat(tensors: Sequence[Tensor], dim: int = 0) -> VarLenTensor:
14111431
14121432 if math .prod (tensors [0 ].size ()[:dim ]) == 1 : # Contiguous path:
14131433 offset [- 1 ] = storage_offset
1414- return tensor_cls (data = data , offset = offset , valid = valid , size = size )
1434+ return tensor_cls (
1435+ data = data ,
1436+ offset = offset ,
1437+ valid = valid ,
1438+ size = size ,
1439+ max_length = max_length ,
1440+ )
14151441
14161442 end = torch .cat (end_views , dim = dim )
14171443 start = torch .as_strided (start , size = (start .numel (),), stride = (1 ,))
14181444 end = torch .as_strided (end , size = (end .numel (),), stride = (1 ,))
14191445
14201446 offset , index = _compact (start , end )
14211447
1422- return tensor_cls (data = data [index ], offset = offset , valid = valid , size = size )
1448+ return tensor_cls (
1449+ data = data [index ],
1450+ offset = offset ,
1451+ valid = valid ,
1452+ size = size ,
1453+ max_length = max_length ,
1454+ )
14231455
14241456
14251457@VarLenTensor .implements (aten .stack .default )
@@ -1469,6 +1501,7 @@ def _from_layout_view(inp: VarLenTensor, view: Tensor) -> VarLenTensor:
14691501 size = view .size (),
14701502 stride = view .stride (),
14711503 storage_offset = 0 if view .numel () == 0 else int (storage_offset ),
1504+ max_length = inp ._max_length ,
14721505 )
14731506
14741507
@@ -1558,4 +1591,5 @@ def _materialize(
15581591 size = size ,
15591592 stride = stride ,
15601593 storage_offset = 0 ,
1594+ max_length = inp ._max_length ,
15611595 )
0 commit comments