Open liamnichols opened 2 years ago
CC: @jckarter @aschwaighofer @drexin
Hi,
I was experimenting with the approach that uses manual conformances with single StringCodingKey
struct as a replacement to Swift generated conformances of Codable
|Decodable
|Encodable
protocols on the iOS project that i work on, hoping to see the app size reduction similar in magnitude to what's been reported in this thread (Decodable (Auto)
- StringCodingKey
) and on CreateAPI thread (see), but unfortunately the difference that i observed and consequently the app size reduction was much much smaller.
I spent some time trying to figure out why, supposing that maybe there was something i was doing wrong. What i actually found out is that when binaries are properly stripped the difference is indeed much much smaller.
To demonstrate this i put together an iOS project from Xcode template, added to it 300 structs that conform to Codable
, using manual conformances with single StringCodingKey
struct and using Swift generated conformances, toggleable by compilation condition, and measured app's binary size when the project is compiled with default Release configuration VS when the project is compiles with default Release configuration + build settings for stripping.
https://github.com/vakhidbetrakhmadov/Codable/tree/main
These are the results that i got:
Test case | App's Binary Size |
---|---|
Swift generated conformances AND default Release configuration ( make build__with_default_codable ) |
3,414,816 bytes (3.4 MB on disk) |
Manual conformances with single StringCodingKey struct AND default Release configuration( make build__with_string_coding_key_codable ) |
855,056 bytes (856 KB on disk) |
Swift generated conformances AND default Release configuration + build settings for stripping ( make build__with_default_codable__and_size_optimization_build_settings ) |
613,472 bytes (614 KB on disk) |
Manual conformances with single StringCodingKey struct AND default Release configuration + build settings for stripping( make build__with_string_coding_key_codable__and_size_optimization_build_settings ) |
260,688 bytes (262 KB on disk) |
Very interesting, thanks for sharing this @vakhidbetrakhmadov! So the trick is the non-default build settings that your project uses:
SWIFT_OPTIMIZATION_LEVEL = -Osize DEPLOYMENT_POSTPROCESSING = YES STRIP_INSTALLED_PRODUCT = YES STRIPFLAGS = -rSTx OTHER_SWIFT_FLAGS = -Xfrontend -internalize-at-link OTHER_LDFLAGS = -Xlinker -exported_symbols_list -Xlinker /dev/null
Do you happen to know much about which settings are the most beneficial here? Or is it a combination of everything?
I'll need to dig into the output binaries a bit further to better understand what exactly is being stripped. It's great that there is a workaround, but I'm not quite sure of the tradeoffs from these settings so would love to learn more 🙇
Describe the bug
When making a type conform to the
Codable
protocol (and leveraging the compiler synthesised code gen), it brings with it a significant (and I guess unexpected, at least to me) increase to the size of the binary.In the most simplified example:
That's over 1.7x increase in size that I would never have anticipated from just conforming to a protocol.
Steps To Reproduce
Steps to reproduce the behavior:
Expected behavior
The
Decodable (Auto)
benchmark should result in a size that is a lot closer toNo Conformance
.Environment (please fill out the following information)
Additional context
I'm coming from CreateAPI/CreateAPI#57, which is a tool for generating entities from OpenAPI schema documents (such as the App Store Connect API). In some cases, the generated code ends up including a lot of
Codable
conforming types and it became apparent that something was bloating the binary size when compiling this code.After some digging, I tracked a significant part of the issue back to the presence of
CodingKey
enums in every type and I was surprised to find that when we replace alln
enums with a singleStringCodingKey
type, our generated code ends up taking significantly less space in the binary.In CreateAPI, we can make use of this optimisation by using
StringCodingKey
, but what stuck out to me was that the compiler generatedCodable
implementations are also bloated due to the creation of multipleCodingKey
enums. While I get the value of usingCodingKey
enums for type safety in manually written implementations, it feels to me that the compiler generated code could probably avoid this?I'm happy to provide more details if required, and I'm also keen to learn more about why enums have this kind of impact if anybody is able to provide more info. Thanks!