@@ -1962,12 +1962,18 @@ void DefaultActorImpl::deallocate() {
19621962#endif
19631963}
19641964
1965+ static size_t
1966+ getDistributedRemoteActorAllocSize (const ClassMetadata *metadata);
1967+
19651968void DefaultActorImpl::deallocateUnconditional () {
19661969 concurrency::trace::actor_deallocate (this );
19671970
19681971#if !SWIFT_CONCURRENCY_EMBEDDED
19691972 auto metadata = cast<ClassMetadata>(this ->metadata );
1970- swift_deallocClassInstance (this , metadata->getInstanceSize (),
1973+ size_t deallocSize = isDistributedRemote () ?
1974+ getDistributedRemoteActorAllocSize (metadata)
1975+ : metadata->getInstanceSize ();
1976+ swift_deallocClassInstance (this , deallocSize,
19711977 metadata->getInstanceAlignMask ());
19721978#else
19731979 // Embedded Swift's runtime doesn't actually use the size/mask values.
@@ -2299,12 +2305,17 @@ static bool isDefaultActorClass(const ClassMetadata *metadata) {
22992305void swift::swift_defaultActor_deallocateResilient (HeapObject *actor) {
23002306#if !SWIFT_CONCURRENCY_EMBEDDED
23012307 auto metadata = cast<ClassMetadata>(actor->metadata );
2308+
23022309 if (isDefaultActorClass (metadata))
23032310 return swift_defaultActor_deallocate (static_cast <DefaultActor*>(actor));
23042311
2305- swift_deallocObject (actor, metadata->getInstanceSize (),
2312+ size_t deallocSize = swift_distributed_actor_is_remote (actor) ?
2313+ getDistributedRemoteActorAllocSize (metadata)
2314+ : metadata->getInstanceSize ();
2315+ swift_deallocObject (actor, deallocSize,
23062316 metadata->getInstanceAlignMask ());
23072317#else
2318+ // TODO(distributed): Embedded should be able to handle remote actor references here
23082319 return swift_defaultActor_deallocate (static_cast <DefaultActor*>(actor));
23092320#endif
23102321}
@@ -2774,25 +2785,50 @@ void swift::swift_nonDefaultDistributedActor_initialize(NonDefaultDistributedAct
27742785 asImpl (_actor)->initialize ();
27752786}
27762787
2788+ // / Compute the minimal allocation size for a 'remote' distributed actor reference.
2789+ // /
2790+ // / Remote distributed actors are proxy objects that only need storage for the
2791+ // / `id`, `actorSystem`, and `unownedExecutor` fields. Any user-defined stored
2792+ // / properties beyond those three are never initialized or accessed on a remote
2793+ // / instance, so we can trim the allocation at the offset where the first
2794+ // / user-defined field would begin.
2795+ static size_t
2796+ getDistributedRemoteActorAllocSize (const ClassMetadata *metadata) {
2797+ auto description = metadata->getDescription ();
2798+ uint32_t numFields = description->NumFields ;
2799+
2800+ assert (numFields >= 3 &&
2801+ " distributed actor must have at least id, actorSystem, and "
2802+ " unownedExecutor fields" );
2803+
2804+ if (numFields >= 4 ) {
2805+ // The 4th field is the first user-defined stored property.
2806+ // Its offset marks the end of the synthesized fields,
2807+ // so it is exactly how much storage we need.
2808+ const auto *fieldOffsets = metadata->getFieldOffsets ();
2809+ return fieldOffsets[3 ];
2810+ }
2811+
2812+ // Only the three required fields exist, remote-ref and local instances have the same size.
2813+ return metadata->getInstanceSize ();
2814+ }
2815+
27772816OpaqueValue*
27782817swift::swift_distributedActor_remote_initialize (const Metadata *actorType) {
27792818 const ClassMetadata *metadata = actorType->getClassObject ();
27802819
2781- // TODO(distributed): make this allocation smaller
2782- // ==== Allocate the memory for the remote instance
2820+ // Use a smaller allocation that only covers the id and actorSystem fields,
2821+ // since remote proxies never access user-defined stored properties.
2822+ size_t allocSize = getDistributedRemoteActorAllocSize (metadata);
2823+ assert (allocSize <= metadata->getInstanceSize () && " Remote reference size was larger than a local instance size!?" );
27832824 HeapObject *alloc = swift_allocObject (metadata,
2784- metadata-> getInstanceSize () ,
2825+ allocSize ,
27852826 metadata->getInstanceAlignMask ());
27862827
2787- // TODO: remove this memset eventually, today we only do this to not have
2788- // to modify the destructor logic, as releasing zeroes is no-op
2789- memset ((void *)(alloc + 1 ), 0 ,
2790- metadata->getInstanceSize () - sizeof (HeapObject));
2828+ // Zero the body so that the destructor can safely release fields without
2829+ // encountering uninitialized memory.
2830+ memset ((void *)(alloc + 1 ), 0 , allocSize - sizeof (HeapObject));
27912831
2792- // TODO(distributed): a remote one does not have to have the "real"
2793- // default actor body, e.g. we don't need an executor at all; so
2794- // we can allocate more efficiently and only share the flags/status field
2795- // between the both memory representations
27962832 // If it is a default actor, we reuse the same layout as DefaultActorImpl,
27972833 // and store flags in the allocation directly as we initialize it.
27982834 if (isDefaultActorClass (metadata)) {
0 commit comments