For reference, in C/C++, the equivalent (sizeof
operator) is compile time, and can be used with template programming (Generics).
I was looking through Swift Algorithms Club for implementations of common data structures and came across their implementation of a Bit Set:
public struct BitSet {
private(set) public var size: Int
private let N = 64
public typealias Word = UInt64
fileprivate(set) public var words: [Word]
public init(size: Int) {
precondition(size > 0)
self.size = size
// Round up the count to the next multiple of 64.
let n = (size + (N-1)) / N
words = [Word](repeating: 0, count: n)
}
<clipped>
In a language where sizeof exists as a compile time constant operator, I would have set N
to sizeof(Word) * 8
or rather MemoryLayout<UInt64>.size * 8
rather than the "magic number" 64. I admit, its not very magic here, but the point stands if only just for making it semantically clear whats going on.
Further, I noted the family of functions, for which the same questions applies (ref).
static func size(ofValue value: T) -> Int
static func stride(ofValue value: T) -> Int
static func alignment(ofValue value: T) -> Int
Edit: Adding some disassembly from generic/not generic version of functions.
Non-generic swift:
func getSizeOfInt() -> Int {
return MemoryLayout<UInt64>.size
}
Produces this disassembly:
(lldb) disassemble --frame
MemoryLayout`getSizeOfInt() -> Int:
0x1000013c0 <+0>: pushq %rbp
0x1000013c1 <+1>: movq %rsp, %rbp
0x1000013c4 <+4>: movl $0x8, %eax
-> 0x1000013c9 <+9>: popq %rbp
0x1000013ca <+10>: retq
Based on the 0x8 constant, this looks like a compile time constant based on @Charles Srstka's answer.
How about the generic swift implementation?
func getSizeOf<T>(_ t:T) -> Int {
return MemoryLayout<T>.size
}
Produced this disassembly:
(lldb) disassemble --frame
MemoryLayout`getSizeOf<A> (A) -> Int:
0x100001390 <+0>: pushq %rbp
0x100001391 <+1>: movq %rsp, %rbp
0x100001394 <+4>: subq $0x20, %rsp
0x100001398 <+8>: movq %rsi, -0x8(%rbp)
0x10000139c <+12>: movq %rdi, -0x10(%rbp)
-> 0x1000013a0 <+16>: movq -0x8(%rsi), %rax
0x1000013a4 <+20>: movq 0x88(%rax), %rcx
0x1000013ab <+27>: movq %rcx, -0x18(%rbp)
0x1000013af <+31>: callq *0x20(%rax)
0x1000013b2 <+34>: movq -0x18(%rbp), %rax
0x1000013b6 <+38>: addq $0x20, %rsp
0x1000013ba <+42>: popq %rbp
0x1000013bb <+43>: retq
0x1000013bc <+44>: nopl (%rax)
The above doesn't look compile time... ? I am not familiar yet with assembler on mac/lldb.
This code:
generates this assembly:
From this, it appears that
MemoryLayout<Int64>.size
is indeed compile-time.Checking the assembly for
stride
andalignment
is left to the reader, but they give similar results (actually identical, in the case ofInt64
).EDIT:
If we're talking about generic functions, obviously more work has to be done since the function will not know the type of which it's getting the size at compile time, and thus can't just put in a constant. But, if you define your function to take a type instead of an instance of the type, it does a little less work than in your example:
called like:
getSizeOf(UInt64.self)
generates this assembly: