Skip to content

Commit aea6030

Browse files
author
pytorchbot
committed
2024-12-09 nightly release (b9db0a3)
1 parent f94caf8 commit aea6030

File tree

7 files changed

+131
-61
lines changed

7 files changed

+131
-61
lines changed

backends/vulkan/runtime/graph/containers/Value.h

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ struct Value final {
5858
bool as_bool;
5959
} u;
6060

61-
api::vTensor as_tensor;
61+
std::unique_ptr<api::vTensor> as_tensor;
6262
api::StagingBuffer as_staging;
6363
TensorRef as_tensorref;
6464

@@ -106,15 +106,18 @@ struct Value final {
106106
rhs.payload.member_name.~dtor_name(); \
107107
break;
108108

109+
#define CASE_MOVE_UNIQUE_PTR_TYPE(type_tag, member_name) \
110+
case type_tag: \
111+
payload.member_name = std::move(rhs.payload.member_name); \
112+
break;
113+
109114
Value(Value&& rhs) noexcept : tag(rhs.tag) {
110115
switch (tag) {
111116
// Scalar types
112117
CASE_MOVE_TRIVIALLY_COPYABLE_TYPE(TypeTag::INT, as_int);
113118
CASE_MOVE_TRIVIALLY_COPYABLE_TYPE(TypeTag::DOUBLE, as_double);
114119
CASE_MOVE_TRIVIALLY_COPYABLE_TYPE(TypeTag::BOOL, as_bool);
115-
// Tensor and tensor adjacent types
116-
CASE_MOVE_MOVEABLE_TYPE(
117-
TypeTag::TENSOR, api::vTensor, as_tensor, vTensor);
120+
// Tensor adjacent types
118121
CASE_MOVE_MOVEABLE_TYPE(
119122
TypeTag::STAGING, api::StagingBuffer, as_staging, StagingBuffer);
120123
CASE_MOVE_MOVEABLE_TYPE(
@@ -132,6 +135,8 @@ struct Value final {
132135
CASE_MOVE_MOVEABLE_TYPE(
133136
TypeTag::STRING, std::string, as_string, basic_string);
134137
CASE_MOVE_MOVEABLE_TYPE(TypeTag::SYMINT, SymInt, as_symint, SymInt);
138+
// Tensor type
139+
CASE_MOVE_UNIQUE_PTR_TYPE(TypeTag::TENSOR, as_tensor);
135140

136141
case TypeTag::NONE:
137142
clearToNone();
@@ -142,6 +147,7 @@ struct Value final {
142147

143148
#undef CASE_MOVE_TRIVIALLY_COPYABLE_TYPE
144149
#undef CASE_MOVE_MOVEABLE_TYPE
150+
#undef CASE_MOVE_UNIQUE_PTR_TYPE
145151

146152
//
147153
// Accessors
@@ -157,9 +163,6 @@ struct Value final {
157163

158164
~Value() {
159165
switch (tag) {
160-
case TypeTag::TENSOR:
161-
payload.as_tensor.~vTensor();
162-
break;
163166
case TypeTag::STAGING:
164167
payload.as_staging.~StagingBuffer();
165168
break;
@@ -184,6 +187,9 @@ struct Value final {
184187
case TypeTag::SYMINT:
185188
payload.as_symint.~SymInt();
186189
break;
190+
case TypeTag::TENSOR:
191+
payload.as_tensor.reset();
192+
break;
187193
// Manually list out the types so that if a type here is added later and
188194
// not handled the compiler can catch it.
189195
case TypeTag::NONE:
@@ -252,12 +258,6 @@ struct Value final {
252258
return payload.member_name; \
253259
}
254260

255-
SUPPORT_TRIVIALLY_MOVEABLE_TYPE(
256-
api::vTensor,
257-
Tensor,
258-
TypeTag::TENSOR,
259-
as_tensor);
260-
261261
SUPPORT_TRIVIALLY_MOVEABLE_TYPE(
262262
api::StagingBuffer,
263263
Staging,
@@ -302,9 +302,36 @@ struct Value final {
302302

303303
SUPPORT_TRIVIALLY_MOVEABLE_TYPE(SymInt, SymInt, TypeTag::SYMINT, as_symint);
304304

305-
#undef SUPPORT_TRIVIALLY_COPYABLE_TYPE
306305
#undef SUPPORT_TRIVIALLY_MOVEABLE_TYPE
307306

307+
#define SUPPORT_UNIQUE_PTR_TYPE(type, type_name, type_tag, member_name) \
308+
explicit Value(type t) : tag(type_tag) { \
309+
payload.member_name = std::make_unique<type>(std::move(t)); \
310+
} \
311+
inline bool is##type_name() const { \
312+
return tag == type_tag; \
313+
} \
314+
inline type& to##type_name() const { \
315+
VK_CHECK_COND( \
316+
is##type_name(), \
317+
"Expected value to have type " #type_name ", got ", \
318+
tag, \
319+
" instead."); \
320+
return *payload.member_name; \
321+
} \
322+
inline const type& toConst##type_name() const { \
323+
VK_CHECK_COND( \
324+
is##type_name(), \
325+
"Expected value to have type " #type_name ", got ", \
326+
tag, \
327+
" instead."); \
328+
return *payload.member_name; \
329+
}
330+
331+
SUPPORT_UNIQUE_PTR_TYPE(api::vTensor, Tensor, TypeTag::TENSOR, as_tensor);
332+
333+
#undef SUPPORT_UNIQUE_PTR_TYPE
334+
308335
private:
309336
Payload payload;
310337
TypeTag tag;

backends/vulkan/test/vulkan_compute_api_test.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1087,8 +1087,8 @@ TEST_F(VulkanComputeAPITest, print_object_sizes) {
10871087

10881088
// Current known size on 64 bit system: 1040 B
10891089
EXPECT_TRUE(sizeof(vTensor) < 1200);
1090-
// Current known size on 64 bit system: 1056 B
1091-
EXPECT_TRUE(sizeof(Value) < 1200);
1090+
// Current known size on 64 bit system: 120 B
1091+
EXPECT_TRUE(sizeof(Value) < 128);
10921092
// Current known size on 64 bit system: 120 B
10931093
EXPECT_TRUE(sizeof(StagingBuffer) < 500);
10941094
// Current known size on 64 bit system: 384 B

exir/emit/_emitter.py

Lines changed: 61 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
from executorch.exir.passes.executorch_prim_ops_registry import is_sym_op
4949
from executorch.exir.print_program import _stacktrace_to_framelist, inspect_node
5050
from executorch.exir.schema import (
51+
AllocationDetails,
5152
BackendDelegate,
5253
BackendDelegateDataReference,
5354
BackendDelegateInlineData,
@@ -328,6 +329,59 @@ def _emit_list(self, val: List[_Argument], val_type: _SchemaType) -> EValue:
328329
ExportErrorType.NOT_SUPPORTED, f"Unknown list type: {val_type}"
329330
)
330331

332+
def _get_allocation_info(self, spec: TensorSpec) -> AllocationDetails:
333+
"""Returns the allocation info for a given TensorSpec."""
334+
self._internal_assert_emitter(
335+
isinstance(spec.mem_id, int) and spec.mem_id >= 0,
336+
self.node,
337+
f"Non-const tensor should be an activation tensor: mem_id {spec.mem_id}",
338+
)
339+
340+
self._internal_assert_emitter(
341+
isinstance(spec.mem_offset, int) and spec.mem_offset >= 0,
342+
self.node,
343+
f"Non-const tensor should be an activation tensor: mem_offset {spec.mem_offset}",
344+
)
345+
try:
346+
allocation_info = make_allocation_info(spec.mem_id, spec.mem_offset)
347+
except AddressSpaceOverflowException as e:
348+
raise InternalError(
349+
self._emit_node_specific_error(
350+
self.node,
351+
(
352+
f"{e}\nHint: If you are using a memory pass based on dynamic shape bounds, "
353+
f"such as ConstraintBasedSymShapeEvalPass, this may be the cause of an "
354+
f"unbacked SymInt with its upper bound lazily set to 2^64-1 (uint64 max) "
355+
"during torch.export()."
356+
),
357+
)
358+
)
359+
return allocation_info
360+
361+
def _save_new_const_tensor(
362+
self,
363+
spec: TensorSpec,
364+
buffer_data: bytes,
365+
hashed: str,
366+
allocation_info: Optional[AllocationDetails],
367+
) -> int:
368+
"""Saves a new constant tensor to the constant buffer and returns the buffer idx"""
369+
370+
self.program_state.allocated_specs.append(spec)
371+
# +1 because the first buffer location is reserved.
372+
373+
# Update buffer_idx to point to the end of the list where we are adding the new buffer.
374+
buffer = Buffer(storage=buffer_data)
375+
if allocation_info:
376+
buffer_idx = len(self.program_state.mutable_buffer)
377+
self.program_state.cached_spec_mutable_hash_values[hashed] = buffer_idx
378+
self.program_state.mutable_buffer.append(buffer)
379+
else:
380+
buffer_idx = len(self.program_state.constant_buffer)
381+
self.program_state.cached_spec_hash_values[hashed] = buffer_idx
382+
self.program_state.constant_buffer.append(buffer)
383+
return buffer_idx
384+
331385
def _tensor_spec_to_evalue(self, spec: TensorSpec) -> EValue:
332386
"""Constructs an EValue from the given TensorSpec."""
333387

@@ -339,35 +393,12 @@ def _tensor_spec_to_evalue(self, spec: TensorSpec) -> EValue:
339393
# default algos to set offsets, so need to check both.
340394
if spec.mem_id is not None and spec.mem_offset is not None:
341395
# Tensor is an activation.
342-
self._internal_assert_emitter(
343-
isinstance(spec.mem_id, int) and spec.mem_id >= 0,
344-
self.node,
345-
f"Non-const tensor should be an activation tensor: mem_id {spec.mem_id}",
346-
)
347-
348-
self._internal_assert_emitter(
349-
isinstance(spec.mem_offset, int) and spec.mem_offset >= 0,
350-
self.node,
351-
f"Non-const tensor should be an activation tensor: mem_offset {spec.mem_offset}",
352-
)
353-
try:
354-
allocation_info = make_allocation_info(spec.mem_id, spec.mem_offset)
355-
except AddressSpaceOverflowException as e:
356-
raise InternalError(
357-
self._emit_node_specific_error(
358-
self.node,
359-
(
360-
f"{e}\nHint: If you are using a memory pass based on dynamic shape bounds, "
361-
f"such as ConstraintBasedSymShapeEvalPass, this may be the cause of an "
362-
f"unbacked SymInt with its upper bound lazily set to 2^64-1 (uint64 max) "
363-
"during torch.export()."
364-
),
365-
)
366-
)
396+
allocation_info = self._get_allocation_info(spec)
367397

398+
# Tensor is either a constant tensor, or a mutable tensor with an initial state.
368399
if spec.const:
369400
# Tensor with a blob we need to serialize. May not actually be constant at runtime
370-
# if it's a weight with an associated gradient
401+
# if it's a weight with an associated gradient.
371402
spec_array_type = (
372403
ctypes.c_char * typing.cast(torch.UntypedStorage, spec.storage).nbytes()
373404
)
@@ -392,23 +423,11 @@ def _tensor_spec_to_evalue(self, spec: TensorSpec) -> EValue:
392423
else:
393424
buffer_idx = self.program_state.cached_spec_hash_values.get(hashed, -1)
394425

395-
# Haven't seen this constant before
426+
# Haven't seen this constant before.
396427
if buffer_idx == -1:
397-
# Update buffer_idx to point to the end of the list where we are adding the new buffer.
398-
buffer = Buffer(storage=buffer_data)
399-
self.program_state.allocated_specs.append(spec)
400-
# +1 because the first buffer location is reserved
401-
402-
if allocation_info:
403-
buffer_idx = len(self.program_state.mutable_buffer)
404-
self.program_state.cached_spec_mutable_hash_values[hashed] = (
405-
buffer_idx
406-
)
407-
self.program_state.mutable_buffer.append(buffer)
408-
else:
409-
buffer_idx = len(self.program_state.constant_buffer)
410-
self.program_state.cached_spec_hash_values[hashed] = buffer_idx
411-
self.program_state.constant_buffer.append(buffer)
428+
buffer_idx = self._save_new_const_tensor(
429+
spec, buffer_data, hashed, allocation_info
430+
)
412431

413432
if spec.const and spec.nbytes() != len(buffer_data):
414433
raise InternalError(

exir/passes/replace_view_copy_with_view_pass.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ def __init__(self, base: TensorSpec, shape: List[int]) -> None:
109109
"mem_obj_id",
110110
"mem_offset",
111111
"dtype", # property
112+
"extra_tensor_info", # property
112113
]
113114

114115
# Make sure _self_fields and _base_fields are disjoint

exir/schema.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,20 @@ class TensorShapeDynamism(IntEnum):
4343
DYNAMIC_UNBOUND = 2
4444

4545

46+
class TensorDataLocation(IntEnum):
47+
SEGMENT = 0
48+
EXTERNAL = 1
49+
50+
4651
@dataclass
4752
class ExtraTensorInfo:
4853
"""
4954
Check program.fbs for explanations of this enum.
5055
"""
5156

52-
mutable_data_segments_idx: Optional[int] = None
57+
mutable_data_segments_idx: int = 0
5358
fully_qualified_name: Optional[str] = None
59+
location: TensorDataLocation = TensorDataLocation.SEGMENT
5460

5561

5662
@dataclass

exir/tensor.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
import executorch.exir.schema as schema
1919
import torch
2020
from executorch.exir.error import internal_assert
21-
from executorch.exir.schema import ScalarType, TensorShapeDynamism
21+
from executorch.exir.schema import ExtraTensorInfo, ScalarType, TensorShapeDynamism
2222
from executorch.exir.sym_util import eval_shape
2323

2424

@@ -132,6 +132,7 @@ def __init__(
132132
is_sparse: bool = False,
133133
const: bool = False,
134134
requires_grad: bool = False,
135+
extra_tensor_info: Optional[ExtraTensorInfo] = None,
135136
) -> None:
136137
self.scalar_type = dtype
137138
self.const = const
@@ -146,6 +147,7 @@ def __init__(
146147
self.is_sparse = is_sparse
147148
self.init_mem_planning_fields()
148149
self.shape_dynamism: TensorShapeDynamism = determine_tensor_dynanism(self.shape)
150+
self.extra_tensor_info = extra_tensor_info
149151

150152
@property
151153
def allocated_memory(self) -> int:
@@ -346,6 +348,7 @@ def to_list(
346348
allocation_info=allocation_info,
347349
layout=layout_enum(spec.layout),
348350
shape_dynamism=spec.shape_dynamism,
351+
extra_tensor_info=spec.extra_tensor_info,
349352
)
350353
return flatbuffer_tensor
351354

schema/program.fbs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,18 +53,32 @@ enum TensorShapeDynamism : byte {
5353
DYNAMIC_UNBOUND = 2,
5454
}
5555

56+
// Indicates where a tensor is stored.
57+
enum TensorDataLocation : byte {
58+
// Stored in a segment of the PTE file.
59+
SEGMENT = 0,
60+
// Stored outside of the PTE file.
61+
EXTERNAL = 1,
62+
}
5663

5764
// Table to put additional information about tensors in that is not applicable
5865
// to the vast majority of tensors in the vast majority of programs.
5966
table ExtraTensorInfo {
6067
// [Optional] Specifies the SubsegmentOffsets in
6168
// program.mutable_data_segments that specifies where the data is located in.
6269
// If not present and the data is located in a segment, then the data is in
63-
// the first index.
70+
// index zero.
6471
mutable_data_segments_idx: uint64;
6572

6673
// [Optional] The unique name of the tensor. e.g. 'mod.linear.weight'
6774
fully_qualified_name: string;
75+
76+
// [Optional] Specifies where the tensor's data is stored.
77+
// - SEGMENT (default): Data is stored in a segment.
78+
// - EXTERNAL: Data is stored outside of the PTE file. fully_qualified_name
79+
// must be non-empty, and is used as a key to find the tensor's external
80+
// data. Tensor.data_buffer_idx is ignored.
81+
location: TensorDataLocation;
6882
}
6983

7084
table Tensor {

0 commit comments

Comments
 (0)