As @Ben_Cohen put it in a WWDC20 talk,
Swift's use of value types actually makes tagged pointers less important, because values no longer need to be exactly pointer sized.
For example, a Swift UUID type can be two words and held inline instead of allocating a separate object because it doesn't fit inside a pointer.
Int and String are not class types, so they aren’t confined to using pointers. This allows Int values and small Strings to avoid storing their contents on the heap without using a tagged pointer. (For more information on how String works, here’s a blog post.)
Swift does use tagged pointers for compatibility with Objective-C (i.e. for NSNumber, NSDate, UIColor and NSIndexSet), but it doesn’t define its own tagged pointers AFAIK.
Part of the problem is that you’re compiling your code unoptimized. You should see a small improvement if you compile it with optimizations.
However, even with optimizations, there’s still some cruft in order to make the class compatible with the Objective-C class system. You can remove this cruft by using a struct instead of a class.
private struct Foo {
var i = 5
init() { i *= 2 }
mutating func foo() { i *= 23 }
}
This allows the compiler to optimize your code further, since the complexity of using a class is removed. You can also use the overflow multiplication assignment operator (&*=) to avoid overflow checking.
If you haven’t already, I’d recommend watching the Protocol-Oriented Programming talk from WWDC15. It explains why you should prefer struct types over class types and how to replace classes in your program with structs and protocols.
Here are a few resources on how Swift works internally and how to optimize Swift:
https://developer.apple.com/videos/play/wwdc2015/409/