Does Swift Guarantee the Storage Order of Fields in Classes and Structs

Does Swift guarantee the storage order of fields in classes and structs?

Yes, the order of the struct elements in memory is the order of
their declaration. The details can be found
in Type Layout
(emphasis added). Note however the use of "currently", so this
may change in a future version of Swift:

Fragile Struct and Tuple Layout

Structs and tuples currently share the same layout algorithm, noted as the "Universal" layout algorithm in the compiler implementation. The algorithm is as follows:

  • Start with a size of 0 and an alignment of 1.
  • Iterate through the
    fields, in element order for tuples, or in var declaration order for
    structs. For each field:

    • Update size by rounding up to the alignment
      of the field, that is, increasing it to the least value greater or
      equal to size and evenly divisible by the alignment of the field.
    • Assign the offset of the field to the current value of size.
    • Update
      size by adding the size of the field.
    • Update alignment to the max of
      alignment and the alignment of the field.
  • The final size and alignment
    are the size and alignment of the aggregate. The stride of the type is
    the final size rounded up to alignment.

The padding/alignment is different from C:

Note that this differs from C or LLVM's normal layout rules in that size and stride are distinct; whereas C layout requires that an embedded struct's size be padded out to its alignment and that nothing be laid out there, Swift layout allows an outer struct to lay out fields in the inner struct's tail padding, alignment permitting.

Only if a struct is imported from C then it is guaranteed to have
the same memory layout. Joe Groff from Apple writes at
[swift-users] Mapping C semantics to Swift

If you depend on a specific layout, you should define the struct in C and import it into Swift for now.

and later in that discussion:

You can leave the struct defined in C and import it into Swift. Swift will respect C's layout.

Example:

struct A {
var a: UInt8 = 0
var b: UInt32 = 0
var c: UInt8 = 0
}

struct B {
var sa: A
var d: UInt8 = 0
}

// Swift 2:
print(sizeof(A), strideof(A)) // 9, 12
print(sizeof(B), strideof(B)) // 10, 12

// Swift 3:
print(MemoryLayout<A>.size, MemoryLayout<A>.stride) // 9, 12
print(MemoryLayout<B>.size, MemoryLayout<B>.stride) // 10, 12

Here var d: UInt8 is layed out in the tail padding of var sa: A.
If you define the same structures in C

struct  CA {
uint8_t a;
uint32_t b;
uint8_t c;
};

struct CB {
struct CA ca;
uint8_t d;
};

and import it to Swift then

// Swift 2:
print(sizeof(CA), strideof(CA)) // 9, 12
print(sizeof(CB), strideof(CB)) // 13, 16

// Swift 3:
print(MemoryLayout<CA>.size, MemoryLayout<CA>.stride) // 12, 12
print(MemoryLayout<CB>.size, MemoryLayout<CB>.stride) // 16, 16

because uint8_t d is layed out after the tail padding of struct CA sa.

As of Swift 3, both size and stride return the same value
(including the struct padding) for structures imported from C,
i.e. the same value as sizeof in C would return.

Here is a simple function which helps to demonstrate the above (Swift 3):

func showMemory<T>(_ ptr: UnsafePointer<T>) {
let data = Data(bytes: UnsafeRawPointer(ptr), count: MemoryLayout<T>.size)
print(data as NSData)
}

The structures defined in Swift:

var a = A(a: 0xaa, b: 0x, c: 0xcc)
showMemory(&a) // <aa000000 cc>

var b = B(sa: a, d: 0xdd)
showMemory(&b) // <aa000000 ccdd>

The structures imported from C:

var ca = CA(a: 0xaa, b: 0x, c: 0xcc)
showMemory(&ca) // <aa000000 cc000000>

var cb = CB(ca: ca, d: 0xdd)
showMemory(&cb) // <aa000000 cc000000 dd000000>

Why Choose Struct Over Class?

According to the very popular WWDC 2015 talk Protocol Oriented Programming in Swift (video, transcript), Swift provides a number of features that make structs better than classes in many circumstances.

Structs are preferable if they are relatively small and copiable because copying is way safer than having multiple references to the same instance as happens with classes. This is especially important when passing around a variable to many classes and/or in a multithreaded environment. If you can always send a copy of your variable to other places, you never have to worry about that other place changing the value of your variable underneath you.

With Structs, there is much less need to worry about memory leaks or multiple threads racing to access/modify a single instance of a variable. (For the more technically minded, the exception to that is when capturing a struct inside a closure because then it is actually capturing a reference to the instance unless you explicitly mark it to be copied).

Classes can also become bloated because a class can only inherit from a single superclass. That encourages us to create huge superclasses that encompass many different abilities that are only loosely related. Using protocols, especially with protocol extensions where you can provide implementations to protocols, allows you to eliminate the need for classes to achieve this sort of behavior.

The talk lays out these scenarios where classes are preferred:

  • Copying or comparing instances doesn't make sense (e.g., Window)
  • Instance lifetime is tied to external effects (e.g., TemporaryFile)
  • Instances are just "sinks"--write-only conduits to external state (e.g.CGContext)

It implies that structs should be the default and classes should be a fallback.

On the other hand, The Swift Programming Language documentation is somewhat contradictory:

Structure instances are always passed by value, and class
instances are always passed by reference. This means that they are
suited to different kinds of tasks. As you consider the data
constructs and functionality that you need for a project, decide
whether each data construct should be defined as a class or as a
structure.

As a general guideline, consider creating a structure when one or more
of these conditions apply:

  • The structure’s primary purpose is to encapsulate a few relatively simple data values.
  • It is reasonable to expect that the encapsulated values will be copied rather than referenced when you assign or pass around an
    instance of that structure.
  • Any properties stored by the structure are themselves value types, which would also be expected to be copied rather than referenced.
  • The structure does not need to inherit properties or behavior from another existing type.

Examples of good candidates for structures include:

  • The size of a geometric shape, perhaps encapsulating a width property and a height property, both of type Double.
  • A way to refer to ranges within a series, perhaps encapsulating a start property and a length property, both of type Int.
  • A point in a 3D coordinate system, perhaps encapsulating x, y and z properties, each of type Double.

In all other cases, define a class, and create instances of that class
to be managed and passed by reference. In practice, this means that
most custom data constructs should be classes, not structures.

Here it is claiming that we should default to using classes and use structures only in specific circumstances. Ultimately, you need to understand the real world implication of value types vs. reference types and then you can make an informed decision about when to use structs or classes. Also, keep in mind that these concepts are always evolving and The Swift Programming Language documentation was written before the Protocol Oriented Programming talk was given.

Swift and mutating struct

The mutability attribute is marked on a storage (constant or variable), not a type. You can think struct has two modes: mutable and immutable. If you assign a struct value to an immutable storage (we call it let or constant in Swift) the value becomes immutable mode, and you cannot change any state in the value. (including calling any mutating method)

If the value is assigned to a mutable storage (we call it var or variable in Swift), you're free to modify the state of them, and calling of mutating method is allowed.

In addition, classes don't have this immutable/mutable mode. IMO, this is because classes are usually used to represent reference-able entity. And reference-able entity is usually mutable because it's very hard to make and manage reference graphs of entities in immutable manner with proper performance. They may add this feature later, but not now at least.

For Objective-C programmers, mutable/immutable concepts are very familiar. In Objective-C we had two separated classes for each concept, but in Swift, you can do this with one struct. Half work.

For C/C++ programmers, this is also very familiar concept. This is exactly what const keyword do in C/C++.

Also, immutable value can be very nicely optimised. In theory, Swift compiler (or LLVM) can perform copy-elision on values passed by let, just like in C++. If you use immutable struct wisely, it will outperform refcounted classes.

Update

As @Joseph claimed this doesn't provide why, I am adding a little more.

Structs have two kind of methods. plain and mutating methods. Plain method implies immutable (or non-mutating). This separation exists only to support immutable semantics. An object in immutable mode shouldn't change its state at all.

Then, immutable methods must guarantee this semantic immutability. Which means it shouldn't change any internal value. So compiler disallows any state changes of itself in a immutable method. In contrast, mutating methods are free to modify states.

And then, you may have a question of why immutable is the default? That's because it's very hard to predict the future state of mutating values, and that usually becomes the main source of headaches and bugs. Many people agreed that the solution is avoiding mutable stuffs, and then immutable by default was on top of wish list for decades in C/C++ family languages and its derivations.

See purely functional style for more details. Anyway, we still need mutable stuffs because immutable stuffs have some weaknesses, and discussing about them seems to be out of topic.

I hope this helps.

Cast to different C struct unsafe pointer in Swift

You can write something like this:

withUnsafePointer(to: &sockAddress) {sockaddrInPtr in
sockaddrInPtr.withMemoryRebound(to: sockaddr.self, capacity: 1) {sockaddrPtr in
bind(sock, sockaddrPtr, UInt32(MemoryLayout<sockaddr_in>.stride))
}
}

Or someone suggests this may be better:

withUnsafePointer(to: &sockAddress) {sockaddrInPtr in
let sockaddrPtr = UnsafeRawPointer(sockaddrInPtr).assumingMemoryBound(to: sockaddr.self)
bind(sock, sockaddrPtr, UInt32(MemoryLayout<sockaddr_in>.stride))
}

This article may be some help.


(UPDATE)
As described in the link shown by Martin R, now MemoryLayout<T>.stride and MemoryLayout<T>.size return the same value which is consistent with C's sizeof, where T is an imported C-struct. I'll keep my stride version of answer here, but that is not something "required" in this case now.

Pass an array within uniforms struct to Metal shader

Since Swift makes no guarantees about struct layout, it would be dangerous to copy the contents of such a struct into a Metal buffer directly (also, as written, the array contains Doubles, which are not supported by Metal currently anyway). There are a few different approaches that could work, depending on the shape of the real problem.

If you know the maximum number of elements in the array, you could add a struct member indicating the actual count, and make the last element of the struct expected by your shader a fixed-length array:

#define MAX_VALUE_COUNT 1024
struct ShaderUniforms {
float t;
uint32_t valueCount;
float values[MAX_VALUE_COUNT];
};

Then, in Swift, you could allocate a Metal buffer of the maximum size (4104 bytes, in this contrived case) and copy however many array elements you need into the buffer (preceded, of course, by the other struct members).

Alternately, yes, you could use a separate buffer parameter of pointer type (e.g., constant float *values [[buffer(1)]]). That would allow you to have a value count that isn't bounded by anything explicitly coded into the shader.

Passing parameters in Metal Compute Kernel using Swift 4

First-off, I want to point out that you should not be using size when you need to get an actual length of a Swift type laid out in memory. You should use stride for that. According to Swift's Type Layout:

The final size and alignment are the size and alignment of the aggregate. The stride of the type is the final size rounded up to alignment.

This answer goes into detail about memory layout in Swift if you want to get a better understanding of the topic.


The problem is that your Metal struct that uses float2 and a Swift struct that replaces it with two separate Float fields have different memory layouts.

Size (stride in case of Swift) of the struct needs to be a multiple of the largest alignment of any struct member. The largest alignment in your Metal struct is 8 bytes (alignment of float2) so there's a padding at the tail of struct after the float value.

struct BoundingBoxParameters {
float2 topLeft; // 8 bytes
float2 size; // 8 bytes
float levelOfDetail; // 4 bytes
// 4 bytes of padding so that size of struct is multiple
// of the largest alignment (which is 8 bytes)

}; // 24 bytes in total

So your Metal struct does in fact end up taking up 24 bytes as the error suggests.

At the same time, your Swift struct, having the largest alignment of 4 bytes, only needs 20 bytes.

private struct BoundingBoxParameters {
var x: Float = 0 // 4 bytes
var y: Float = 0 // 4 bytes
var width: Float = 0 // 4 bytes
var height: Float = 0 // 4 bytes
var levelOfDetail: Float = 1.0 // 4 bytes
// no need for any padding

} // 20 bytes in total

That's why they end up incompatible with each other and dummy field compensating 4 missing bytes to Swift struct.

To resolve this I suggest you use float2 from simd in Swift instead of Floats:

import simd 

private struct BoundingBoxParameters {
var topLeft = float2(x: 0, y: 0)
var size = float2(x: 0, y: 0)
var levelOfDetail: Float = 1.0
}

Don't forget to use MemoryLayout<BoundingBoxParameters>.stride (24 bytes) to get the length instead of size (20 bytes).


Same goes for the 3x3 matrix case: Metal's float3x3 has size of 48 bytes and alignment of 16 bytes. As I assume, you've created a Swift struct with 9 Floats which would have stride/size of 36 bytes and alignment of 4 bytes. Hence, the mis-alignment. Use matrix_float3x3 from simd.

In general, for any cases when you use vectors or matrices in Metal you should use corresponding simd types in Swift.

Secure Memory For Swift Objects

If you want complete control over a region of memory you allocate yourself, you can use UnsafePointer and co:

// allocate enough memory for ten Ints
var ump = UnsafeMutablePointer<Int>.alloc(10)
// memory is in an uninitialized raw state

// initialize that memory with Int objects
// (here, from a collection)
ump.initializeFrom(reverse(0..<10))

// memory property gives you access to the underlying value
ump.memory // 9

// UnsafeMutablePointer acts like an IndexType
ump.successor().memory // 8
// and it has a subscript, but it's not a CollectionType
ump[3] // = 6

// wrap it in an UnsafeMutableBufferPointer to treat it
// like a collection (or UnsafeBufferPointer if you don't
// need to be able to alter the values)
let col = UnsafeMutableBufferPointer(start: ump, count: 10)
col[3] = 99
println(",".join(map(col,toString)))
// prints 9,8,7,99,5,4,3,2,1,0

ump.destroy(10)
// now the allocated memory is back in a raw state
// you could re-allocate it...
ump.initializeFrom(0..<10)
ump.destroy(10)

// when you're done, deallocate the memory
ump.dealloc(10)

You can also have UnsafePointer point to other memory, such as memory you’re handed by some C API.

UnsafePointer can be passed into C functions that take a pointer to a contiguous block of memory. So for your purposes, you could then pass this pointer into a function like mlock:

let count = 10
let ump = UnsafeMutablePointer.allocate<Int>(count)
mlock(ump, UInt(sizeof(Int) * count))
// initialize, use, and destroy the memory
munlock(ump, UInt(sizeof(Int) * count))
ump.dealloc(count)

You can even hold your own custom types:

struct MyStruct {
let a: Int
let b: Int
}

var pointerToStruct = UnsafeMutablePointer<MyStruct>.alloc(1)
pointerToStruct.initialize(MyStruct(a: 1, b: 2))
pointerToStruct.memory.b // 2
pointerToStruct.destroy()
pointerToStruct.dealloc(1)

However be aware if doing this with classes, or even arrays or strings (or a struct that contains them), that all you will be holding in your memory is pointers to other memory that these objects allocate and own. If this matters to you (i.e. you are doing something special to this memory such as securing it, in your example), this is probably not what you want.

So either you need to use fixed-size objects, or make further use of UnsafePointer to hold pointers to more memory regions. If they don't need to dynamically resize, then just a single allocation of an unsafe pointer, possibly wrapped in a UnsafeBufferPointer for a collection interface, could do it.

If you need more dynamic behavior, below is a very bare-bones implementation of a collection that can resize as necessary, that could be enhanced to cover specialty memory-handling logic:

// Note this is a class not a struct, so it does NOT have value semantics,
// changing a copy changes all copies.
public class UnsafeCollection<T> {
private var _len: Int = 0
private var _buflen: Int = 0
private var _buf: UnsafeMutablePointer<T> = nil

public func removeAll(keepCapacity: Bool = false) {
_buf.destroy(_len)
_len = 0
if !keepCapacity {
_buf.dealloc(_buflen)
_buflen = 0
_buf = nil
}
}

public required init() { }
deinit { self.removeAll(keepCapacity: false) }

public var count: Int { return _len }
public var isEmpty: Bool { return _len == 0 }
}

To cover the requirements of MutableCollectionType (i.e. CollectionType plus assignable subscript):

extension UnsafeCollection: MutableCollectionType {
typealias Index = Int
public var startIndex: Int { return 0 }
public var endIndex: Int { return _len }

public subscript(idx: Int) -> T {
get {
precondition(idx < _len)
return _buf[idx]
}
set(newElement) {
precondition(idx < _len)
let ptr = _buf.advancedBy(idx)
ptr.destroy()
ptr.initialize(newElement)
}
}

typealias Generator = IndexingGenerator<UnsafeCollection>
public func generate() -> Generator {
return Generator(self)
}
}

And ExtensibleCollectionType, to allow for dynamic growth:

extension UnsafeCollection: ExtensibleCollectionType {
public func reserveCapacity(n: Index.Distance) {
if n > _buflen {
let newBuf = UnsafeMutablePointer<T>.alloc(n)
newBuf.moveInitializeBackwardFrom(_buf, count: _len)
_buf.dealloc(_buflen)
_buf = newBuf
_buflen = n
}
}

public func append(x: T) {
if _len == _buflen {
reserveCapacity(Int(Double(_len) * 1.6) + 1)
}
_buf.advancedBy(_len++).initialize(x)
}

public func extend<S: SequenceType where S.Generator.Element == T>
(newElements: S) {
var g = newElements.generate()
while let x: T = g.next() {
self.append(x)
}
}
}


Related Topics



Leave a reply



Submit